/* ==========================================================================
   Sales Track — design system
   Single stylesheet for the whole CRM. Tokens first, then layout, then
   components. Everything is theme-aware via CSS custom properties.
   ========================================================================== */

/* --- Tokens --------------------------------------------------------------- */
:root {
  /* Brand */
  --brand-50:  #e8f3fb;
  --brand-100: #cce4f4;
  --brand-200: #99c9e9;
  --brand-300: #66aede;
  --brand-400: #3393d3;
  --brand-500: #0d84cc;
  --brand-600: #0072BC;
  --brand-700: #005b96;
  --brand-800: #004370;

  /* Neutrals */
  --slate-50:  #f8fafc;
  --slate-100: #f1f5f9;
  --slate-200: #e2e8f0;
  --slate-300: #cbd5e1;
  --slate-400: #94a3b8;
  --slate-500: #64748b;
  --slate-600: #475569;
  --slate-700: #334155;
  --slate-800: #1e293b;
  --slate-900: #0f172a;
  --slate-950: #020617;

  /* Semantic */
  --success: #16a34a; --success-bg: #dcfce7; --success-fg: #166534;
  --warning: #d97706; --warning-bg: #fef3c7; --warning-fg: #92400e;
  --danger:  #D71920; --danger-bg:  #fee2e2; --danger-fg:  #991b1b;
  --info:    #0284c7; --info-bg:    #e0f2fe; --info-fg:    #075985;
  --purple:  #7c3aed; --purple-bg:  #ede9fe; --purple-fg:  #5b21b6;
  --pink:    #db2777; --pink-bg:    #fce7f3; --pink-fg:    #9d174d;
  --teal:    #0d9488; --teal-bg:    #ccfbf1; --teal-fg:    #115e59;

  /* Surfaces */
  --bg:            var(--slate-100);
  --surface:       #ffffff;
  --surface-2:     var(--slate-50);
  --surface-hover: var(--slate-100);
  --border:        var(--slate-200);
  --border-strong: var(--slate-300);

  /* Text */
  --text:        var(--slate-800);
  --text-muted:  var(--slate-500);
  --text-soft:   var(--slate-400);
  --text-invert: #ffffff;

  /* Sidebar */
  --sb-bg:     var(--slate-900);
  --sb-fg:     #cbd5e1;
  --sb-fg-dim: #64748b;
  --sb-hover:  rgba(255,255,255,.06);
  --sb-active: var(--brand-600);
  --sb-border: rgba(255,255,255,.07);

  /* Shape + motion */
  --r-sm: 6px; --r: 10px; --r-lg: 14px; --r-xl: 20px; --r-full: 999px;
  --sh-sm: 0 1px 2px rgba(15,23,42,.06);
  --sh:    0 1px 3px rgba(15,23,42,.08), 0 1px 2px rgba(15,23,42,.04);
  --sh-md: 0 4px 12px rgba(15,23,42,.08);
  --sh-lg: 0 12px 32px rgba(15,23,42,.12);
  --t: 160ms cubic-bezier(.4,0,.2,1);

  /* Metrics */
  --sb-w: 264px;
  --sb-w-collapsed: 76px;
  --top-h: 64px;

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

[data-theme="dark"] {
  --bg:            var(--slate-950);
  --surface:       #0f1729;
  --surface-2:     #131c30;
  --surface-hover: #1a2438;
  --border:        #1e293b;
  --border-strong: #334155;
  --text:          #e2e8f0;
  --text-muted:    #94a3b8;
  --text-soft:     #64748b;
  --sb-bg:         #080f1f;
  --success-bg: rgba(22,163,74,.16);  --success-fg: #4ade80;
  --warning-bg: rgba(217,119,6,.16);  --warning-fg: #fbbf24;
  --danger-bg:  rgba(220,38,38,.16);  --danger-fg:  #f87171;
  /*
   * The solid brand reds are re-stepped for a dark surface.
   *
   * The -bg and -fg pairs were flipped for dark mode but the solid tokens
   * were not, so #D71920 - a red chosen to read on white - was still being
   * painted on #0f172a. The delete icon in every table was a dark red on a
   * dark background: technically the right hue, and not legible as a
   * warning, which is the entire job of that colour.
   */
  --danger:  #f87171;
  --success: #4ade80;
  --warning: #fbbf24;
  --info-bg:    rgba(2,132,199,.16);  --info-fg:    #38bdf8;
  --purple-bg:  rgba(124,58,237,.16); --purple-fg:  #a78bfa;
  --pink-bg:    rgba(219,39,119,.16); --pink-fg:    #f472b6;
  --teal-bg:    rgba(13,148,136,.16); --teal-fg:    #2dd4bf;
  --sh-sm: 0 1px 2px rgba(0,0,0,.4);
  --sh:    0 1px 3px rgba(0,0,0,.5);
  --sh-md: 0 4px 12px rgba(0,0,0,.5);
  --sh-lg: 0 12px 32px rgba(0,0,0,.6);
}

/* --- Reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--brand-600); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3, h4 { line-height: 1.25; font-weight: 650; letter-spacing: -.01em; }
:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 2px; border-radius: 4px; }

/* Screen-reader only, but focusable (skip links) */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link:focus {
  position: fixed; top: 12px; left: 12px; z-index: 200; width: auto; height: auto;
  clip: auto; margin: 0; padding: 10px 16px; background: var(--brand-600);
  color: #fff; border-radius: var(--r); box-shadow: var(--sh-lg);
}

/* --- App shell ------------------------------------------------------------ */
.app { min-height: 100vh; }

.sidebar {
  position: fixed; inset: 0 auto 0 0; width: var(--sb-w); z-index: 60;
  background: var(--sb-bg); color: var(--sb-fg);
  display: flex; flex-direction: column;
  border-right: 1px solid var(--sb-border);
  transition: width var(--t), transform var(--t);
}
.sidebar__brand {
  display: flex; align-items: center; gap: 12px;
  height: var(--top-h); padding: 0 20px; flex-shrink: 0;
  border-bottom: 1px solid var(--sb-border);
}
.sidebar__logo {
  width: 34px; height: 34px; border-radius: 9px; flex-shrink: 0;
  background: linear-gradient(135deg, var(--brand-500), var(--brand-700));
  display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 15px;
  box-shadow: 0 2px 8px rgba(0,114,188,.42);
}
.sidebar__title { font-size: 15px; font-weight: 700; color: #fff; letter-spacing: -.01em; white-space: nowrap; }
.sidebar__subtitle { font-size: 11px; color: var(--sb-fg-dim); white-space: nowrap; }

.sidebar__nav { flex: 1; overflow-y: auto; padding: 14px 12px 20px; }
.sidebar__nav::-webkit-scrollbar { width: 6px; }
.sidebar__nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.12); border-radius: 3px; }

.nav-group { margin-bottom: 20px; }
.nav-group__label {
  font-size: 10.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--sb-fg-dim); padding: 0 12px; margin-bottom: 7px; white-space: nowrap;
}
.nav-item {
  display: flex; align-items: center; gap: 12px; position: relative;
  padding: 9px 12px; border-radius: var(--r); color: var(--sb-fg);
  font-weight: 500; font-size: 13.5px; margin-bottom: 2px;
  transition: background var(--t), color var(--t);
  white-space: nowrap;
}
.nav-item:hover { background: var(--sb-hover); color: #fff; text-decoration: none; }
.nav-item.is-active { background: var(--sb-active); color: #fff; box-shadow: 0 2px 10px rgba(0,114,188,.45); }
.nav-item.is-active .nav-item__icon { opacity: 1; }
.nav-item__icon { width: 19px; height: 19px; flex-shrink: 0; opacity: .82; }
.nav-item__label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.nav-item__badge {
  background: rgba(255,255,255,.16); color: #fff; font-size: 11px; font-weight: 700;
  padding: 1px 8px; border-radius: var(--r-full); min-width: 22px; text-align: center;
}
.nav-item.is-active .nav-item__badge { background: rgba(255,255,255,.25); }

.sidebar__footer { padding: 12px; border-top: 1px solid var(--sb-border); flex-shrink: 0; }

/* Collapsed (desktop rail) */
.app.is-collapsed .sidebar { width: var(--sb-w-collapsed); }
.app.is-collapsed .sidebar__title,
.app.is-collapsed .sidebar__subtitle,
.app.is-collapsed .nav-item__label,
.app.is-collapsed .nav-item__badge,
.app.is-collapsed .nav-group__label { display: none; }
.app.is-collapsed .sidebar__brand { justify-content: center; padding: 0; }
.app.is-collapsed .nav-item { justify-content: center; padding: 11px; }
.app.is-collapsed .nav-group { margin-bottom: 12px; }
.app.is-collapsed .main { margin-left: var(--sb-w-collapsed); }
/* Tooltip for rail mode */
.app.is-collapsed .nav-item::after {
  content: attr(data-tip); position: absolute; left: calc(100% + 10px); top: 50%;
  transform: translateY(-50%); background: var(--slate-900); color: #fff;
  padding: 6px 10px; border-radius: var(--r-sm); font-size: 12px; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity var(--t); box-shadow: var(--sh-lg);
  border: 1px solid var(--sb-border); z-index: 70;
}
.app.is-collapsed .nav-item:hover::after { opacity: 1; }

.main {
  margin-left: var(--sb-w); min-height: 100vh;
  display: flex; flex-direction: column; transition: margin var(--t);
}

/* --- Topbar --------------------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: 50; height: var(--top-h); flex-shrink: 0;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 14px; padding: 0 20px;
}
.icon-btn {
  display: grid; place-items: center; width: 38px; height: 38px; flex-shrink: 0;
  border: 1px solid transparent; background: transparent; border-radius: var(--r);
  color: var(--text-muted); cursor: pointer; transition: all var(--t);
}
.icon-btn:hover { background: var(--surface-hover); color: var(--text); }
.icon-btn svg { width: 19px; height: 19px; }

.search { position: relative; flex: 1; max-width: 460px; }
.search input {
  width: 100%; height: 38px; padding: 0 14px 0 38px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r); color: var(--text); transition: all var(--t);
}
.search input::placeholder { color: var(--text-soft); }
.search input:focus {
  outline: none; border-color: var(--brand-500); background: var(--surface);
  box-shadow: 0 0 0 3px var(--brand-100);
}
[data-theme="dark"] .search input:focus { box-shadow: 0 0 0 3px rgba(0,114,188,.28); }
.search__icon {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-soft); pointer-events: none;
}
.search__kbd {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-size: 11px; font-family: var(--mono); color: var(--text-soft);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; background: var(--surface);
}
.search__results {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 80;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--sh-lg); max-height: 380px; overflow-y: auto; padding: 6px; display: none;
}
.search__results.is-open { display: block; }
.search__item {
  display: flex; align-items: center; gap: 10px; padding: 9px 10px;
  border-radius: var(--r-sm); cursor: pointer; color: inherit;
}
.search__item:hover, .search__item.is-active { background: var(--surface-hover); text-decoration: none; }
.search__empty { padding: 18px; text-align: center; color: var(--text-muted); font-size: 13px; }

.topbar__spacer { flex: 1; }

/* User menu */
.usermenu { position: relative; flex-shrink: 0; }
.usermenu__trigger {
  display: flex; align-items: center; gap: 10px; padding: 5px 10px 5px 5px;
  background: transparent; border: 1px solid var(--border); border-radius: var(--r-full);
  cursor: pointer; transition: all var(--t);
}
.usermenu__trigger:hover { background: var(--surface-hover); }
.avatar {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  background: linear-gradient(135deg, var(--brand-500), var(--brand-700));
  color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 12px;
}
.avatar--lg { width: 44px; height: 44px; font-size: 16px; }
.usermenu__name { font-size: 13px; font-weight: 600; line-height: 1.2; }
.usermenu__role { font-size: 11px; color: var(--text-muted); line-height: 1.2; }
.dropdown {
  position: absolute; right: 0; top: calc(100% + 8px); min-width: 232px; z-index: 90;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--sh-lg); padding: 6px; display: none;
}
.dropdown.is-open { display: block; }
.dropdown__header { padding: 10px 12px; border-bottom: 1px solid var(--border); margin-bottom: 6px; }
.dropdown__item {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 9px 12px; border-radius: var(--r-sm); color: var(--text); font-size: 13.5px;
  background: none; border: 0; cursor: pointer;
}
.dropdown__item:hover { background: var(--surface-hover); text-decoration: none; }
.dropdown__item svg { width: 16px; height: 16px; color: var(--text-muted); }
.dropdown__item--danger { color: var(--danger); }
.dropdown__item--danger svg { color: var(--danger); }
.dropdown__sep { height: 1px; background: var(--border); margin: 6px 0; }

/* --- Page ----------------------------------------------------------------- */
.content { flex: 1; padding: 24px; max-width: 1560px; width: 100%; margin: 0 auto; }
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 22px;
}
.page-head__title { font-size: 23px; font-weight: 700; letter-spacing: -.02em; }
.page-head__sub { color: var(--text-muted); font-size: 13.5px; margin-top: 3px; }
.page-head__actions { display: flex; gap: 10px; flex-wrap: wrap; }
.breadcrumb { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-muted); margin-bottom: 7px; }
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: var(--brand-600); }

/* --- Buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; padding: 0 16px; border-radius: var(--r);
  font-size: 13.5px; font-weight: 600; white-space: nowrap;
  border: 1px solid transparent; cursor: pointer; transition: all var(--t);
  text-decoration: none;
}
.btn:hover { text-decoration: none; }
.btn:disabled, .btn.is-disabled { opacity: .55; pointer-events: none; }
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.btn--primary { background: var(--brand-600); color: #fff; box-shadow: var(--sh-sm); }
.btn--primary:hover { background: var(--brand-700); box-shadow: var(--sh-md); color: #fff; }
.btn--secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); }
.btn--secondary:hover { background: var(--surface-hover); color: var(--text); }
.btn--ghost { background: transparent; color: var(--text-muted); }
.btn--ghost:hover { background: var(--surface-hover); color: var(--text); }
.btn--danger { background: var(--danger); color: #fff; }
.btn--danger:hover { filter: brightness(.92); color: #fff; }
.btn--success { background: var(--success); color: #fff; }
.btn--success:hover { filter: brightness(.92); color: #fff; }
.btn--warning { background: var(--warning); color: #fff; }
.btn--warning:hover { filter: brightness(.92); color: #fff; }
.btn--sm { height: 32px; padding: 0 12px; font-size: 12.5px; border-radius: var(--r-sm); }
.btn--sm svg { width: 14px; height: 14px; }
.btn--xs { height: 27px; padding: 0 9px; font-size: 12px; border-radius: var(--r-sm); }
.btn--xs svg { width: 13px; height: 13px; }
.btn--icon { width: 38px; padding: 0; }
.btn--icon.btn--sm { width: 32px; }
.btn--icon.btn--xs { width: 27px; }
.btn--block { width: 100%; }

/* --- Cards ---------------------------------------------------------------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--sh-sm);
}
.card__head {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 16px 20px; border-bottom: 1px solid var(--border); flex-wrap: wrap;
}
.card__title { font-size: 15px; font-weight: 650; }
.card__sub { font-size: 12.5px; color: var(--text-muted); margin-top: 2px; }
.card__body { padding: 20px; }
.card__body--flush { padding: 0; }
.card__foot {
  /* A card foot is always a row: pagination beside a rows-per-page picker, or
     a line of buttons. It carried gap/align-items/flex-wrap but nobody ever
     set display:flex, so all three were inert - which is why the "Rows"
     selector sat jammed against the pager instead of at the right-hand end. */
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 14px 20px; border-top: 1px solid var(--border);
  background: var(--surface-2); border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* Opt out for the few feet that hold a block-level form instead of a row. */
.card__foot.is-block { display: block; }
.card__foot.text-right { justify-content: flex-end; }

/* --- Stat tiles ----------------------------------------------------------- */
.stats { display: grid; gap: 14px; margin-bottom: 22px; }
.stats--4 { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.stats--auto { grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); }
.stat {
  position: relative; overflow: hidden; display: block;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 16px 18px; box-shadow: var(--sh-sm);
  transition: all var(--t); color: inherit;
}
a.stat:hover { text-decoration: none; transform: translateY(-2px); box-shadow: var(--sh-md); border-color: var(--border-strong); }
.stat.is-active { border-color: var(--brand-500); box-shadow: 0 0 0 3px var(--brand-100); }
[data-theme="dark"] .stat.is-active { box-shadow: 0 0 0 3px rgba(0,114,188,.25); }
.stat__top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.stat__label { font-size: 12px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .045em; }
.stat__icon { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; flex-shrink: 0; }
.stat__icon svg { width: 17px; height: 17px; }
.stat__value { font-size: 27px; font-weight: 750; letter-spacing: -.025em; line-height: 1.1; }
.stat__meta { font-size: 12px; color: var(--text-muted); margin-top: 5px; display: flex; align-items: center; gap: 5px; }
.stat__bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: var(--brand-500); }

/* Tonal helpers */
.tone-brand   { background: var(--brand-50);   color: var(--brand-700); }
.tone-success { background: var(--success-bg); color: var(--success-fg); }
.tone-warning { background: var(--warning-bg); color: var(--warning-fg); }
.tone-danger  { background: var(--danger-bg);  color: var(--danger-fg); }
.tone-info    { background: var(--info-bg);    color: var(--info-fg); }
.tone-purple  { background: var(--purple-bg);  color: var(--purple-fg); }
.tone-pink    { background: var(--pink-bg);    color: var(--pink-fg); }
.tone-teal    { background: var(--teal-bg);    color: var(--teal-fg); }
.tone-slate   { background: var(--slate-100);  color: var(--slate-600); }
[data-theme="dark"] .tone-brand { background: rgba(0,114,188,.20); color: var(--brand-300); }
[data-theme="dark"] .tone-slate { background: rgba(148,163,184,.14); color: var(--slate-300); }

/* --- Badges --------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  padding: 3px 9px; border-radius: var(--r-full);
  font-size: 11.5px; font-weight: 650; line-height: 1.5;
}
.badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.badge--outline { background: transparent; border: 1px solid var(--border-strong); color: var(--text-muted); }

/* --- Tables --------------------------------------------------------------- */
.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.table th {
  text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--text-muted);
  padding: 11px 16px; background: var(--surface-2);
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
.table th a { color: inherit; display: inline-flex; align-items: center; gap: 4px; }

/* Let the alignment utilities actually reach a table heading.
 *
 * `.table th` sets text-align:left at specificity 0-1-1, which quietly beat the
 * plain `.text-right` utility at 0-1-0. Every numeric column in the app was
 * therefore captioned on the left while its figures sat on the right - 29
 * headings across ten pages, including Value on Leads. Restated here at 0-2-1
 * so the utility wins where it is asked for, and nowhere else. */
.table th.text-right  { text-align: right; }
.table th.text-center { text-align: center; }
.table td { padding: 13px 16px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.table tbody tr { transition: background var(--t); }
.table tbody tr:hover { background: var(--surface-hover); }
.table tbody tr:last-child td { border-bottom: 0; }
/* The full .table__actions rule lives with the other action styles further
   down; this seat is left empty deliberately rather than restating a gap that
   the later rule immediately overrides. Two live definitions of one selector is
   how the inert .card__foot flex properties survived as long as they did. */.table--compact td { padding: 9px 14px; }
.cell-strong { font-weight: 600; color: var(--text); }
.cell-muted { color: var(--text-muted); font-size: 12.5px; }
.cell-mono { font-family: var(--mono); font-size: 12.5px; }
.cell-num { text-align: right; font-variant-numeric: tabular-nums; }

/* --- Forms ---------------------------------------------------------------- */
.form-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* The control row that ends a form.
 *
 * A form's fields and the button that submits them are different things, and
 * the eye has to see that separation before it finds the button. Nothing in
 * the system said so: .form-grid has no bottom margin and .toolbar has no top
 * one, so "Log follow-up" sat flush against the comment box above it and read
 * as part of the field rather than the act of finishing.
 *
 * Every form that ends in buttons uses this, so the spacing is one decision
 * made once instead of a margin guessed at each call site. */
.form-actions {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 10px; margin-top: 18px;
}
.form-actions__spacer { flex: 1 1 auto; }

/* Where the form is long enough that a rule helps the eye find the end. */
.form-actions--divided { padding-top: 16px; border-top: 1px solid var(--border); }

/* Tight variant for a form embedded in a list row, where a full gap would
   stretch the row and a rule would be noise. */
.form-actions--tight { margin-top: 12px; }
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field--full { grid-column: 1 / -1; }
.label { font-size: 12.5px; font-weight: 600; color: var(--text); }
.label .req { color: var(--danger); margin-left: 2px; }
.hint { font-size: 12px; color: var(--text-muted); }
.input, .select, .textarea {
  width: 100%; min-height: 38px; padding: 8px 12px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--r);
  transition: border-color var(--t), box-shadow var(--t);
}
.input::placeholder, .textarea::placeholder { color: var(--text-soft); }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--brand-500); box-shadow: 0 0 0 3px var(--brand-100);
}
[data-theme="dark"] .input:focus,
[data-theme="dark"] .select:focus,
[data-theme="dark"] .textarea:focus { box-shadow: 0 0 0 3px rgba(0,114,188,.28); }
.input:disabled, .select:disabled, .textarea:disabled { background: var(--surface-2); color: var(--text-muted); cursor: not-allowed; }
.textarea { min-height: 92px; resize: vertical; line-height: 1.6; }
.select {
  appearance: none; padding-right: 36px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 15px;
}
.input--invalid, .select--invalid { border-color: var(--danger); }
.field-error { font-size: 12px; color: var(--danger); }
/* A checkbox with a text label: the whole row is the target, so it gets the
   vertical padding that makes it comfortable to hit. Sizing and accent colour
   come from the single input[type=checkbox] rule further down - restating them
   here is how the app ended up with a 16px box in a form and a 17px box in a
   table, for the same control. */
.checkbox, .radio {
  display: flex; align-items: center; gap: 9px;
  cursor: pointer; font-size: 13.5px;
  padding: 5px 0;
}
.switch { position: relative; display: inline-block; width: 40px; height: 22px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch__slider {
  position: absolute; inset: 0; background: var(--slate-300); border-radius: var(--r-full);
  cursor: pointer; transition: background var(--t);
}
.switch__slider::before {
  content: ""; position: absolute; width: 17px; height: 17px; left: 2.5px; top: 2.5px;
  background: #fff; border-radius: 50%; transition: transform var(--t); box-shadow: var(--sh-sm);
}
.switch input:checked + .switch__slider { background: var(--brand-600); }
.switch input:checked + .switch__slider::before { transform: translateX(18px); }

/* --- Filter bar ----------------------------------------------------------- */
/* The filter bar's layout is defined once, further down. Declaring it twice
   is how .filters .field ended up with four competing rules and the widest
   one silently winning. */
.chips { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 14px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px;
  background: var(--brand-50); color: var(--brand-700); border: 1px solid var(--brand-200);
  border-radius: var(--r-full); font-size: 12.5px; font-weight: 550;
}
[data-theme="dark"] .chip { background: rgba(0,114,188,.18); border-color: rgba(0,114,188,.34); color: var(--brand-300); }
.chip a { display: grid; place-items: center; width: 15px; height: 15px; border-radius: 50%; color: inherit; opacity: .65; }
.chip a:hover { opacity: 1; background: rgba(0,0,0,.08); text-decoration: none; }

/* --- Alerts / toasts ------------------------------------------------------ */
.alert {
  display: flex; gap: 12px; align-items: flex-start; padding: 13px 16px;
  border-radius: var(--r); border: 1px solid transparent; margin-bottom: 18px; font-size: 13.5px;
}
.alert svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }
.alert--success { background: var(--success-bg); color: var(--success-fg); border-color: var(--success); }
.alert--danger  { background: var(--danger-bg);  color: var(--danger-fg);  border-color: var(--danger); }
.alert--warning { background: var(--warning-bg); color: var(--warning-fg); border-color: var(--warning); }
.alert--info    { background: var(--info-bg);    color: var(--info-fg);    border-color: var(--info); }
/* The dismiss control is a real target, not just the glyph inside it. */
.alert__close {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  opacity: .6; color: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; margin-top: -3px; margin-bottom: -3px;
  border-radius: var(--r-sm); transition: opacity var(--t), background var(--t);
}
.alert__close:hover { opacity: 1; background: rgba(0,0,0,.06); }
.alert__close:hover { opacity: 1; }

.toasts {
  position: fixed; top: 18px; right: 18px; z-index: 300;
  display: flex; flex-direction: column; gap: 10px; pointer-events: none;
}
.toast {
  display: flex; gap: 11px; align-items: flex-start; min-width: 288px; max-width: 400px;
  padding: 13px 15px; background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--brand-500); border-radius: var(--r);
  box-shadow: var(--sh-lg); pointer-events: auto; font-size: 13.5px;
  animation: toast-in .26s cubic-bezier(.2,.9,.3,1.2);
}
.toast.is-out { animation: toast-out .2s ease forwards; }
.toast--success { border-left-color: var(--success); }
.toast--danger  { border-left-color: var(--danger); }
.toast--warning { border-left-color: var(--warning); }
.toast svg { width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; }
@keyframes toast-in { from { opacity: 0; transform: translateX(24px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateX(24px); } }

/* --- Modal ---------------------------------------------------------------- */
.modal {
  position: fixed; inset: 0; z-index: 200; display: none;
  align-items: flex-start; justify-content: center; padding: 5vh 16px;
  background: rgba(15,23,42,.55); overflow-y: auto;
}
.modal.is-open { display: flex; animation: fade-in .16s ease; }
.modal__box {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-xl);
  box-shadow: var(--sh-lg); width: 100%; max-width: 620px; margin: auto 0;
  animation: modal-in .22s cubic-bezier(.2,.9,.3,1.1);
}
.modal__box--lg { max-width: 900px; }
.modal__box--sm { max-width: 440px; }
.modal__head {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 18px 22px; border-bottom: 1px solid var(--border);
}
.modal__title { font-size: 16.5px; font-weight: 650; }
.modal__body { padding: 22px; }
.modal__foot {
  display: flex; justify-content: flex-end; gap: 10px; padding: 16px 22px;
  border-top: 1px solid var(--border); background: var(--surface-2);
  border-radius: 0 0 var(--r-xl) var(--r-xl); flex-wrap: wrap;
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-in { from { opacity: 0; transform: translateY(-14px) scale(.985); } to { opacity: 1; transform: none; } }

/* --- Empty / loading ------------------------------------------------------ */
.empty { text-align: center; padding: 52px 20px; }
.empty__icon {
  width: 60px; height: 60px; border-radius: 50%; margin: 0 auto 16px;
  background: var(--surface-2); display: grid; place-items: center; color: var(--text-soft);
}
.empty__icon svg { width: 27px; height: 27px; }
.empty__title { font-size: 15.5px; font-weight: 650; margin-bottom: 5px; }
.empty__text { color: var(--text-muted); font-size: 13.5px; max-width: 420px; margin: 0 auto 18px; }

.spinner {
  width: 17px; height: 17px; border: 2px solid var(--border-strong);
  border-top-color: var(--brand-600); border-radius: 50%;
  animation: spin .65s linear infinite; display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* --- Pagination ----------------------------------------------------------- */
.pagination { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.pagination__info { font-size: 12.5px; color: var(--text-muted); }
.pagination__list { display: flex; gap: 4px; align-items: center; }
.pagination__link {
  min-width: 34px; height: 34px; padding: 0 9px; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--r-sm); font-size: 13px;
  color: var(--text); background: var(--surface); font-weight: 550;
}
.pagination__link:hover { background: var(--surface-hover); text-decoration: none; }
.pagination__link.is-active { background: var(--brand-600); border-color: var(--brand-600); color: #fff; }
.pagination__link.is-disabled { opacity: .42; pointer-events: none; }

/* --- Progress ------------------------------------------------------------- */
.progress { height: 7px; background: var(--surface-2); border-radius: var(--r-full); overflow: hidden; }
.progress__bar { height: 100%; background: var(--brand-600); border-radius: var(--r-full); transition: width .4s ease; }

/* --- Tabs ----------------------------------------------------------------- */
.tabs { display: flex; gap: 3px; border-bottom: 1px solid var(--border); margin-bottom: 20px; overflow-x: auto; }
.tab {
  padding: 10px 15px; font-size: 13.5px; font-weight: 600; color: var(--text-muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; cursor: pointer;
  background: none; border-top: 0; border-left: 0; border-right: 0;
}
.tab:hover { color: var(--text); text-decoration: none; }
.tab.is-active { color: var(--brand-600); border-bottom-color: var(--brand-600); }

/* --- Utilities ------------------------------------------------------------ */
.grid { display: grid; gap: 16px; }
/* min(Npx, 100%) so the track never demands more width than actually exists.
   A flat minmax(300px, 1fr) keeps asking for 300px on a 280px phone and pushes
   the page sideways. */
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.flex { display: flex; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-1 { gap: 5px; } .gap-2 { gap: 9px; } .gap-3 { gap: 14px; }
.mt-1 { margin-top: 6px; } .mt-2 { margin-top: 12px; } .mt-3 { margin-top: 20px; }
.mb-0 { margin-bottom: 0; } .mb-3 { margin-bottom: 20px; }
/* .mb-2 is defined once, further down, at 14px. It was declared here too,
   at 12px, and silently lost - two values for one utility is how a layout
   drifts a couple of pixels depending on which rule a reader found first. */
.text-muted { color: var(--text-muted); }
.text-sm { font-size: 12.5px; }
/* The hidden attribute outranks any class that sets display.
 *
 * `hidden` works through a user-agent rule (`[hidden] { display: none }`), and
 * a user-agent rule loses to any author rule at all. So every component that
 * declares its own display and is toggled with `el.hidden = true` stayed on
 * screen: the bulk-action bar sat at the foot of Leads offering Reassign and
 * Delete with nothing selected, and the new-customer dialog opened showing an
 * empty red error box. `!important` is the point of the rule rather than a
 * shortcut around one - hidden must beat whatever the component declares. */
[hidden] { display: none !important; }

.text-right { text-align: right; }
.text-center { text-align: center; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nowrap { white-space: nowrap; }
.hidden { display: none !important; }
.w-full { width: 100%; }

/* --- Responsive ----------------------------------------------------------- */
.sidebar__scrim { display: none; }
@media (max-width: 1024px) {
  .sidebar { transform: translateX(-100%); box-shadow: var(--sh-lg); }
  .app.is-open .sidebar { transform: none; }
  .main { margin-left: 0 !important; }
  .app.is-open .sidebar__scrim {
    display: block; position: fixed; inset: 0; z-index: 55; background: rgba(15,23,42,.5);
  }
  /* Rail mode is desktop-only */
  .app.is-collapsed .sidebar { width: var(--sb-w); }
  .app.is-collapsed .sidebar__title,
  .app.is-collapsed .sidebar__subtitle,
  .app.is-collapsed .nav-item__label,
  .app.is-collapsed .nav-item__badge,
  .app.is-collapsed .nav-group__label { display: block; }
  .app.is-collapsed .nav-item { justify-content: flex-start; padding: 9px 12px; }
  .app.is-collapsed .nav-item::after { content: none; }
  .app.is-collapsed .sidebar__brand { justify-content: flex-start; padding: 0 20px; }
}
@media (max-width: 768px) {
  .content { padding: 16px; }
  .search__kbd { display: none; }
  .usermenu__name, .usermenu__role { display: none; }
  .usermenu__trigger { border: 0; padding: 0; }
  .page-head__title { font-size: 20px; }
  .stats--auto { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}
@media (max-width: 560px) {
  .stats--4, .stats--auto { grid-template-columns: repeat(2, 1fr); }
  .stat { padding: 13px; }
  .stat__value { font-size: 21px; }
}

/* --- Print ---------------------------------------------------------------- */
@media print {
  .sidebar, .topbar, .page-head__actions, .filters, .no-print, .toasts { display: none !important; }
  .main { margin-left: 0 !important; }
  .content { padding: 0; max-width: none; }
  .card { border: 1px solid #ddd; box-shadow: none; break-inside: avoid; }
  body { background: #fff; font-size: 12px; }
  a { color: inherit; text-decoration: none; }
}

/* --- Sidebar brand -------------------------------------------------------- */
/* The full wordmark needs the expanded width; the rail shows just the mark.
   Both are inline SVG so the "SOLUTION" half can be white on the dark panel. */
.sidebar__mark { display: none; flex-shrink: 0; }
.sidebar__wordmark { display: block; flex-shrink: 0; }
.app.is-collapsed .sidebar__wordmark { display: none; }
.app.is-collapsed .sidebar__mark { display: block; }
@media (max-width: 1024px) {
  /* Rail mode is desktop-only, so the drawer always shows the wordmark. */
  .app.is-collapsed .sidebar__wordmark { display: block; }
  .app.is-collapsed .sidebar__mark { display: none; }
}

/* --- User menu alignment -------------------------------------------------- */
/* Name and role stack as a proper flex column. The name is clamped rather than
   allowed to wrap: "System Administrator" used to break onto two lines and
   knock the avatar and caret out of vertical alignment. */
.usermenu__trigger { line-height: 1.25; }
.usermenu__meta {
  display: flex; flex-direction: column; align-items: flex-start;
  min-width: 0; gap: 1px;
}
.usermenu__name {
  font-size: 13px; font-weight: 600; max-width: 150px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.usermenu__role { font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.usermenu__caret { width: 16px; height: 16px; color: var(--text-muted); flex-shrink: 0; }

.usermenu__card { display: flex; align-items: center; gap: 11px; min-width: 0; }
.usermenu__cardtext { min-width: 0; display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }

/* Sidebar brand sits flush with the nav items below it. */
.sidebar__brand { gap: 10px; padding-right: 12px; }
.sidebar__wordmark img, .sidebar__mark img { max-width: 100%; }
.sidebar__wordmark svg, .sidebar__mark svg { display: block; }

/* --- Ticket items grid ---------------------------------------------------- */
/* The create modal prices the ticket inline, so its grid needs a wider box
   than a normal form modal and inputs that stay compact inside the cells. */
#itemsTable td { padding: 8px 10px; vertical-align: middle; }
#itemsTable .input, #itemsTable .select { min-height: 34px; padding: 6px 10px; }
#itemsTable .select { padding-right: 30px; background-position: right 8px center; }
#itemsTable tfoot td { border-top: 1px solid var(--border-strong); border-bottom: 0; }
#ticketModal .modal__box { max-width: 1000px; }

/* --- Icon sizing safety net ----------------------------------------------- */
/* icon() now emits width/height attributes, so an icon can never stretch to
   fill its container again. These rules just tune the contexts that had no
   sizing rule of their own. */
.breadcrumb svg { width: 14px; height: 14px; flex-shrink: 0; }
.pagination__link svg { width: 15px; height: 15px; }
.chip svg { width: 13px; height: 13px; flex-shrink: 0; }
.chip a svg { width: 12px; height: 12px; }
.card__head svg, .card__title svg { flex-shrink: 0; }
.table svg { flex-shrink: 0; }

/* ===========================================================================
   Charts
   ===========================================================================
   Marks carry a literal light-palette `fill` presentation attribute written by
   Chart::. CSS beats a presentation attribute, so the [data-s] rules below
   re-step the palette for the dark surface on screen - while the SERIALISED
   svg keeps only its attributes, which is what makes an exported PNG land on
   the light palette every time, without any JavaScript deciding colours.
   =========================================================================== */

.chart {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 18px 18px 14px; margin: 0;
  box-shadow: var(--sh-sm); min-width: 0;
}
.chart__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; margin-bottom: 14px;
}
.chart__heading { min-width: 0; }
.chart__title { font-size: 14.5px; font-weight: 650; letter-spacing: -.01em; }
.chart__sub { font-size: 12px; color: var(--text-muted); margin-top: 3px; line-height: 1.5; }

.chart__tools { display: flex; gap: 4px; flex-shrink: 0; }
.chart__tool {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 9px; font-size: 11.5px; font-weight: 600;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text-muted);
  cursor: pointer; transition: all var(--t); white-space: nowrap;
}
.chart__tool:hover { background: var(--surface-hover); color: var(--text); border-color: var(--border-strong); }
.chart__tool svg { width: 13px; height: 13px; }
.chart__tool.is-on { background: var(--brand-600); border-color: var(--brand-600); color: #fff; }

/* The plot scrolls inside its own box; the page never scrolls sideways. */
.chart__canvas { overflow-x: auto; overflow-y: hidden; }
.chart__canvas svg { display: block; width: 100%; height: auto; }
.chart--wide .chart__canvas svg { min-width: 640px; }

.chart__legend {
  display: flex; flex-wrap: wrap; gap: 6px 16px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border);
}
.chart__key { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-muted); }
.chart__swatch { width: 11px; height: 11px; border-radius: 3px; flex-shrink: 0; }

.chart__table { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 4px; max-height: 420px; overflow: auto; }
.chart__table .table { font-size: 12.5px; }
.chart__table .table th, .chart__table .table td { padding: 7px 10px; }
.chart--empty .empty { padding: 30px 16px; }

/* Hover affordance. The ring on a marker is part of its hit target. */
.ct-hit { cursor: default; transition: opacity .12s ease; }
.chart__canvas:hover .ct-hit { opacity: .55; }
.chart__canvas .ct-hit:hover { opacity: 1; }

.ct-tip {
  position: fixed; z-index: 300; pointer-events: none; opacity: 0;
  transform: translate(-50%, -100%) translateY(-10px);
  background: var(--slate-900); color: #fff; border-radius: var(--r-sm);
  padding: 7px 11px; font-size: 12px; line-height: 1.45; max-width: 280px;
  box-shadow: var(--sh-lg); transition: opacity .1s ease;
}
.ct-tip.is-on { opacity: 1; }
.ct-tip b { display: block; font-weight: 650; }
.ct-tip span { color: #cbd5e1; }

/* --- Dark-mode palette steps ---------------------------------------------- */
[data-theme="dark"] .chart svg [data-s="1"] { fill: #3393d3; stroke: #3393d3; }
[data-theme="dark"] .chart svg [data-s="2"] { fill: #d95926; stroke: #d95926; }
[data-theme="dark"] .chart svg [data-s="3"] { fill: #199e70; stroke: #199e70; }
[data-theme="dark"] .chart svg [data-s="4"] { fill: #c98500; stroke: #c98500; }
[data-theme="dark"] .chart svg [data-s="5"] { fill: #d55181; stroke: #d55181; }
[data-theme="dark"] .chart svg [data-s="6"] { fill: #008300; stroke: #008300; }
[data-theme="dark"] .chart svg [data-s="7"] { fill: #9085e9; stroke: #9085e9; }
[data-theme="dark"] .chart svg [data-s="8"] { fill: #e66767; stroke: #e66767; }
/* A line/area path is stroked, not filled; undo the blanket fill above. */
[data-theme="dark"] .chart svg path[data-s][fill="none"] { fill: none; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="1"] { background: #3393d3; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="2"] { background: #d95926; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="3"] { background: #199e70; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="4"] { background: #c98500; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="5"] { background: #d55181; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="6"] { background: #008300; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="7"] { background: #9085e9; }
[data-theme="dark"] .chart__legend .chart__swatch[data-s="8"] { background: #e66767; }

/* Neutral tokens for the dark surface. */
[data-theme="dark"] .chart svg [data-ct="ink"]     { fill: #e2e8f0; }
[data-theme="dark"] .chart svg [data-ct="ink2"]    { fill: #cbd5e1; }
[data-theme="dark"] .chart svg [data-ct="muted"]   { fill: #94a3b8; }
[data-theme="dark"] .chart svg [data-ct-stroke="grid"] { stroke: #1e293b; }
[data-theme="dark"] .chart svg [data-ct="cell-empty"]  { fill: #0f1729; stroke: #1e293b; }
[data-theme="dark"] .chart svg [data-ring="1"]     { stroke: #0f1729; }

/* ===========================================================================
   Meters and KPI extras
   =========================================================================== */
.meter { padding: 12px 0; }
.meter__top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 7px; }
.meter__label { font-size: 12.5px; font-weight: 600; color: var(--text); }
.meter__val { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.meter__of { font-weight: 500; color: var(--text-muted); }
.meter__track { height: 8px; border-radius: var(--r-full); overflow: hidden; }
.meter__fill { height: 100%; border-radius: var(--r-full); transition: width .3s ease; }
.meter__foot { font-size: 11.5px; color: var(--text-muted); margin-top: 6px; }

.sparkline { display: block; width: 100%; height: 32px; overflow: visible; }

.stat--link { text-decoration: none; color: inherit; display: block; }
.stat--link:hover { border-color: var(--border-strong); box-shadow: var(--sh-md); }
.stat__delta { font-size: 12px; font-weight: 650; display: inline-flex; align-items: center; gap: 3px; }
.stat__delta--up   { color: var(--success); }
.stat__delta--down { color: var(--danger); }
.stat__delta svg { width: 13px; height: 13px; }

/* ===========================================================================
   Chart grid
   =========================================================================== */
.chart-grid { display: grid; gap: 16px; margin-bottom: 18px; }
/* A 420px minimum let a wide screen fit three columns, and a chart authored at
   ~760 units then rendered at ~490px - a 60% downscale that took 10.5px labels
   to six and a half. The widest chart here is ~845 units, so a column narrower
   than that squeezes it however generously the cap is set: the minimum has to
   clear the charts themselves. On a normal screen that means one chart per
   row, at full size; a wide monitor still gets two. */
/* Two columns, said plainly rather than left to auto-fit.
   auto-fit takes whatever number of tracks the minimum allows, so a wide
   screen quietly became three columns of 490px and squeezed every chart to
   60%. The charts are drawn to fit half a row; the grid should give them
   exactly that, and drop to one column when half a row is too little. */
.chart-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Two columns only where two columns actually fit. A chart is ~580 units, so
   the pair needs about 1180px of content - which this layout has at roughly
   1500px of viewport once the sidebar is taken out. Below that, one column
   at full size beats two that are squeezed. */
@media (max-width: 1500px) { .chart-grid--2 { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 900px) { .chart-grid--2 { grid-template-columns: minmax(0, 1fr); } }

/* ===========================================================================
   Follow-up timeline
   =========================================================================== */
.timeline { position: relative; padding-left: 30px; }
.timeline::before {
  content: ""; position: absolute; left: 9px; top: 6px; bottom: 6px;
  width: 2px; background: var(--border);
}
.tl-item { position: relative; padding-bottom: 20px; }
.tl-item:last-child { padding-bottom: 0; }
.tl-item__dot {
  position: absolute; left: -30px; top: 2px; width: 20px; height: 20px;
  border-radius: 50%; display: grid; place-items: center;
  background: var(--surface); border: 2px solid var(--border-strong);
  font-size: 10px; font-weight: 700; color: var(--text-muted);
}
.tl-item__dot.is-latest { border-color: var(--brand-600); color: var(--brand-600); }
.tl-item__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 5px; }
.tl-item__seq { font-size: 12px; font-weight: 700; color: var(--text); }
.tl-item__meta { font-size: 11.5px; color: var(--text-muted); }
.tl-item__body {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r); padding: 10px 13px; font-size: 13px; line-height: 1.6;
  white-space: pre-wrap; word-break: break-word;
}
.tl-item__body--empty { color: var(--text-soft); font-style: italic; }
.tl-item__actions { display: flex; gap: 6px; margin-top: 6px; }

/* ===========================================================================
   Permission grid
   =========================================================================== */
.permgrid { width: 100%; border-collapse: collapse; font-size: 13px; }
.permgrid th, .permgrid td { padding: 9px 10px; border-bottom: 1px solid var(--border); }
.permgrid thead th {
  background: var(--surface-2); font-size: 11px; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-muted); font-weight: 700;
  position: sticky; top: 0; z-index: 2; text-align: center;
}
.permgrid thead th:first-child { text-align: left; }
.permgrid td { text-align: center; }
.permgrid td:first-child { text-align: left; }
.permgrid tbody tr:hover { background: var(--surface-hover); }
.permgrid__module { font-weight: 600; }
.permgrid__desc { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; font-weight: 400; }
.permgrid__group td {
  background: var(--surface-2); font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted);
  text-align: left;
}
/* One checkbox, one size, everywhere.
 *
 * Only the permissions grid ever styled its boxes, so every other checkbox
 * fell back to the browser default and the app carried three sizes of the same
 * control: 13px selecting a lead, 16px enabling a user, 17px granting a
 * permission. The smallest was on the most-clicked one - the row selector that
 * drives every bulk action - where 13px is a genuinely difficult mouse target.
 */
input[type=checkbox], input[type=radio] {
  width: 17px; height: 17px;
  cursor: pointer;
  accent-color: var(--brand-600);
  flex: 0 0 auto;
  margin: 0;
}

/* A table's select box gets a larger hit area than its ink, so the click does
   not have to be precise to the pixel. The <label> around it is what carries
   the target: 17px of ink inside a 34px square that is entirely clickable. */
.table .col-check { text-align: center; padding-left: 4px; padding-right: 4px; }

.check-hit {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin: -8px 0;
  border-radius: var(--r-sm); cursor: pointer;
  transition: background var(--t);
}
.check-hit:hover { background: var(--surface-2); }
.check-hit:focus-within { background: var(--surface-2); box-shadow: 0 0 0 2px var(--brand-100); }
.permgrid td.is-na { color: var(--text-soft); }

/* ===========================================================================
   Assorted layout helpers
   =========================================================================== */
.kv { display: grid; grid-template-columns: minmax(110px, auto) 1fr; gap: 9px 16px; font-size: 13.5px; }
.kv dt { color: var(--text-muted); font-size: 12.5px; }
.kv dd { font-weight: 550; word-break: break-word; }

/* minmax(0, 1fr) rather than a bare 1fr, in the stacked case too.
 *
 * `1fr` is shorthand for `minmax(auto, 1fr)`, and that `auto` minimum is the
 * track's min-content - so a column holding a wide table refuses to shrink,
 * the .table-wrap inside it never gets narrow enough to scroll, and the whole
 * PAGE scrolls sideways instead. Users did exactly that below 1100px: a card
 * stuck at 951px in an 861px viewport. The explicit 0 minimum lets the track
 * shrink and hands the overflow back to the wrap, which is built for it. */
.split { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; align-items: start; }
@media (max-width: 1100px) { .split { grid-template-columns: minmax(0, 1fr); } }

.split-wide { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 1100px) { .split-wide { grid-template-columns: minmax(0, 1fr); } }

.stack { display: flex; flex-direction: column; gap: 16px; }
.row-actions { display: flex; gap: 6px; justify-content: flex-end; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; }
.text-xs { font-size: 11.5px; }
.gap-2 { gap: 10px; }
.flex-wrap { flex-wrap: wrap; }
.mt-2 { margin-top: 14px; }
.mb-2 { margin-bottom: 14px; }
.w-auto { width: auto; }

.toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
.toolbar__spacer { flex: 1 1 auto; }

.tag-row { display: flex; gap: 6px; flex-wrap: wrap; }

.callout {
  display: flex; gap: 12px; padding: 13px 15px; border-radius: var(--r);
  border: 1px solid var(--border); background: var(--surface-2);
  font-size: 13px; line-height: 1.6;
}
.callout svg { flex-shrink: 0; width: 17px; height: 17px; margin-top: 1px; }
.callout--info { border-color: var(--info); background: var(--info-bg); color: var(--info-fg); }
.callout--warn { border-color: var(--warning); background: var(--warning-bg); color: var(--warning-fg); }
.callout code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  background: rgba(0,0,0,.06); padding: 1px 5px; border-radius: 4px; font-size: 12px;
  word-break: break-all;
}
[data-theme="dark"] .callout code { background: rgba(255,255,255,.08); }

.report-card {
  display: flex; gap: 13px; align-items: flex-start; padding: 16px;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); transition: all var(--t); text-decoration: none; color: inherit;
}
.report-card:hover { border-color: var(--brand-400); box-shadow: var(--sh-md); transform: translateY(-1px); }
.report-card__icon {
  width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;
  background: var(--brand-50); color: var(--brand-700); flex-shrink: 0;
}
[data-theme="dark"] .report-card__icon { background: rgba(0,114,188,.16); color: #38bdf8; }
.report-card__icon svg { width: 19px; height: 19px; }
.report-card__title { font-weight: 650; font-size: 14px; }
.report-card__desc { font-size: 12.5px; color: var(--text-muted); margin-top: 3px; line-height: 1.55; }
.report-card__links { display: flex; gap: 8px; margin-top: 9px; flex-wrap: wrap; }

/* Print: the sidebar, toolbars and chrome are noise on paper. */
@media print {
  .sidebar, .topbar, .chart__tools, .filters, .page-head__actions,
  .pagination, .toolbar, .row-actions, .btn { display: none !important; }
  .main { margin: 0 !important; }
  .content { padding: 0 !important; }
  .chart, .card { break-inside: avoid; box-shadow: none; border-color: #ddd; }
  .chart__canvas svg { min-width: 0 !important; }
}

/* ===========================================================================
   Corporate polish
   ===========================================================================
   Tightens the density, weight and rhythm of the shared components so the app
   reads as one considered system rather than a set of screens that each grew
   their own spacing.
   =========================================================================== */

/* --- Page header ---------------------------------------------------------- */
.page-head {
  padding-bottom: 18px; margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
  align-items: flex-end;
}
.page-head__title { font-size: 23px; font-weight: 700; letter-spacing: -.022em; }
.page-head__sub { font-size: 13px; color: var(--text-muted); margin-top: 5px; line-height: 1.5; }
.page-head__actions { gap: 8px; flex-wrap: wrap; }

/* --- Filter bar ----------------------------------------------------------- */
/* A toolbar, not a form dump: one row, aligned baselines, actions pinned right. */
.filters {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 14px 16px; margin-bottom: 18px;
  box-shadow: var(--sh-sm);
  /*
   * A grid, not a flex row.
   *
   * As flex, each line shared out its own leftover space among however many
   * fields happened to land on it, and `flex: 0 1 auto` sized every field to
   * its content - a <select> is as wide as its longest option. So Product came
   * out at 319px next to Priority at 148px, row two did not line up with row
   * three, and the bar read as a heap of controls rather than a set of columns.
   *
   * Equal tracks fix it by construction: every control is the same width, and
   * the columns line up down the bar however many filters a page offers.
   * auto-fill with a min() floor also handles narrow screens without a separate
   * rule - the track count simply drops to one.
   */
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr));
  gap: 12px 14px;
  /*
   * start, not end.
   *
   * Aligning to the bottom lines the controls up only while every field is
   * the same height. The moment one carries a hint under its select, that
   * field grows, the row grows with it, and every field WITHOUT a hint drops
   * to the bottom - so its select sits level with the other's hint text
   * rather than with the other's select.
   *
   * Measured on the leads bar: seven controls that should share one baseline
   * sat on three, Handed to 63px high and Handed out by 80px high, and
   * Invoiced was pushed onto a row of its own.
   *
   * From the top the labels line up, and since every label is one line of the
   * same size the controls line up under them. A hint then hangs below its
   * own control without moving anything else.
   */
  align-items: start;
}

/* min-width:0 is what stops a long option from widening its own track. */
.filters .field { margin: 0; min-width: 0; }

/*
 * Search used to earn two tracks via :first-child, which was a lie on any
 * page passing hidden inputs - the hidden input is the first child, so the
 * rule silently did nothing on Follow-Ups while working on Leads. The same
 * bar laid out two different ways depending on a detail nobody can see.
 * One track each, everywhere, and the columns line up on every page.
 */

/* Controls fill their track rather than sizing to their content. */
.filters .input,
.filters .select { width: 100%; max-width: 100%; }
/*
 * One line, always. Aligning from the top only keeps the controls level while
 * the labels above them are the same height, so a label that wrapped on a
 * narrow track would drop its own control half a line below the rest.
 */
.filters .label {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-muted); margin-bottom: 5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.filters .input, .filters .select { min-height: 38px; font-size: 13px; }
/*
 * Pinned to the last track, not given a row of its own.
 *
 * A full-width row put Apply below an otherwise complete bar with an empty
 * track beside it - a hole and an orphan row, one caused by the other. The
 * last column is where the eye already expects the buttons, and when the
 * fields leave that track free the whole bar collapses to a single row.
 *
 * Wraps rather than overflows: a track can be as narrow as 190px, which
 * two buttons only just fit into.
 */
.filters__actions {
  grid-column: -2 / -1;
  display: flex; gap: 8px; justify-content: flex-end; align-items: center;
  flex-wrap: wrap; min-width: 0;
}
/* One track wide is the whole bar, so let it stretch rather than crowd. */
@media (max-width: 480px) {
  .filters__actions { grid-column: 1 / -1; }
  .filters__actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* --- Cards ---------------------------------------------------------------- */
.card { border-radius: var(--r-lg); }
.card__head {
  padding: 16px 20px; gap: 14px;
  border-bottom: 1px solid var(--border); align-items: flex-start;
}
.card__title { display: flex; align-items: center; gap: 8px; font-size: 14.5px; }
.card__title svg { width: 16px; height: 16px; color: var(--text-muted); flex-shrink: 0; }
.card__sub { line-height: 1.5; }

/* --- Tables --------------------------------------------------------------- */
.table th {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .055em; color: var(--text-muted);
  padding: 11px 16px; white-space: nowrap;
  background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.table td { padding: 12px 16px; font-size: 13px; }
.table--compact th { padding: 9px 12px; }
.table--compact td { padding: 9px 12px; font-size: 12.5px; }
.table tbody tr:hover { background: var(--surface-hover); }
.cell-strong { font-weight: 600; color: var(--text); }
.cell-num { font-variant-numeric: tabular-nums; }
.table a.cell-strong:hover { color: var(--brand-600); text-decoration: underline; }

/* Sticky header inside a scrolling table body. */
.table-wrap--tall { max-height: 620px; overflow: auto; }
.table-wrap--tall thead th { position: sticky; top: 0; z-index: 2; }

/* Row actions stay put and stop competing for attention until hovered. */

/* --- Stat tiles ----------------------------------------------------------- */
.stat { border-radius: var(--r-lg); padding: 16px 18px; }
.stat__label { font-size: 10.5px; letter-spacing: .06em; }
.stat__value { font-size: 26px; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.stats { gap: 13px; margin-bottom: 20px; }

/* --- Modals --------------------------------------------------------------- */
.modal {
  background: rgba(15, 23, 42, .55);
  backdrop-filter: blur(2px);
  padding: 24px; align-items: flex-start; overflow-y: auto;
}
.modal__box {
  border-radius: var(--r-xl); box-shadow: 0 24px 64px rgba(15, 23, 42, .28);
  margin: auto; width: 100%;
  animation: modal-rise .18s cubic-bezier(.2, .8, .3, 1);
}
@keyframes modal-rise {
  from { opacity: 0; transform: translateY(12px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
.modal__head {
  padding: 18px 22px; border-bottom: 1px solid var(--border);
  display: flex; align-items: flex-start; justify-content: space-between; gap: 14px;
}
.modal__title { display: flex; align-items: center; gap: 9px; }
.modal__title svg { width: 18px; height: 18px; color: var(--brand-600); flex-shrink: 0; }
.modal__sub { font-size: 12.5px; color: var(--text-muted); margin-top: 3px; line-height: 1.5; }
.modal__body { padding: 20px 22px; max-height: calc(100vh - 230px); overflow-y: auto; }
.modal__foot {
  padding: 15px 22px; border-top: 1px solid var(--border);
  background: var(--surface-2);
  border-radius: 0 0 var(--r-xl) var(--r-xl);
  display: flex; gap: 10px; align-items: center;
}
.modal__foot .toolbar__spacer { flex: 1 1 auto; }
.modal__close {
  background: none; border: 0; cursor: pointer; color: var(--text-muted);
  padding: 5px; border-radius: var(--r-sm); line-height: 0; flex-shrink: 0;
  transition: all var(--t);
}
.modal__close:hover { background: var(--surface-hover); color: var(--text); }
.modal__close svg { width: 18px; height: 18px; }

/* --- Forms ---------------------------------------------------------------- */
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 15px; }
.form-grid .field--full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form-grid { grid-template-columns: minmax(0, 1fr); } }

.label { font-size: 12.5px; font-weight: 600; margin-bottom: 6px; display: block; }
.label .req { color: var(--danger); margin-left: 2px; }
.input, .select, .textarea { font-size: 13.5px; }
.hint { font-size: 11.5px; color: var(--text-muted); margin-top: 5px; line-height: 1.5; }

/* A labelled divider inside a long form, so it reads as sections not a wall. */
.form-section {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 12px;
  margin: 6px 0 2px; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--text-muted);
}
.form-section::after { content: ""; flex: 1; height: 1px; background: var(--border); }

/* --- Buttons -------------------------------------------------------------- */
.btn { font-weight: 600; letter-spacing: -.005em; }
.btn--xs { padding: 5px 8px; }
.btn--icon-only { padding: 6px; }
.btn--icon-only svg { margin: 0; }

/* --- Bulk action bar ------------------------------------------------------ */
/* Docked to the bottom of the viewport rather than pushing the table down,
   so ticking a row never moves the row you were about to tick next. */
.bulkbar {
  position: sticky; bottom: 16px; z-index: 40; margin-top: 16px;
  background: var(--slate-900); color: #fff;
  border-radius: var(--r-lg); box-shadow: 0 12px 32px rgba(15, 23, 42, .3);
  padding: 12px 16px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
[data-theme="dark"] .bulkbar { background: #1e293b; border: 1px solid var(--border-strong); }
.bulkbar__count { font-weight: 700; font-size: 13.5px; white-space: nowrap; }
.bulkbar__count span { font-weight: 500; color: #94a3b8; }
.bulkbar__spacer { flex: 1 1 auto; }
.bulkbar .select, .bulkbar .input {
  min-height: 34px; font-size: 12.5px; width: auto;
  background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.16); color: #fff;
}
.bulkbar .select option { color: var(--text); background: var(--surface); }
.bulkbar .input::placeholder { color: #94a3b8; }
.bulkbar .btn--ghost { color: #cbd5e1; border-color: rgba(255,255,255,.18); }
.bulkbar .btn--ghost:hover { background: rgba(255,255,255,.1); color: #fff; }

/* --- Pagination ----------------------------------------------------------- */
.pagination { padding: 4px 0; gap: 14px; flex-wrap: wrap; }
.pagination__info { font-size: 12.5px; color: var(--text-muted); }
.pagination__link { min-width: 34px; height: 34px; font-size: 13px; }

/* Rows-per-page selector, right of the pager. */
.perpage { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-muted); }
.perpage .select { min-height: 32px; padding: 4px 26px 4px 9px; font-size: 12.5px; width: auto; }

/* --- Empty states --------------------------------------------------------- */
.empty { padding: 46px 24px; }
.empty__icon {
  width: 52px; height: 52px; border-radius: 14px; margin: 0 auto 14px;
  display: grid; place-items: center;
  background: var(--surface-2); color: var(--text-soft);
  border: 1px solid var(--border);
}
.empty__title { font-size: 15px; font-weight: 650; margin-bottom: 5px; }
.empty__text { font-size: 13px; color: var(--text-muted); max-width: 420px; margin: 0 auto; line-height: 1.6; }
.empty__actions { margin-top: 16px; display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }

/* --- Scope banner --------------------------------------------------------- */
/* Shown to a rep whose view is limited to their own leads. Stating it once, up
   front, stops "where did all the leads go?" being asked every Monday. */
.scope-note {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; font-weight: 600; color: var(--brand-700);
  background: var(--brand-50); border: 1px solid var(--brand-200);
  padding: 5px 11px; border-radius: var(--r-full); margin-bottom: 14px;
}
[data-theme="dark"] .scope-note {
  background: rgba(0,114,188,.14); border-color: rgba(0,114,188,.34); color: #7cc4f0;
}
.scope-note svg { width: 13px; height: 13px; }

/* --- Detail key/value ----------------------------------------------------- */
.kv { gap: 0; grid-template-columns: minmax(120px, 170px) 1fr; }
.kv dt {
  padding: 9px 0; border-bottom: 1px solid var(--border);
  font-size: 12px; font-weight: 600; color: var(--text-muted);
}
.kv dd { padding: 9px 0; border-bottom: 1px solid var(--border); font-weight: 500; }
.kv dt:last-of-type, .kv dd:last-of-type { border-bottom: 0; }
@media (max-width: 560px) {
  .kv { grid-template-columns: minmax(0, 1fr); }
  .kv dt { border-bottom: 0; padding-bottom: 0; }
}

/* --- Avatars -------------------------------------------------------------- */
.avatar {
  font-weight: 700; letter-spacing: -.01em;
  background: var(--brand-600); color: #fff;
}
.avatar--muted { background: var(--slate-400); }

/* --- Tabs ----------------------------------------------------------------- */
.tabs { align-items: center; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 18px; }
.tab {
  padding: 10px 16px; font-size: 13.5px; font-weight: 600;
  color: var(--text-muted); border-bottom: 2px solid transparent;
  text-decoration: none; transition: all var(--t); white-space: nowrap;
}
.tab:hover { color: var(--text); }
.tab.is-active { color: var(--brand-600); border-bottom-color: var(--brand-600); }

/* --- Misc ----------------------------------------------------------------- */
.chip {
  font-size: 12px; padding: 4px 10px; border-radius: var(--r-full);
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-muted);
  display: inline-flex; align-items: center; gap: 5px; text-decoration: none;
}
a.chip:hover { border-color: var(--brand-400); color: var(--brand-700); }
[data-theme="dark"] a.chip:hover { color: #7cc4f0; }

.badge { font-size: 11.5px; font-weight: 600; padding: 3px 9px; }
.badge__dot { width: 5px; height: 5px; }

.alert { border-radius: var(--r-lg); padding: 14px 16px; }

.truncate-2 {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; line-height: 1.45;
}

/* Sidebar rhythm */
.nav-group__label {
  font-size: 10px; letter-spacing: .09em; font-weight: 700;
  padding: 0 14px; margin-bottom: 4px;
}
.nav-item { font-size: 13.5px; font-weight: 550; border-radius: var(--r); }
.nav-item.is-active { font-weight: 650; }

/* --- Collapsible "log a call" in the follow-up queue ---------------------- */
.logcall { border-top: 1px solid var(--border); margin-top: 14px; padding-top: 12px; }
.logcall__toggle {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font-size: 12.5px; font-weight: 650; color: var(--brand-600);
  padding: 6px 12px; border-radius: var(--r-sm); list-style: none;
  border: 1px solid var(--brand-200); background: var(--brand-50);
  transition: all var(--t); user-select: none;
}
.logcall__toggle::-webkit-details-marker { display: none; }
.logcall__toggle:hover { background: var(--brand-100); }
.logcall__toggle svg { width: 14px; height: 14px; transition: transform var(--t); }
.logcall[open] .logcall__toggle { background: var(--surface-hover); color: var(--text); border-color: var(--border); }
.logcall[open] .logcall__toggle svg { transform: rotate(45deg); }
.logcall__form { margin-top: 14px; }
[data-theme="dark"] .logcall__toggle {
  background: rgba(0,114,188,.14); border-color: rgba(0,114,188,.34); color: #7cc4f0;
}
[data-theme="dark"] .logcall__toggle:hover { background: rgba(0,114,188,.22); }

/* A chart narrower than its card is centred, not left-stranded. The inline
   max-width Chart:: writes stops the SVG being scaled up past its natural
   size, which is what made every chart look 2x too big on a wide screen. */
.chart__canvas svg { margin-inline: auto; }

/* ===========================================================================
   Row actions
   ===========================================================================
   Two faults this replaces:

   1. `.table__actions` was display:flex with no align-items, so it defaulted to
      `stretch`. A plain <a class="btn"> is 27px tall, but a <form> wrapping a
      button stretched to the full row height and pinned its button to the top -
      so the edit link and the delete button sat on different lines.

   2. Every action carried `opacity: .62` until the row was hovered, on top of
      an already-grey `.btn--ghost`. That is why edit and delete looked like
      they had "no colour": they were washed-out grey glyphs. Destructive and
      constructive actions now read differently at a glance, without hovering.
   =========================================================================== */

/* Deliberately NOT display:flex.
 *
 * A <td> that declares display:flex stops being a table-cell box: the table
 * wraps it in an anonymous cell, and any width set on it applies to the flex
 * box inside rather than to the column. That is what put the icons on top of
 * the Value figures - the column resolved to 16px while the buttons kept their
 * full 98px and hung out of the left edge, because justify-content:flex-end
 * overflows backwards.
 *
 * Keeping the cell a real table-cell lets the table size the column from its
 * content, which is the behaviour that was wanted all along. The buttons are
 * already inline-flex, so they line up on one line; font-size:0 removes the
 * whitespace between them so the only gap is the one specified below. */
.table__actions {
  text-align: right;
  white-space: nowrap;
  font-size: 0;
}
.table__actions > * {
  vertical-align: middle;
  font-size: 12px;              /* restore for any action that carries a label */
}
.table__actions > * + * { margin-left: 4px; }


/* Shrink-to-fit, for every action column in the app rather than just Leads.
 * width:1% is resolved by the table up to the cell's min-content, which is now
 * the true width of the control strip - so the column takes exactly what the
 * buttons need and hands the rest of the row to the data. Users carries four
 * actions and was the next one due to overlap. */
td.table__actions, th.col-actions { width: 1%; }
/* A form is only ever a wrapper for one button here; it must not become a
   flex item with a height of its own. */
.table__actions form {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin: 0;
}

/* Full opacity always. The colour carries the meaning, not the hover. */
.table__actions .btn--xs { opacity: 1; }

/* Uniform square hit target, so a row of icons reads as one control strip. */
.table__actions .btn--icon-only {
  width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); border: 1px solid transparent;
  transition: background var(--t), color var(--t), border-color var(--t);
}
.table__actions .btn--icon-only svg { width: 15px; height: 15px; }

/* --- Semantic action tones ------------------------------------------------
   Neutral by default; the two that matter are tinted. Delete is the only red
   thing in a row, which is exactly how often it should be reached for. */

.btn--act        { color: var(--text-muted); }
.btn--act:hover  { background: var(--surface-hover); color: var(--text); border-color: var(--border); }

.btn--act-view       { color: var(--slate-500); }
.btn--act-view:hover { background: var(--surface-hover); color: var(--text); border-color: var(--border); }

.btn--act-edit       { color: var(--brand-600); }
.btn--act-edit:hover { background: var(--brand-50); color: var(--brand-700); border-color: var(--brand-200); }

.btn--act-danger       { color: var(--danger); }
.btn--act-danger:hover { background: var(--danger-bg); color: var(--danger-fg); border-color: var(--danger); }

.btn--act-go       { color: var(--success); }
.btn--act-go:hover { background: var(--success-bg); color: var(--success-fg); border-color: var(--success); }

.btn--act-key       { color: var(--purple); }
.btn--act-key:hover { background: var(--purple-bg); color: var(--purple-fg); border-color: var(--purple); }

/* Dark mode: the tinted backgrounds above are light washes, so they need
   their own steps rather than inheriting a near-white fill. */
[data-theme="dark"] .btn--act-edit         { color: #7cc4f0; }
[data-theme="dark"] .btn--act-edit:hover   { background: rgba(0,114,188,.18); color: #a5d8f7; border-color: rgba(0,114,188,.4); }
[data-theme="dark"] .btn--act-danger       { color: #f87171; }
[data-theme="dark"] .btn--act-danger:hover { background: rgba(220,38,38,.18); color: #fca5a5; border-color: rgba(220,38,38,.45); }
[data-theme="dark"] .btn--act-go           { color: #4ade80; }
[data-theme="dark"] .btn--act-go:hover     { background: rgba(22,163,74,.18); color: #86efac; border-color: rgba(22,163,74,.45); }
[data-theme="dark"] .btn--act-key          { color: #a78bfa; }
[data-theme="dark"] .btn--act-key:hover    { background: rgba(124,58,237,.18); color: #c4b5fd; border-color: rgba(124,58,237,.45); }
[data-theme="dark"] .btn--act-view:hover   { background: var(--surface-hover); color: var(--text); border-color: var(--border-strong); }

/* Disabled-looking row (an inactive product or account) must not also grey out
   its own controls to the point of being unusable. */
.table tbody tr[style*="opacity"] .table__actions { opacity: 1; }

/* --- Buttons generally ---------------------------------------------------- */
/* Icon and label share a baseline in every button, not just the icon-only ones. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  line-height: 1;
}
.btn svg { flex-shrink: 0; }

/* Toolbars and card heads: keep mixed <a>, <button> and <form> on one line. */
.card__head .btn, .page-head__actions .btn, .toolbar .btn { vertical-align: middle; }
.page-head__actions { display: flex; align-items: center; }
.card__head form { display: inline-flex; align-items: center; margin: 0; }

/* The timeline's Remove button sat flush against the entry above it. */
.tl-item__actions .btn { color: var(--danger); }
.tl-item__actions .btn:hover { background: var(--danger-bg); color: var(--danger-fg); }

/* ===========================================================================
   Responsive tables
   ===========================================================================
   The leads register carries eleven columns. On a 1600px screen with the
   sidebar open there is about 1320px of content width, so the last column -
   the one holding View / Edit / WhatsApp - was pushed off the right edge and
   the only way to reach a button was to scroll the table sideways, for every
   single row.

   Two mechanisms, in order of importance:

   1. The action column is STUCK to the right edge. However far the table is
      scrolled, the buttons are exactly where the user left them. This is the
      fix that matters - it holds at any width, on any device.

   2. Lower-value columns drop out as the viewport narrows, so on a normal
      screen there is nothing to scroll in the first place. Nothing is lost:
      every hidden column is still on the lead's own page, in the export, and
      in the report.
   =========================================================================== */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --- 1. Sticky action column ---------------------------------------------- */
.table--sticky-actions { border-collapse: separate; border-spacing: 0; }

.table--sticky-actions th:last-child,
.table--sticky-actions td:last-child {
  position: sticky;
  right: 0;
  z-index: 2;
  background: var(--surface);

  /* At rest this is just a column, so it gets a column's hairline - the same
     1px the other cells use, and nothing more. The previous rule painted a
     drop shadow unconditionally, which is why a grey smudge sat down the side
     of every table whether or not there was anything to scroll.
     The shadow is added by .is-scrolled-x below, and only then. */
  border-left: 1px solid var(--border);
  transition: box-shadow .18s ease, border-color .18s ease;
}

/* Scrolled: the hairline gives way to a soft edge, because now there really is
   content passing underneath and the column is genuinely floating over it. */
.table-wrap.is-scrolled-x .table--sticky-actions th:last-child,
.table-wrap.is-scrolled-x .table--sticky-actions td:last-child {
  border-left-color: transparent;
  box-shadow: -8px 0 12px -8px rgba(15, 23, 42, .16);
}
[data-theme="dark"] .table-wrap.is-scrolled-x .table--sticky-actions th:last-child,
[data-theme="dark"] .table-wrap.is-scrolled-x .table--sticky-actions td:last-child {
  box-shadow: -8px 0 12px -8px rgba(0, 0, 0, .5);
}
.table--sticky-actions thead th:last-child {
  background: var(--surface-2);
  z-index: 4;
}
.table--sticky-actions tbody tr:hover td:last-child { background: var(--surface-hover); }
[data-theme="dark"] .table--sticky-actions th:last-child,
[data-theme="dark"] .table--sticky-actions td:last-child { background: var(--surface); }
[data-theme="dark"] .table--sticky-actions thead th:last-child { background: var(--surface-2); }
[data-theme="dark"] .table--sticky-actions tbody tr:hover td:last-child { background: var(--surface-hover); }

/* The separate border model loses the shared row rule, so restate it. */
.table--sticky-actions td { border-bottom: 1px solid var(--border); }
.table--sticky-actions thead th { border-bottom: 1px solid var(--border); }
.table--sticky-actions tbody tr:last-child td { border-bottom: 0; }

/* --- 2. Priority columns --------------------------------------------------- */
/* p3 goes first, p1 last. Marked on both the <th> and every <td> so a column
   disappears whole. */
@media (max-width: 1500px) { .table [data-col="p3"] { display: none; } }
@media (max-width: 1280px) { .table [data-col="p2"] { display: none; } }
@media (max-width: 1040px) { .table [data-col="p1"] { display: none; } }

/* ===========================================================================
   Responsive charts
   ===========================================================================
   A chart in the 340px sidebar of a .split was min-width:460px, so it forced
   a scrollbar inside a card that was already too narrow - and the toolbar,
   sized for four buttons, squeezed the heading into a column one word wide.
   =========================================================================== */

.chart__canvas svg { min-width: 0; }

/* Restore a sensible floor only where there is room for one. */
@media (min-width: 900px) {
  .chart__canvas svg { min-width: 320px; }
}

.chart__head { flex-wrap: wrap; }
.chart__heading { flex: 1 1 210px; min-width: 0; }
.chart__tools { flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }

/* In a narrow column the toolbar drops under the heading instead of crushing
   it. Labels give way to icons first, so four buttons still fit on one line. */
@media (max-width: 1180px) {
  .split .chart__tool span,
  .split-wide .chart__tool span { display: none; }
  .split .chart__tool,
  .split-wide .chart__tool { padding: 6px 8px; }
}
@media (max-width: 620px) {
  .chart__tool span { display: none; }
  .chart__tool { padding: 6px 8px; }
  .chart__head { gap: 10px; }
}

/* ===========================================================================
   Layout at narrower widths
   =========================================================================== */

/* The two-column split becomes one column before the sidebar gets too narrow
   to hold a chart. */
@media (max-width: 1240px) {
  /* minmax(0, …) here too - this block comes later in the file and wins, so a
     bare 1fr reinstates the min-content floor the earlier rule removes. */
  .split       { grid-template-columns: minmax(0, 1fr); }
  .split-wide  { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 1100px) {
  .chart-grid--2 { grid-template-columns: minmax(0, 1fr); }
}

/* Filter bar: fields stay usable rather than shrinking to a stub. */
@media (max-width: 860px) {
  /* Only padding needs adjusting; the grid handles the rest by itself. */
  .filters { padding: 12px; gap: 10px 12px; }
}

/* Page header stacks so the title never collides with its buttons. */
@media (max-width: 760px) {
  .page-head { flex-direction: column; align-items: flex-start; gap: 12px; }
  .page-head__actions { width: 100%; flex-wrap: wrap; }
  .page-head__title { font-size: 20px; }
  .content { padding: 16px 14px; }
  .stats--4 { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
  .card__head { flex-direction: column; align-items: stretch; }
  .modal { padding: 12px; }
  .modal__body { padding: 16px; max-height: calc(100vh - 200px); }
  .modal__head, .modal__foot { padding: 14px 16px; }
  .form-grid { grid-template-columns: minmax(0, 1fr); }
}

/* The bulk bar is the one thing that must never wrap into a wall on a phone. */
@media (max-width: 900px) {
  .bulkbar { padding: 10px 12px; gap: 8px; }
  .bulkbar .select, .bulkbar .input { flex: 1 1 140px; min-width: 0; }
  .bulkbar__count { flex: 1 1 100%; }
}

/* Pagination footer: pager and rows-per-page stack rather than overflow. */
@media (max-width: 620px) {
  .card__foot { flex-direction: column; align-items: stretch; }
  .card__foot .toolbar__spacer { display: none; }
  .pagination { flex-direction: column; align-items: flex-start; gap: 10px; }
  .perpage { justify-content: flex-end; }
}

/* Timeline indents less when there is no room for the gutter. */
@media (max-width: 560px) {
  .timeline { padding-left: 22px; }
  .tl-item__dot { left: -22px; width: 17px; height: 17px; font-size: 9px; }
}

/* Never let a long unbroken value (a URL, an email) widen the whole page.
 *
 * `anywhere` outside a table only; inside one it was doing real damage. The
 * difference between the two keywords is not where they break but what they
 * tell the layout engine a box can shrink to: `anywhere` counts a single
 * character as the minimum content width, `break-word` counts the longest
 * word. A table given `anywhere` therefore believes every column can be one
 * letter wide, and `.table { width: 100% }` duly squeezes them all until the
 * text shatters - "Kurunegala" set as "Kuru / neg / ala", a lead number split
 * across two lines.
 *
 * With `break-word` a column can never be narrower than its longest word, so
 * the register overflows into .table-wrap and scrolls, which is what the wrap
 * is for. A genuinely unbreakable token still breaks rather than running away.
 */
.table td { overflow-wrap: break-word; }

/* ===========================================================================
   Register: a wide report read like the spreadsheet it replaces
   ===========================================================================
   Thirty columns of a lead register do not belong in a table that tries to fit
   the screen. Squeezed, every column collapses to its shortest break
   opportunity - and browsers treat a hyphen as one, so "SL-002676" split into
   "SL-" and "002676" and a date wrapped after its comma. Rows ran to three and
   four lines and nothing lined up.

   One line per row instead, scrolling sideways in the wrap. A long comment is
   cut with an ellipsis and keeps its full text in a tooltip; the export was
   always the place to read it in full.
   =========================================================================== */
.table--register th,
.table--register td { white-space: nowrap; }

.table--register td {
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Identifiers, dates and figures are never the column that should give way. */
.table--register td.cell-num { max-width: none; }
.kv dd, .card__sub { overflow-wrap: anywhere; }
.mono { overflow-wrap: anywhere; }

/* Customer picker: a select that shares its row with an "add" button. */
.picker { display: flex; align-items: center; gap: 8px; }
.picker .select { flex: 1 1 auto; min-width: 0; }
.picker .btn { flex: 0 0 auto; }
@media (max-width: 560px) { .picker { flex-wrap: wrap; } .picker .btn { width: 100%; } }

/* ===========================================================================
   Leads register
   ===========================================================================
   The previous layout gave "1st response" and "Current" a column each, but on
   real data they are identical on 70% of rows - so most of the table printed
   the same badge twice, or an empty one. Calls printed a warning-toned "None"
   pill on every untouched lead, which shouts on rows where nothing is wrong.

   Now: one Response column that adds "opened as X" only when the opening
   response actually differed, and one Next step column carrying the callback
   and the call count together. Two columns fewer, and every cell earns its
   width.
   =========================================================================== */

.table--leads td { padding: 10px 14px; vertical-align: middle; }
.table--leads th { padding: 10px 14px; }

/* Predictable widths, so a long company name cannot push the table sideways. */
.table--leads .col-check    { width: 38px; }
.table--leads .col-customer { max-width: 250px; min-width: 190px; }
.table--leads .col-product  { max-width: 190px; }
.table--leads .col-response { max-width: 190px; }
.table--leads .col-next     { width: 130px; }
/* width:1% is the shrink-to-fit idiom: the table resolves it up to the cell's
   min-content, which - now that the buttons cannot shrink - is the real width
   of the control strip. The narrower side padding buys back 16px without
   crowding, since the buttons already carry their own hit area. */
.table--leads td.col-actions { padding-left: 6px; padding-right: 10px; }

/* Second line of a cell: the supporting detail, never competing with the first. */
.cell-sub { font-size: 11.5px; line-height: 1.5; margin-top: 2px; }
.cell-sub .cell-mono { font-size: 11.5px; }

/* --- Priority, as a stripe rather than a column ---------------------------- */
/* A whole column to say "Medium" on 80% of rows is a poor trade; the two that
   matter get a 3px edge instead, readable without being read. */
.lead-row > td:first-child { position: relative; }
.lead-row.is-high > td:first-child::before,
.lead-row.is-urgent > td:first-child::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
}
.lead-row.is-high > td:first-child::before   { background: var(--warning); }
.lead-row.is-urgent > td:first-child::before { background: var(--danger); }

/* A closed lead is still readable, just visibly finished. */
.lead-row.is-closed .cell-strong { font-weight: 550; }
.lead-row.is-closed { color: var(--text-muted); }

/* --- Next step ------------------------------------------------------------- */
.due {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12.5px; font-weight: 600; color: var(--text);
}
.due svg { flex-shrink: 0; }
/*
 * Next step: five states, each one legible, each one distinguishable.
 *
 * "Closed" was plain grey body text with no icon while every other state in the
 * column carried a colour and a mark, so the one that ends the story read as an
 * afterthought. It is a state, not an absence, and now looks like one.
 *
 * Two of the others failed on contrast, measured against the surfaces they
 * actually sit on:
 *
 *   today        --warning     3.19:1 on white - the MOST urgent state was the
 *                              hardest to read. --warning-fg is the same hue
 *                              stepped for text and gives 7.09.
 *   no callback  --text-soft   2.56:1 on white, 3.76 on dark. Recessive is
 *                              right; invisible is not. --text-muted is still
 *                              quieter than everything around it at 4.76/6.97.
 *
 * Colour is never the only signal: late, today and closed each carry an icon,
 * a future date is plain weight, and "no callback set" is italic. A screen
 * reader and a colourblind reader get the same five states.
 */
.due--late   { color: var(--danger); }
.due--today  { color: var(--warning-fg); }
.due--closed { color: var(--text-muted); font-weight: 600; }
.due--none   { color: var(--text-muted); font-weight: 500; font-style: italic; }
[data-theme="dark"] .due--late  { color: #f87171; }
[data-theme="dark"] .due--today { color: #fbbf24; }

/* --- Density ---------------------------------------------------------------
   The table is scanned far more often than it is read, so rows stay tight and
   nothing is allowed to wrap to a third line. */
.table--leads .truncate { max-width: 100%; }
.table--leads .badge { max-width: 100%; }
.table--leads .badge:not(.tone-warning) { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* The permission matrix is denser than a data table, so its hit area matches
   the cell it sits in rather than the roomier 34px used in a lead row. */
.permgrid td .check-hit { width: 30px; height: 30px; margin: -6px 0; }

/* ===========================================================================
   Targets: actual against target, in one cell
   ===========================================================================
   Each metric states its own case - the number reached, the number aimed at,
   and how far along that is - so there is no separate attainment column to
   cross-reference and no metric left without a bar.
   =========================================================================== */
.table--targets td { vertical-align: middle; padding-top: 10px; padding-bottom: 10px; }
.table--targets th { white-space: nowrap; }

.tgt { min-width: 148px; }

.tgt__nums { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }
.tgt__actual { font-weight: 650; font-variant-numeric: tabular-nums; font-size: 13.5px; }
.tgt__of     { font-size: 11px; color: var(--text-muted); }
.tgt__target { font-variant-numeric: tabular-nums; color: var(--text-muted); font-size: 13px; }

/* The target is editable where it is read, so the eye never has to travel. */
.tgt__input {
  width: 84px; min-height: 30px; padding: 2px 8px;
  font-size: 13px; text-align: right;
  font-variant-numeric: tabular-nums;
}

.tgt__bar {
  position: relative; height: 5px; margin-top: 6px;
  border-radius: var(--r-full); background: var(--surface-3, var(--surface-2));
  overflow: hidden;
}
.tgt__bar span { display: block; height: 100%; border-radius: inherit; transition: width var(--t); }
.tgt__bar--good span { background: var(--success); }
.tgt__bar--near span { background: var(--warning); }
.tgt__bar--low  span { background: var(--brand-600); }
.tgt__bar--none span { background: transparent; }

.tgt__pct { margin-top: 4px; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* A target nobody set is stated plainly rather than shown as a full bar. */
.tgt__bar--none { background: repeating-linear-gradient(
  -45deg, var(--surface-2) 0 4px, transparent 4px 8px); }

/* ===========================================================================
   Responsive: small screens
   =========================================================================== */

/* A chart must be allowed to be narrower than the chart inside it.
 *
 * .chart__canvas is an overflow-x container, but a grid or flex item carries
 * min-width:auto - it refuses to shrink below its content - so the figure grew
 * to the full 1076px of a wide timeline chart and took the whole page sideways
 * with it. Explicit zero lets the figure shrink and hands the excess to the
 * scroller that was already there. */
.chart,
.chart__canvas,
.chart--wide { min-width: 0; }

/* Touch targets follow the pointer, not the width.
 *
 * A tablet in landscape is 1024px wide and still operated with a thumb, so
 * sizing these on width alone leaves the most awkward case - a big touch
 * screen - with controls built for a mouse. `pointer: coarse` asks the
 * question that actually matters. The width clause stays for small screens
 * that report a fine pointer, and for browsers that do not answer.
 */
@media (max-width: 900px), (pointer: coarse) {
  /* 27px is comfortable with a cursor and awkward with a thumb. */
  .btn--xs { height: 36px; padding: 0 12px; }
  .chart__tool { min-height: 36px; padding: 0 12px; }
  .table__actions .btn--icon-only { width: 36px; height: 36px; }
  .check-hit { width: 40px; height: 40px; margin: -10px 0; }

  /* Chart toolbars wrap rather than pushing the card wide. */
  .chart__tools { flex-wrap: wrap; }
}

/* Layout, unlike target size, really is about width: a tablet in landscape
   has room for three filter fields even though it is operated by thumb. */
/* Nothing needed for narrow screens any more: auto-fill with a min() floor
   drops the bar to a single column on its own. */

@media (max-width: 560px) {
  /* The page header stacks so a long title never fights its action buttons. */
  .page-head { flex-direction: column; align-items: flex-start; gap: 12px; }
  .page-head__actions { width: 100%; }
  .page-head__actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* An aside inside a section heading: the heading is uppercase and tracked out,
   which is wrong for a parenthetical like "(optional)". */
.form-section__note {
  text-transform: none; letter-spacing: 0;
  font-weight: 500; color: var(--text-muted);
}

/* The call queue's standing-against-target line: a prompt, not a chart. */
.queue-target {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 15px; margin-bottom: 14px;
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--r); background: var(--surface);
  font-size: 13.5px;
}
.queue-target svg { flex: 0 0 auto; }
.queue-target__text { flex: 1 1 240px; min-width: 0; }
.queue-target__bar {
  flex: 0 0 132px; height: 5px; border-radius: var(--r-full);
  background: var(--surface-2); overflow: hidden;
}
.queue-target__bar span { display: block; height: 100%; border-radius: inherit; }

.queue-target--good { border-left-color: var(--success); }
.queue-target--near { border-left-color: var(--warning); }
.queue-target--low  { border-left-color: var(--brand-600); }
.queue-target--good svg { color: var(--success); }
.queue-target--near svg { color: var(--warning); }
.queue-target--low  svg { color: var(--brand-600); }
.queue-target__bar--good span { background: var(--success); }
.queue-target__bar--near span { background: var(--warning); }
.queue-target__bar--low  span { background: var(--brand-600); }

/* ===========================================================================
   Stacked fields inside a card body
   ===========================================================================
   .form-grid spaces its children with gap:16px, so any form built on the grid
   breathes. A form that simply stacks .field divs in a .card__body had no rule
   at all - nothing set a margin, nothing set a gap - so New role, Add a person
   and New feed token ran each label straight into the control above it, and the
   submit button sat flush against the last field.

   Matching the grid's 16px keeps a stacked form and a gridded one on the same
   rhythm, so the two never read as different components.
   =========================================================================== */
.card__body > .field + .field,
.card__body > .field + .form-grid,
.card__body > .form-grid + .field,
.card__body > .field + .checkbox,
.card__body > .checkbox + .field { margin-top: 16px; }

/* The control that ends such a form needs the same separation the .form-actions
   row gets, without every caller having to wrap a single button in one. */
.card__body > .field + .btn,
.card__body > .form-grid + .btn,
.card__body > .field + .callout,
.card__body > .callout + .btn { margin-top: 18px; }

/* --- Distribute: pick who is on the floor ---------------------------------- */
.pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr));
  gap: 2px 14px;
}
/* A count on a toolbar button: how much work the button is about to do. */
.btn__count {
  display: inline-block; margin-left: 6px; padding: 1px 7px;
  border-radius: var(--r-full); background: rgba(0, 0, 0, .10);
  font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.btn--primary .btn__count { background: rgba(255, 255, 255, .22); }

/* --- Foldable form sections ------------------------------------------------
   A heading that owns the fields beneath it, and can put them away. The count
   tells you what is inside without opening it, which is the difference between
   a fold people use and one they ignore. */
.form-section--foldable {
  cursor: pointer;
  user-select: none;
  transition: color var(--t);
}
.form-section--foldable:hover { color: var(--text); }

.form-section__toggle {
  display: inline-flex; align-items: center; gap: 8px;
  margin-left: 2px; padding: 0; border: 0; background: none;
  color: inherit; font: inherit; cursor: pointer;
}
.form-section__chev {
  width: 0; height: 0;
  border-left: 4.5px solid transparent;
  border-right: 4.5px solid transparent;
  border-top: 5px solid currentColor;
  transition: transform var(--t);
}
.form-section--foldable.is-closed .form-section__chev { transform: rotate(-90deg); }

.form-section__count {
  padding: 1px 7px; border-radius: var(--r-full);
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: 10.5px; font-weight: 600; letter-spacing: 0;
  text-transform: none; color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
/* Closed: say what is in there rather than leaving a bare number. */
.form-section--foldable.is-closed .form-section__count::after { content: " hidden"; }

/* The fold toggle is the affordance, but the whole heading row is clickable -
   so give the row itself the height rather than fattening the little chevron. */
.form-section--foldable { padding: 4px 0; }
@media (max-width: 900px), (pointer: coarse) {
  .form-section--foldable { padding: 9px 0; }
  .form-section__toggle { padding: 6px 0; }
}

/* --- Customer search on the lead form -------------------------------------- */
.cust-search { position: relative; margin-bottom: 8px; }
.cust-search__results {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 4px);
  max-height: 280px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--sh-lg, 0 12px 32px rgba(15,23,42,.16));
  padding: 4px;
}
.cust-hit {
  display: block; width: 100%; text-align: left;
  padding: 8px 10px; border: 0; border-radius: var(--r-sm);
  background: none; cursor: pointer; font: inherit; color: var(--text);
}
.cust-hit:hover, .cust-hit:focus { background: var(--surface-2); outline: none; }
.cust-hit__name { font-weight: 600; font-size: 13.5px; }
.cust-hit__meta { font-size: 11.5px; color: var(--text-muted); margin-top: 1px; }
.cust-search__empty { padding: 10px; font-size: 12.5px; color: var(--text-muted); }

/* A rep and how many they take: the name reads first, the number sits beside it. */
.pick-row { display: flex; align-items: center; gap: 10px; }
.pick-row .checkbox { flex: 1 1 auto; min-width: 0; }
.pick-row__qty {
  flex: 0 0 74px; width: 74px;
  min-height: 30px; padding: 2px 8px;
  font-size: 12.5px; text-align: right;
  font-variant-numeric: tabular-nums;
}

/* --- WooCommerce sync bar --------------------------------------------------
   One line above the catalogue: where it is linked, and the button to refresh
   it. The credentials are configured in .env, not here. */
.woo-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px; margin-bottom: 14px;
  border: 1px solid var(--border); border-left: 3px solid var(--brand-600);
  border-radius: var(--r); background: var(--surface);
  font-size: 13px;
}
.woo-bar__text { flex: 1 1 240px; display: flex; align-items: center; gap: 8px; min-width: 0; }
.woo-bar svg { flex: 0 0 auto; color: var(--brand-600); }

/* --- Product row: the photo, then what a rep quotes from -------------------- */
.prod { display: flex; gap: 12px; align-items: flex-start; min-width: 0; }
.prod__img {
  flex: 0 0 52px; width: 52px; height: 52px;
  object-fit: cover; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--border);
}
.prod__img--none {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-soft);
}
.prod__body { min-width: 0; flex: 1 1 auto; }
/* Two lines of description and no more: a shop's copy runs to paragraphs and a
   table row is not the place to read them. */
.prod__descr {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; margin-top: 3px; line-height: 1.45;
}
.prod__tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.prod__tags:empty { display: none; }

/* A choice that cannot do anything says so rather than looking available. */
.checkbox.is-disabled { opacity: .55; cursor: default; }
.checkbox.is-disabled input { cursor: default; }

/* ============================================================================
   Waiting for you
   ----------------------------------------------------------------------------
   The rep's own untouched pile, sitting above the KPI tiles on the dashboard.

   It gets a left edge in the brand colour and nothing else. The temptation with
   a "look at this" band is a tinted fill, but this sits directly above four
   stat tiles and a wall of charts - a filled panel there reads as a fifth chart
   and loses the very emphasis it was reaching for. A 3px edge against the same
   surface as everything else is the quietest mark that still says "this one is
   different", and it survives dark mode without a second colour decision.
   ========================================================================== */
.card--inbox {
  border-left: 3px solid var(--brand);
  margin-bottom: 18px;
}
.card--inbox .card__title .badge {
  /* The count is part of the title, so it sits on the text baseline rather
     than floating at the cap height the badge's own line-height would give it. */
  vertical-align: baseline;
  margin-left: 2px;
}

/* No narrow-screen rule here on purpose. .card__head already wraps and
   stretches its button to full width below ~700px - measured at 390px and
   768px against the "Needs a call" card, which comes out within a pixel.
   A media query that restates what the base already does is a rule the next
   person has to read, test, and discover is inert. */

/* The destructive option in the Distribute dialog.
   Warning ink rather than a filled panel: it sits inside a radio label among
   two calmer siblings, and a block of colour there would read as "selected". */
.hint--warn {
  /* --warning-fg, not --warning: the -fg token is the one redefined under
     dark mode (#92400e -> #fbbf24), so the text stays legible on both
     surfaces. --warning is the solid brand step and does not flip. */
  color: var(--warning-fg);
  display: flex;
  align-items: flex-start;
  gap: 5px;
}
.hint--warn svg { flex: 0 0 auto; margin-top: 1px; }

/* Who already holds this customer, inside a search result.
   Its own line rather than appended to the meta string: the meta line is
   facts about the account (company, number, town) and this is a fact about
   our side of it, which is a different kind of thing to read. */
.cust-hit__rep {
  display: block;
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 2px;
}
.cust-hit__rep.is-shared {
  color: var(--warning-fg);
  font-weight: 600;
}

/* How many leads are waiting, on the Leads nav item.
   Sits at the end of the row and does not push the label: the sidebar
   collapses to icons on narrow screens, where the label is hidden and this
   would otherwise be the only thing left in a row that should be empty. */
.nav-item__count {
  margin-left: auto;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.5;
  text-align: center;
  flex: 0 0 auto;
}
.is-collapsed .nav-item__count,
.sidebar--icons .nav-item__count { display: none; }

/* ============================================================================
   Product lines on a lead
   ----------------------------------------------------------------------------
   A grid rather than a table: the row has to collapse to stacked fields on a
   phone, and a <table> that reflows is a fight with the layout engine every
   time. The column widths are fixed for qty and price because those inputs
   have a known size, and the product select takes whatever is left.
   ========================================================================== */
.lines {
  border: 1px solid var(--border);
  border-radius: var(--r-md, 8px);
  overflow: hidden;
}
.lines__head,
.line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 84px 132px 116px 36px;
  gap: 8px;
  align-items: center;
  padding: 8px 10px;
}
.lines__head {
  background: var(--surface-2, rgba(0, 0, 0, .03));
  border-bottom: 1px solid var(--border);
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.lines__head span:nth-child(2),
.lines__head span:nth-child(3),
.lines__head span:nth-child(4) { text-align: right; }

.line + .line { border-top: 1px solid var(--border); }
.line .input,
.line .select { margin: 0; }
.line .input[data-line-qty],
.line .input[data-line-price] { text-align: right; }

.line__total {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
}
/*
 * Recessive by colour, never by opacity.
 *
 * This sat at opacity .55, which on the light theme looked politely faint
 * and on the dark one measured 2.98:1 against the surface - under the 3.0
 * a non-text graphic needs, and reported as simply invisible. Fading a
 * muted colour drags it toward whatever is behind it, so the darker the
 * theme the more it disappears; the same token at full strength is 6.96.
 *
 * It stays quieter than the controls beside it because --text-muted is
 * quieter than they are, which is a decision that survives a theme change.
 */
.line__drop {
  justify-self: center;
  /*
   * Red at rest, not grey.
   *
   * This removes a line from a quote, which is destructive and small and
   * sits at the end of a row - three reasons it needs to read as a control
   * rather than as decoration. A muted grey was reported as invisible in
   * dark mode, and it was: --danger is re-stepped per theme (#D71920 on
   * white, #f87171 on the dark surface) where the neutral inks are tuned
   * for body text sitting on a card, not for a 16px glyph.
   */
  color: var(--danger);
}
.line__drop:hover,
.line__drop:focus-visible {
  color: var(--danger);
  background: var(--danger-bg);
}

/* The only row left keeps a live button - it clears the row rather than
   removing it, so a wrong pick can be undone without reloading the page. */

.lines__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.lines__sum {
  font-size: 13px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 720px) {
  /* Stacked, with the header dropped - it labels columns that no longer sit
     side by side, and each input carries its own aria-label already. */
  .lines__head { display: none; }
  .line {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "product product"
      "qty     price"
      "total   drop";
    row-gap: 6px;
  }
  .line [data-line-product] { grid-area: product; }
  .line [data-line-qty]     { grid-area: qty; }
  .line [data-line-price]   { grid-area: price; }
  .line__total              { grid-area: total; text-align: left; }
  .line__drop               { grid-area: drop; justify-self: end; }
}

/* The product lines on the read-only lead view. A definition list cell, so the
   list carries no bullet or indent of its own and lines up under the label. */
.line-list { list-style: none; margin: 0; padding: 0; }
.line-list li {
  display: grid;
  grid-template-columns: 3.2em minmax(0, 1fr) auto;
  gap: 8px;
  align-items: baseline;
  padding: 2px 0;
}
.line-list li + li { border-top: 1px dashed var(--border); }
.line-list__qty   { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.line-list__money { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The little "?" beside a permission column heading. Muted and small: it is a
   hint that hovering explains more, not a control. */
.permgrid__what {
  display: inline-block;
  margin-left: 3px;
  width: 13px; height: 13px;
  line-height: 13px;
  border-radius: 50%;
  background: var(--border-strong);
  color: var(--surface);
  font-size: 9px;
  font-weight: 700;
  text-align: center;
  vertical-align: text-top;
  cursor: help;
}

/* A customer the typed number already belongs to.
   Cards rather than chips: each carries three lines of evidence - who, how
   much history, and when it started - which a chip cannot hold legibly. */
.dupe-hit {
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px solid var(--warning-fg);
  border-radius: var(--r-md, 8px);
  background: var(--surface);
}
.dupe-hit + .dupe-hit { margin-top: 6px; }
.dupe-hit__who  { font-weight: 650; font-size: 13px; }
.dupe-hit__meta { font-size: 12px; color: var(--text-muted); margin-top: 1px; }
.dupe-hit__when { font-size: 11.5px; color: var(--text-muted); margin-top: 1px; }
.dupe-hit .btn  { margin-top: 6px; }

/* --- Searchable select ---------------------------------------------------- */
/*
 * A text box in front of a <select>, with a list that narrows as you type. The
 * <select> is still there and still what the form posts; it is hidden only once
 * the script has replaced it, so with JavaScript off the native control is what
 * you get.
 */
.combo { position: relative; min-width: 0; }

.combo__input { width: 100%; padding-right: 30px; text-overflow: ellipsis; }
.combo.has-value .combo__input { font-weight: 600; }

/* The caret is decoration, but it is also the click target for opening the
   list with the mouse, so it needs real size rather than a background image. */
.combo__caret {
  position: absolute; top: 0; right: 0; width: 30px; height: 100%;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.combo__caret::before {
  content: ''; width: 7px; height: 7px; margin-top: -3px;
  border-right: 2px solid var(--text-soft); border-bottom: 2px solid var(--text-soft);
  transform: rotate(45deg); transition: transform .15s ease;
}
.combo.is-open .combo__caret::before { transform: rotate(-135deg); margin-top: 3px; }

.combo__menu {
  /* Fixed, not absolute: the row it sits in is inside a box that clips its
     own rounded corners with overflow:hidden, which swallowed the menu
     whole. Coordinates come from the script, which follows scroll and
     resize while the menu is open. */
  position: fixed; z-index: 200;
  max-height: 320px; overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-lg); box-shadow: var(--sh-lg);
  padding: 4px;
}

.combo__group {
  padding: 8px 10px 4px; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted);
  position: sticky; top: -4px; background: var(--surface);
}

.combo__opt {
  display: flex; align-items: baseline; gap: 10px;
  padding: 7px 10px; border-radius: var(--r-sm); cursor: pointer;
  font-size: 13px; color: var(--text);
}
.combo__opt-name  { flex: 1 1 auto; min-width: 0; }
.combo__opt-price { flex: 0 0 auto; font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/*
 * One highlight, driven by the keyboard cursor, and the mouse moves that same
 * cursor rather than painting a second one. Two independently highlighted rows
 * in a list you can drive both ways is how people pick the wrong item.
 */
.combo__opt.is-cursor { background: var(--brand-600); color: #fff; }
.combo__opt.is-cursor .combo__opt-price { color: rgba(255, 255, 255, .82); }
.combo__opt[aria-selected="true"]:not(.is-cursor) { background: var(--surface-hover); font-weight: 600; }

.combo__empty { padding: 12px 10px; font-size: 13px; color: var(--text-muted); }

@media (hover: hover) {
  .combo__opt:hover:not(.is-cursor) { background: var(--surface-hover); }
}

/* --- The note on a call-list row ------------------------------------------ */
/*
 * What was last said to this person, given its own line rather than a muted
 * span between the call buttons - where it was the first thing to wrap and the
 * last thing anybody read. Two lines at most: enough to know whether it is
 * worth opening the lead, not so much that the list stops being a list.
 */
.fu-note {
  display: flex; gap: 9px; align-items: flex-start;
  margin: 0 0 12px; padding: 9px 11px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.fu-note svg { flex: 0 0 auto; width: 15px; height: 15px; margin-top: 1px; color: var(--text-muted); }
.fu-note__kind {
  display: block; font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-muted); margin-bottom: 2px;
}
.fu-note__text {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; font-size: 13px; color: var(--text); line-height: 1.45;
}

/* --- Live filters --------------------------------------------------------- */
/*
 * A filter takes effect when it is set, so Apply has nothing left to do. The
 * class is added by script, never by the markup: if the script does not run,
 * the button is still there and the bar still works the way it always did.
 */
/*
 * Keyed off an attribute the head script sets before the first paint, not
 * off a class added once the page has loaded. The class version hid the
 * button a few frames too late, so every page load showed Apply and then
 * took it away again.
 */
html[data-js] .filters__actions button[type="submit"] { display: none; }

/*
 * A navigation is under way. The fields fade rather than lock - the page is
 * about to be replaced, and a control that goes dead under the cursor reads as
 * a crash where a control that dims reads as "working on it".
 */
.filters.is-filtering .field { opacity: .55; transition: opacity .12s ease; }
.filters.is-filtering { cursor: progress; }
.filters.is-filtering .filters__actions::before {
  content: ""; width: 15px; height: 15px; border: 2px solid var(--border-strong);
  border-top-color: var(--brand-600); border-radius: 50%;
  animation: spin .65s linear infinite; display: inline-block;
}

@media (prefers-reduced-motion: reduce) {
  .filters.is-filtering .filters__actions::before { animation: none; }
  .filters.is-filtering .field { transition: none; }
}

/* A badge never lets its text out of its pill ------------------------------- */
/*
 * The pill is an inline-flex box and its text is nowrap, so a narrow column
 * shrinks the coloured box while the words keep their full width - the tail of
 * a long status ends up outside the pill, on the page background. Capping the
 * pill at the space available and trimming the label inside it keeps the two
 * together; the full name stays in the title attribute.
 */
.badge { max-width: 100%; }
.badge__text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

/* --- "Is this number already ours?" ---------------------------------------- */
/*
 * Shown under the number as it is typed, on every form that captures a lead.
 * Warning-toned rather than danger: a repeat enquiry from an existing customer
 * is normal and often good news - what must not happen is a second record for
 * them, so this offers the existing one rather than forbidding anything.
 */
.phone-match { margin-top: 8px; display: grid; gap: 8px; }

.phone-hit {
  padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--warning-bg); border: 1px solid var(--warning);
  color: var(--warning-fg);
}
.phone-hit__who  { font-size: 13.5px; }
.phone-hit__meta { font-size: 12px; opacity: .9; margin-top: 2px; }
.phone-hit__act  { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; align-items: center; }
.phone-hit__done { font-size: 12.5px; font-weight: 600; }

/* The pill sits on a tinted panel, so it needs its own ground to stay legible. */
.phone-hit .btn--ghost { background: var(--surface); }

/* The rep field, the moment it is filled in for you --------------------------
 * It sits below the fold on both capture forms, so being set correctly and
 * being seen to be set are two different things - and only the second one stops
 * somebody reporting it as broken.
 */
.field.is-seated .select,
.field.is-seated .combo__input {
  border-color: var(--success);
  /* A plain rgba rather than color-mix: nothing else here relies on it, and
     a browser that does not understand the value drops the whole rule. */
  box-shadow: 0 0 0 3px rgba(22, 163, 74, .22);
  transition: box-shadow .18s ease, border-color .18s ease;
}
@media (prefers-reduced-motion: reduce) {
  .field.is-seated .select,
  .field.is-seated .combo__input { transition: none; }
}

/* Sending the lead somewhere other than the customer's own rep. */
.owner-warning {
  margin-top: 6px; padding: 8px 10px; border-radius: var(--r-sm);
  background: var(--warning-bg); border: 1px solid var(--warning);
  color: var(--warning-fg); font-size: 12.5px; line-height: 1.45;
}

/* --- Jump to a section ----------------------------------------------------- */
/*
 * Twenty charts across five sections is five screens of scrolling to reach the
 * one you came for. A row of anchors, not a script: they survive a reload, they
 * can be bookmarked, and the back button behaves.
 */
.chart-index {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  margin: -6px 0 18px;
}
.chart-index__lead {
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-muted); margin-right: 4px;
}
.chart-index__link {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: var(--r-full);
  background: var(--surface); border: 1px solid var(--border);
  color: var(--text); font-size: 12.5px; font-weight: 600;
  text-decoration: none; white-space: nowrap;
}
.chart-index__link:hover { background: var(--surface-hover); border-color: var(--border-strong); }
.chart-index__link svg { color: var(--text-muted); }

/* The section that was jumped to. :target is the browser telling us where the
   reader is; a focus ring on a scrolled-to region reads as an error otherwise. */
[id^="sec-"]:focus { outline: none; }
[id^="sec-"]:target { scroll-margin-top: 84px; }

/* ===========================================================================
   The marketing calendar.

   Two lanes to a day - design on the left, video on the right - because the
   question the officer is asking while scheduling is not "what is on the 14th"
   but "is one of these two people buried while the other is idle". Colour
   carries the lane, not the person: there is one person per lane today, and
   naming the lane keeps the calendar readable when that changes.

   Both lanes are always drawn, empty or not. An empty lane is the information.
   =========================================================================== */

.mk-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  margin-bottom: 14px;
}
.mk-nav { display: flex; align-items: center; gap: 6px; }
.mk-nav__month { font-size: 15px; margin-left: 6px; white-space: nowrap; }
/* The filters are a normal .filters grid now, so they inherit the alignment
 rule the rest of the app uses and cannot drift from it. */
.mk-filters { margin-bottom: 14px; }

/* One control for month and year, which is the browser own picker. */
.mk-jump { display: inline-flex; }
.mk-jump__month {
  min-height: 32px; font-size: 13px; font-weight: 600;
  padding: 3px 8px; width: auto; min-width: 150px;
}
.mk-bar__new { margin-left: auto; }
.mk-nav .btn.is-current { background: var(--surface-hover); }

/* The month's headline counts. Zeros for the bad news are dropped in the
   template rather than shown as "0 blocked", which reads as a problem. */
.mk-stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.mk-stat {
  display: flex; align-items: baseline; gap: 6px;
  padding: 6px 11px; border-radius: var(--r-full);
  background: var(--surface-2); border: 1px solid var(--border);
  text-decoration: none; color: inherit;
  transition: transform var(--t), box-shadow var(--t);
}
.mk-stat:hover { transform: translateY(-1px); box-shadow: var(--sh-sm); }
.mk-stat:focus-visible { outline: 2px solid var(--brand-600); outline-offset: 2px; }
/* The slice you are looking at, so the strip says where you are. */
.mk-stat.is-active { box-shadow: inset 0 0 0 2px var(--brand-600); }
.mk-stat__n { font-weight: 700; font-size: 14px; font-variant-numeric: tabular-nums; }
.mk-stat__l { font-size: 11.5px; color: var(--text-muted); }
.mk-stat--purple  { background: rgba(139,92,246,.10);  border-color: rgba(139,92,246,.28); }
.mk-stat--teal    { background: rgba(13,148,136,.10);  border-color: rgba(13,148,136,.28); }
.mk-stat--success { background: rgba(22,163,74,.10);   border-color: rgba(22,163,74,.28); }
.mk-stat--warning { background: rgba(217,119,6,.10);   border-color: rgba(217,119,6,.28); }
.mk-stat--danger  { background: rgba(220,38,38,.10);   border-color: rgba(220,38,38,.28); }
.mk-stat--info    { background: rgba(2,132,199,.10);   border-color: rgba(2,132,199,.28); }

/* Calendar beside its panel. One column until there is room for both. */
.mk-layout { display: grid; grid-template-columns: 1fr; gap: 16px; align-items: start; }
@media (min-width: 1180px) {
  .mk-layout--split { grid-template-columns: minmax(0, 1fr) 380px; }
}

.mk-cal {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 12px; box-shadow: var(--sh-sm);
}
.mk-cal__legend {
  display: flex; flex-wrap: wrap; gap: 14px;
  margin-bottom: 10px; font-size: 11.5px; color: var(--text-muted);
}
.mk-key { display: inline-flex; align-items: center; gap: 6px; }
.mk-key__sw { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
/* A legend swatch wears the lane tone, like the chips do. */
.mk-key__sw.mk-tone-slate { background: var(--border-strong); }
.mk-key--late   .mk-key__sw { background: transparent; box-shadow: inset 0 0 0 2px #dc2626; }
.mk-key--done   .mk-key__sw { background: var(--border-strong); }

.mk-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px;
}
.mk-grid__dow {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-muted);
  text-align: center; padding: 4px 0 6px;
}

.mk-day {
  min-height: 116px; padding: 5px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface);
  display: flex; flex-direction: column; gap: 5px;
}
.mk-day--out     { background: var(--surface-2); border-style: dashed; opacity: .5; }
.mk-day--weekend { background: var(--surface-2); }
.mk-day--today   { border-color: var(--brand-600); box-shadow: inset 0 0 0 1px var(--brand-600); }
/* Four or more in a day is the point at which a person is over-committed;
   the tint is a warning to the officer, not an error on the day. */
.mk-day--heavy   { background: rgba(217,119,6,.06); }

.mk-day__head { display: flex; align-items: center; gap: 5px; }
.mk-day__n { font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }
.mk-day--today .mk-day__n { color: var(--brand-700); }
.mk-day__count-unused {
  font-size: 10px; padding: 0 5px; border-radius: var(--r-full);
  background: var(--surface-hover); color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.mk-day__add {
  margin-left: auto; width: 18px; height: 18px; line-height: 16px;
  text-align: center; border-radius: var(--r-sm);
  color: var(--text-soft); text-decoration: none; font-size: 14px;
  opacity: 0; transition: opacity var(--t), background var(--t);
}
.mk-day:hover .mk-day__add,
.mk-day__add:focus-visible { opacity: 1; background: var(--surface-hover); color: var(--brand-600); }

/* The two lanes. Always both, so an empty one reads as spare capacity. */
.mk-lanes { display: flex; flex-direction: column; gap: 3px; flex: 1; min-height: 0; }
/* The hairline between the two lanes: design above it, video below. */
.mk-lanes__rule { border-top: 1px dashed var(--border); margin: 1px 0; }

/* D2 / V1 - the balance in two characters, so titles keep the full cell. */
.mk-tally {
  font-size: 9.5px; font-weight: 700; letter-spacing: .02em;
  padding: 1px 4px; border-radius: var(--r-sm);
  font-variant-numeric: tabular-nums;
}
/* The tally borrows the same palette, so it never needs its own rule. */

.mk-chip {
  display: flex; align-items: center; gap: 4px;
  padding: 3px 5px; border-radius: var(--r-sm);
  font-size: 11px; line-height: 1.25; text-decoration: none;
  border: 1px solid transparent; min-width: 0;
  transition: transform var(--t), box-shadow var(--t);
}
.mk-chip:hover { transform: translateY(-1px); box-shadow: var(--sh-sm); }
.mk-chip:focus-visible { outline: 2px solid var(--brand-600); outline-offset: 1px; }
.mk-chip__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.mk-chip__dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; background: currentColor; }
.mk-chip__pri {
  margin-left: auto; font-weight: 700; font-size: 9.5px;
  opacity: .85; flex: 0 0 auto;
}

/*
 * Lane colour comes from the lane row, not from its name.
 *
 * There were two rules here, .mk-chip--design and .mk-chip--video, which
 * meant a third lane was styleless until somebody remembered to add a third
 * rule. The palette is declared once and a lane names the tone it wants, so
 * a lane created this afternoon is styled this afternoon.
 */
.mk-tone-purple  { background: rgba(139,92,246,.13); color: #6d28d9; }
.mk-tone-teal    { background: rgba(13,148,136,.13); color: #0f766e; }
.mk-tone-brand   { background: rgba(0,114,188,.13);  color: var(--brand-700); }
.mk-tone-pink    { background: rgba(219,39,119,.13); color: #9d174d; }
.mk-tone-warning { background: rgba(217,119,6,.13);  color: #92400e; }
.mk-tone-info    { background: rgba(2,132,199,.13);  color: #075985; }
.mk-tone-success { background: rgba(22,163,74,.13);  color: #15803d; }
.mk-tone-danger  { background: rgba(220,38,38,.13);  color: #991b1b; }
.mk-tone-slate   { background: var(--surface-hover); color: var(--text-muted); }

/* Done recedes: it is finished work and the eye should skip it. */
/* Finished work recedes, whatever lane it is in - so this outranks the tone. */
.mk-chip.mk-chip--done { background: var(--surface-2); color: var(--text-soft); }
.mk-chip--done .mk-chip__title { text-decoration: line-through; }

.mk-chip--blocked { box-shadow: inset 0 0 0 1px rgba(217,119,6,.55); }
/* Past its day and not finished - the one thing worth interrupting for. */
.mk-chip--late    { box-shadow: inset 0 0 0 1px #dc2626; }
.mk-chip.is-open  { outline: 2px solid var(--brand-600); outline-offset: 1px; }

[data-theme="dark"] .mk-tone-purple  { background: rgba(139,92,246,.24); color: #c4b5fd; }
[data-theme="dark"] .mk-tone-teal    { background: rgba(13,148,136,.24); color: #5eead4; }
[data-theme="dark"] .mk-tone-brand   { background: rgba(0,114,188,.26);  color: var(--brand-300); }
[data-theme="dark"] .mk-tone-pink    { background: rgba(219,39,119,.24); color: #f9a8d4; }
[data-theme="dark"] .mk-tone-warning { background: rgba(217,119,6,.24);  color: #fcd34d; }
[data-theme="dark"] .mk-tone-info    { background: rgba(2,132,199,.24);  color: #7dd3fc; }
[data-theme="dark"] .mk-tone-success { background: rgba(22,163,74,.24);  color: #86efac; }
[data-theme="dark"] .mk-tone-danger  { background: rgba(220,38,38,.24);  color: #fca5a5; }
[data-theme="dark"] .mk-day--heavy   { background: rgba(217,119,6,.12); }
[data-theme="dark"] .mk-stat--purple  { background: rgba(139,92,246,.18); }
[data-theme="dark"] .mk-stat--teal    { background: rgba(13,148,136,.18); }
[data-theme="dark"] .mk-stat--success { background: rgba(22,163,74,.18); }
[data-theme="dark"] .mk-stat--warning { background: rgba(217,119,6,.18); }
[data-theme="dark"] .mk-stat--danger  { background: rgba(220,38,38,.18); }
[data-theme="dark"] .mk-stat--info    { background: rgba(2,132,199,.18); }

/* The side panel: the officer's form, or a read-only sheet plus the thread. */
.mk-panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--sh-sm);
  position: sticky; top: 78px; max-height: calc(100vh - 96px);
  display: flex; flex-direction: column; overflow: hidden;
}
.mk-panel__head {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 14px; border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.mk-panel__head strong { font-size: 13.5px; min-width: 0; overflow-wrap: anywhere; }
.mk-panel__head .btn { margin-left: auto; flex: 0 0 auto; }
.mk-panel__body { padding: 14px; display: flex; flex-direction: column; gap: 12px; }
.mk-panel__danger { padding: 0 14px 12px; }
.mk-panel__danger .btn { color: var(--danger); }

.mk-facts { display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; font-size: 13px; margin: 0; }
.mk-facts dt { color: var(--text-muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; }
.mk-facts dd { margin: 0; }
.mk-brief p { margin: 4px 0 0; font-size: 13px; line-height: 1.6; }

.mk-thread { border-top: 1px solid var(--border); display: flex; flex-direction: column; }
.mk-thread__head {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px; font-size: 11.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted);
}
.mk-thread__head span { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: 400; }

.mk-notes { list-style: none; margin: 0; padding: 0 14px; display: flex; flex-direction: column; gap: 10px; }
.mk-notes__empty { padding: 0 14px 10px; }
.mk-note { border-left: 2px solid var(--border); padding-left: 10px; }
.mk-note__meta { display: flex; gap: 8px; font-size: 11px; color: var(--text-muted); }
.mk-note__meta strong { color: var(--text); }
.mk-note__move { font-size: 12px; margin-top: 2px; }
.mk-note__text { font-size: 13px; margin-top: 3px; line-height: 1.55; overflow-wrap: anywhere; }

.mk-progress { padding: 12px 14px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 10px; }
.mk-progress__locked { padding: 0 14px 12px; display: flex; gap: 6px; align-items: flex-start; }

.mk-below { margin-top: 16px; }

/* A month on a phone is a column of days, not a squeezed grid. */
@media (max-width: 720px) {
  .mk-grid { grid-template-columns: 1fr; }
  .mk-grid__dow { display: none; }
  .mk-day { min-height: 0; }
  .mk-day--out { display: none; }
  .mk-day__add { opacity: 1; }
  .mk-panel { position: static; max-height: none; }
}

/*
 * One scrolling region, one footer.
 *
 * The commit button used to sit inside the form with position:sticky, which
 * pinned it to the bottom of the form's own scroll box - and the form is only
 * the top third of the dialog, so Save floated across the middle of the
 * fields with the files and the thread still visible below it.
 *
 * A dialog has one thing that scrolls and one row that does not.
 */
.mk-panel__scroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column;
}
.mk-panel__foot {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}
/* Pushes Cancel and Save to the right, and keeps Remove away from them so a
   hand reaching for Save cannot land on it. */
.mk-panel__foot-gap { flex: 1 1 auto; }
.mk-panel__foot-danger { color: var(--danger); }
.mk-panel__foot-danger:hover { background: rgba(220,38,38,.08); }

/* View switcher, the Plan drawer, weeks, the list and attachments. */
.mk-views { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-full); overflow: hidden; }
.mk-view {
  padding: 5px 12px; font-size: 12px; text-decoration: none;
  color: var(--text-muted); background: var(--surface);
  border-right: 1px solid var(--border);
}
.mk-view:last-child { border-right: 0; }
.mk-view.is-on { background: var(--brand-600); color: #fff; font-weight: 600; }
.mk-view:hover:not(.is-on) { background: var(--surface-hover); color: var(--text); }

/* Two planning tools that are used once a month - reachable, not in the way. */
.mk-more { position: relative; }
.mk-more > summary { list-style: none; cursor: pointer; }
.mk-more > summary::-webkit-details-marker { display: none; }
.mk-more__panel {
  position: absolute; z-index: 40; top: calc(100% + 6px); right: 0;
  width: min(340px, 92vw); max-height: 70vh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--sh-lg); padding: 12px;
  display: flex; flex-direction: column; gap: 16px;
}
.mk-mini { display: flex; flex-direction: column; gap: 6px; }
.mk-mini + .mk-mini { border-top: 1px solid var(--border); padding-top: 14px; }
.mk-mini__title { font-size: 12.5px; font-weight: 700; }
.mk-mini__row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.mk-mini .hint { margin: 0 0 2px; }

.mk-week { margin-bottom: 12px; border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
.mk-week__head {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 12px; background: var(--surface-2); font-size: 12.5px;
}
.mk-week__days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 1px; background: var(--border); }
.mk-week__day { background: var(--surface); padding: 7px; min-height: 92px; display: flex; flex-direction: column; gap: 3px; }
.mk-week__day.is-today { box-shadow: inset 0 0 0 2px var(--brand-600); }
.mk-week__n { font-size: 11px; color: var(--text-muted); font-weight: 600; }
.mk-week__none { color: var(--text-soft); font-size: 12px; }

.mk-list tr.is-late td { background: rgba(220,38,38,.04); }
[data-theme="dark"] .mk-list tr.is-late td { background: rgba(220,38,38,.10); }

.mk-files { border-top: 1px solid var(--border); }
.mk-filelist { list-style: none; margin: 0; padding: 0 14px 4px; display: flex; flex-direction: column; gap: 7px; }
/*
 * A reference is shown, not named.
 *
 * A filename tells a designer nothing about whether it is the right shot;
 * they have to download it to find out, and four attachments is four
 * downloads to answer one question.
 */
.mk-file { display: flex; align-items: flex-start; gap: 9px; font-size: 12.5px; }
.mk-file__thumb {
  flex: 0 0 auto; width: 64px; height: 46px; border-radius: var(--r-sm);
  overflow: hidden; border: 1px solid var(--border); background: var(--surface-2);
  display: block;
}
.mk-file__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-file__video {
  flex: 0 0 auto; width: 132px; max-height: 84px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: #000; display: block;
}
.mk-file__doc {
  flex: 0 0 auto; width: 46px; height: 46px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface-2);
  display: grid; place-items: center; color: var(--text-muted);
}
.mk-file__body { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mk-file__name { display: inline-flex; align-items: center; gap: 5px; min-width: 0; overflow-wrap: anywhere; }
.mk-file__meta { color: var(--text-muted); font-size: 11px; }
.mk-file form { margin-left: auto; }
.mk-file__x {
  border: 0; background: none; cursor: pointer; font-size: 15px; line-height: 1;
  color: var(--text-soft); padding: 0 4px;
}
.mk-file__x:hover { color: var(--danger); }
.mk-upload { padding: 10px 14px 14px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.mk-upload .input { flex: 1 1 170px; font-size: 12px; }
.mk-upload .hint { flex: 1 0 100%; margin: 0; }

@media (max-width: 720px) {
  .mk-week__days { grid-template-columns: 1fr; }
  .mk-more__panel { position: static; width: auto; box-shadow: none; }
}

/* Ad spend: a daily job, so the form is in front of you rather than behind a
   button, and it borrows the filter bar's grid so it lines up with everything
   else on the page. */
.mk-spendform {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(180px, 100%), 1fr));
  gap: 12px 14px; align-items: start;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 14px 16px; margin-bottom: 6px;
  box-shadow: var(--sh-sm);
}
.mk-spendform__note { margin: 0 0 14px; }
.mk-stat--brand { background: rgba(0,114,188,.10); border-color: rgba(0,114,188,.28); }
[data-theme="dark"] .mk-stat--brand { background: rgba(0,114,188,.20); }

/* The time a chip is at, ahead of its title so a day reads as a schedule. */
.mk-chip__at { font-variant-numeric: tabular-nums; font-weight: 600; opacity: .85; flex: 0 0 auto; }

/* The lane editor: one row per lane, each its own form so a save touches one. */
.mk-lanerow { padding: 6px 10px !important; }
.mk-laneform { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.mk-laneform .input, .mk-laneform .select { width: auto; min-height: 32px; font-size: 12.5px; }
.mk-laneform input[name="lane_name"] { flex: 1 1 180px; }
.mk-laneform input[name="lane_order"] { width: 5.5rem; }
.mk-laneform__used { font-size: 11.5px; color: var(--text-muted); min-width: 4.5rem; }

/* The task dialog. It borrows .modal for the backdrop and the box, and keeps
   the panel's own internals - the thread and the files scroll inside it. */
.mk-modal .mk-panel {
  position: static; max-height: min(86vh, 900px);
  width: 100%; box-shadow: none; border: 0; border-radius: var(--r-lg);
}
.mk-modal .mk-panel__head { border-radius: var(--r-lg) var(--r-lg) 0 0; }
.mk-panel__sub {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--text-muted); margin-top: 3px;
}
@media (min-width: 900px) { .mk-modal .modal__box { max-width: 620px; } }

/* ===========================================================================
   The day-end report
   ===========================================================================
   A report, not a dashboard. The panels sit in a grid that reflows rather than
   a fixed set of columns, so one piece of markup reads on a laptop, on the
   projector at the morning meeting, and on paper - and paper matters here,
   because printing is how this leaves the building.
   =========================================================================== */

.card--report .card__actions { display: flex; gap: 8px; flex-wrap: wrap; }

.report-head {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.report-stat {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  padding: 12px 14px;
}
.report-stat__n {
  font-size: 26px; font-weight: 700; line-height: 1.1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.report-stat__k { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

/*
 * The wide modifier is for the cross-tab and the two long lists. Everything
 * else is a short table of people and packs two or three abreast.
 */
/*
 * A laid-out report, not a bag of panels.
 *
 * auto-fit put as many columns on the row as would fit - four on a wide
 * screen - so the three top panels sat in three of them and left the fourth
 * empty, and align-items:start let each one stop at its own height, giving
 * three ragged bottoms and a hand-span of white to the right of the lot.
 *
 * Twelve columns and an explicit span per panel instead: the three lists
 * take a third each, the responses and the cross-tab take the width, and the
 * closing pair takes a half each. Panels on a row stretch to the tallest, so
 * their caps and their feet line up.
 */
.report-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 14px;
  align-items: stretch;
}
.report-panel { grid-column: span 4; display: flex; flex-direction: column; }
.report-panel--half { grid-column: span 6; }
.report-panel--wide { grid-column: 1 / -1; }

/* The table takes the slack, so a short panel is padded at the foot rather
   than leaving its neighbour standing on nothing. */
.report-panel .table-wrap { flex: 1 1 auto; }

/* Two abreast on a laptop, one on a phone - a three-column table of people
   set at 320px is unreadable however neatly it is aligned. */
@media (max-width: 1180px) {
  .report-panel, .report-panel--half { grid-column: span 6; }
}
@media (max-width: 720px) {
  .report-grid { grid-template-columns: minmax(0, 1fr); }
  .report-panel, .report-panel--half, .report-panel--wide { grid-column: 1 / -1; }
}
.report-panel {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--surface);
}

/*
 * Headings wrap; they do not push the numbers off the panel.
 *
 * These come from the sales workbook and some of them are a sentence -
 * "Actual Count of Response(1st follow up)". Left on one line that heading
 * was wider than the panel, and the count beside it was clipped mid-digit:
 * 120 read as 12. A wrong number is worse than a tall header.
 */
.report-panel th { white-space: normal; vertical-align: bottom; line-height: 1.3; }
.report-panel td { white-space: normal; }

/*
 * A long list scrolls on screen and prints whole.
 *
 * Forty products, most of them asked for once, is honest but it buries the
 * panels under it. On paper there is nothing to scroll into, so the cap
 * lifts and the whole list prints.
 */
.report-panel--scroll .table-wrap { max-height: 430px; overflow-y: auto; }

/* The title and its download button share the cap. */
.report-panel__head {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  padding-right: 8px;
}
.report-panel__head .report-panel__title { flex: 1 1 auto; background: none; border: 0; }
.report-panel__png { flex: 0 0 auto; }

.report-panel__title {
  margin: 0;
  padding: 10px 14px;
  font-size: 13px; font-weight: 600; letter-spacing: .01em;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}

/* A total belongs to the column above it, so it is ruled rather than coloured. */
.report-total td {
  border-top: 2px solid var(--border);
  font-weight: 700;
  background: var(--surface-2);
}

.report-panel__note {
  margin: 0;
  padding: 8px 14px 0;
  font-size: 11px;
  color: var(--text-muted);
  line-height: 1.45;
}

/*
 * The response tiles.
 *
 * The one panel that is not a table: a name and a count read faster as a
 * tile than as a two-column list with a hand-span of white down the middle,
 * which is how the team's own sheet shows it too.
 */
.report-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 10px;
  padding: 14px;
}
.report-tile {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  background: var(--surface);
}
.report-tile__k {
  font-size: 10.5px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted);
  line-height: 1.35; min-height: 2.7em;
}
.report-tile__n {
  font-size: 22px; font-weight: 700; line-height: 1.15;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.report-tile__s { font-size: 11px; color: var(--text-muted); }

/*
 * The poster the download is made from.
 *
 * Out of sight but in the document, because the button serialises the live
 * element. display:none would do as well, but a zero-size clip keeps the SVG
 * measurable if it ever needs to be - a hidden subtree reports no geometry.
 */
.report-poster {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  pointer-events: none;
}

.report-source {
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--text-muted);
  line-height: 1.5;
}

/*
 * On paper.
 *
 * There is no PDF library in this application and the browser's own Save as
 * PDF is better than one, so print is the PDF. The page already hides its
 * chrome and sets .card { break-inside: avoid } for print; only what those
 * rules get wrong for a long report is restated here.
 *
 * The card must be allowed to break - it is the whole report and will not fit
 * on one page - while each panel inside it must not, because a rep table split
 * across a fold is where somebody misreads a total.
 */
@media print {
  .chart-index,
  .callout,
  .report-poster,
  .report-panel__png,
  .card__actions { display: none !important; }

  .card--report { border: 0; box-shadow: none; break-inside: auto; }

  .report-panel { break-inside: avoid; page-break-inside: avoid; }
  .report-grid  { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .report-panel--wide { grid-column: 1 / -1; }

  .report-head    { grid-template-columns: repeat(4, 1fr); }
  .report-stat__n { font-size: 20px; }

  /* The source line is half the reason for printing it, so it stays whole. */
  .report-source { break-inside: avoid; color: #000; }

  /*
   * Print the report, not the page it lives on.
   *
   * Analytics Studio is a long page of charts and the browser will happily
   * give you all twenty-one pages of it. The button on the report sets this
   * class first, so what comes out of the printer is the evening report and
   * nothing else; the page-level Print button is untouched and still prints
   * everything, which is what it is for.
   */
  body.printing-report .content > *:not(.card--report) { display: none !important; }
  body.printing-report .card--report { margin: 0; }

  .table      { font-size: 10px; }
  .table-wrap { overflow: visible !important; }
  /*
   * The long list prints whole, and prints last.
   *
   * On screen it scrolls inside its panel; on paper there is nothing to
   * scroll into, so the cap lifts and all forty products come out. Left in
   * place that one column runs three times the height of its neighbours and
   * leaves half a page blank beside them, so on paper it goes to the end -
   * where a long tail belongs anyway. Nothing is dropped: a printed report
   * that quietly omits rows is worse than an untidy one.
   */
  .report-panel--scroll .table-wrap { max-height: none !important; overflow: visible !important; }
  .report-panel--scroll { order: 99; }
}
