/* ── Chatbot Platform Admin Panel ── */
:root {
  --noble-primary: #55072b;
  --noble-dark: #3a0520;
  --noble-light: #f8f4f6;
  --noble-accent: #8b1a4a;
  --green: #16a34a;
  --yellow: #d97706;
  --red: #dc2626;
  --blue: #2563eb;
  --gray-50: #f9fafb;
  --gray-100: #f3f4f6;
  --gray-200: #e5e7eb;
  --gray-300: #d1d5db;
  --gray-400: #9ca3af;
  --gray-500: #6b7280;
  --gray-600: #4b5563;
  --gray-700: #374151;
  --gray-800: #1f2937;
  --gray-900: #111827;
  --radius: 10px;
  --radius-sm: 7px;
  --radius-lg: 16px;
  --radius-pill: 999px;

  /* Layered shadows: a tight contact shadow plus a wide soft one, so surfaces
     read as lifted paper instead of outlined boxes. */
  --shadow: 0 1px 2px rgba(17,24,39,0.04), 0 4px 12px -4px rgba(17,24,39,0.10);
  --shadow-sm: 0 1px 2px rgba(17,24,39,0.05);
  --shadow-md: 0 2px 4px rgba(17,24,39,0.04), 0 14px 32px -10px rgba(17,24,39,0.18);
  --shadow-lg: 0 4px 8px rgba(17,24,39,0.04), 0 28px 60px -16px rgba(17,24,39,0.28);
  --ring: 0 0 0 3px rgba(85,7,43,0.14);

  /* One motion vocabulary for the whole panel. --ease-out for anything that
     settles, --ease-spring for controls that should feel physical (chips,
     toggles, buttons). Durations stay short: this is a tool people live in. */
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.30, 0.54, 1);
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 320ms;

  /* The indigo already used for links and meta throughout the panel, named so
     new code stops re-typing the hex. */
  --accent-ink: #4f46e5;

  /* One palette for a chat message, shared by the live-chat page and the
     conversations preview panel. Who sent it is carried by color + side:
     the visitor on the left in neutral, the team in green, the AI in violet
     (the same violet the "AI draft" affordance already uses). */
  --msg-visitor-bg: #f4f6f9;
  --msg-visitor-line: #e3e8ef;
  --msg-team-bg: #f0fdf6;
  --msg-team-line: #b6ecd0;
  --msg-ai-bg: #f7f5ff;
  --msg-ai-line: #e5def9;
}

/* Cross-document view transitions: navigating the admin becomes a soft
   cross-fade + rise instead of a white flash. Progressive enhancement — browsers
   without support just navigate instantly. The sidebar gets its own transition
   name so it stays put rather than cross-fading with the page body. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }

  ::view-transition-old(root) {
    animation: adminPageOut var(--dur-2) var(--ease-out) both;
  }
  ::view-transition-new(root) {
    animation: adminPageIn var(--dur-3) var(--ease-out) both;
  }
  @keyframes adminPageOut {
    to { opacity: 0; transform: translateY(-4px); }
  }
  @keyframes adminPageIn {
    from { opacity: 0; transform: translateY(8px); }
  }
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: var(--gray-50);
  color: var(--gray-800);
  line-height: 1.5;
}

/* ── Login ── */
.login-body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: var(--noble-light);
}

.login-card {
  background: white;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 40px;
  width: 360px;
  text-align: center;
}

.login-brand { margin-bottom: 24px; }
.login-brand h1 { font-size: 20px; color: var(--gray-800); margin-top: 12px; }
.login-brand p { color: var(--gray-500); font-size: 14px; }

.login-error {
  background: #fef2f2;
  color: var(--red);
  padding: 8px 12px;
  border-radius: var(--radius);
  font-size: 14px;
  margin-bottom: 16px;
}

.login-card input {
  width: 100%;
  padding: 10px 14px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-size: 15px;
  margin-bottom: 12px;
  outline: none;
}
.login-card input:focus { border-color: var(--noble-primary); box-shadow: 0 0 0 3px rgba(85,7,43,0.1); }

.login-card button {
  width: 100%;
  padding: 10px;
  background: var(--noble-primary);
  color: white;
  border: none;
  border-radius: var(--radius);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
}
.login-card button:hover { background: var(--noble-dark); }

/* ── Layout ── */
.admin-layout {
  display: flex;
  min-height: 100vh;
}

.admin-sidebar {
  width: 220px;
  background: var(--noble-primary);
  color: white;
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 10;
  /* Held out of the root view transition so the rail reads as app chrome that
     persists while only the page body changes. */
  view-transition-name: admin-rail;
}

.sidebar-header {
  padding: 14px 16px 10px;
  border-bottom: 1px solid rgba(255,255,255,0.15);
}
.sidebar-header .sidebar-brand {
  display: block;
  padding: 0 0 8px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
}
.sidebar-header .sidebar-brand:hover {
  opacity: 0.85;
}
/* Collapsed, the rail is icons only and a wordmark would just be clipped. */
.sidebar-collapsed .sidebar-header .sidebar-brand {
  display: none;
}
.sidebar-header .sidebar-user {
  padding: 0 0 6px;
}
.sidebar-header .sidebar-agent-status {
  padding: 0;
}

.sidebar-nav {
  list-style: none;
  padding: 12px 0;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.25) transparent;
}
.sidebar-nav::-webkit-scrollbar { width: 6px; }
.sidebar-nav::-webkit-scrollbar-track { background: transparent; }
.sidebar-nav::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.2);
  border-radius: 3px;
}
.sidebar-nav::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.35); }

/* Nav rows are inset pills rather than full-bleed bands: the breathing room on
   either side is what stops a 20-item rail from reading as a wall of stripes. */
.sidebar-nav a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 1px 8px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  color: rgba(255,255,255,0.72);
  text-decoration: none;
  font-size: 14px;
  transition: color var(--dur-1) var(--ease-out),
              background-color var(--dur-1) var(--ease-out);
}
.sidebar-nav a svg {
  flex: none;
  opacity: 0.8;
  transition: opacity var(--dur-1) var(--ease-out),
              transform var(--dur-2) var(--ease-spring);
}
.sidebar-nav a:hover { color: white; background: rgba(255,255,255,0.09); }
.sidebar-nav a:hover svg { opacity: 1; transform: translateX(1px); }
.sidebar-nav a.active { color: white; background: rgba(255,255,255,0.14); font-weight: 500; }
.sidebar-nav a.active svg { opacity: 1; }
/* Active marker grows out of the pill's left edge instead of appearing. */
.sidebar-nav a.active::before {
  content: '';
  position: absolute;
  left: -4px;
  top: 50%;
  width: 3px;
  height: 18px;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.9);
  transform: translateY(-50%) scaleY(1);
  animation: navMarkIn var(--dur-3) var(--ease-spring) both;
}
@keyframes navMarkIn {
  from { transform: translateY(-50%) scaleY(0.2); opacity: 0; }
}
.sidebar-nav a:focus-visible,
.sidebar-footer a:focus-visible {
  outline: 2px solid rgba(255,255,255,0.7);
  outline-offset: 1px;
}

.sidebar-agent-status {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 4px;
}
.sidebar-agent-status .toggle-switch input:checked + .toggle-slider,
.agent-status-toggle:checked + .toggle-slider {
  background: #16a34a;
}
.agent-status-label {
  font-size: 13px;
  color: rgba(255,255,255,0.5);
  font-weight: 500;
}
.agent-status-label.is-available {
  color: #4ade80;
}

/* ── Phone status in the rail ──
   Chrome, not page furniture: it lives here (rather than in phone-dock.css)
   because the row renders on the Calls page too, where the dock's stylesheet
   is deliberately not loaded.

   Sized like the Notify/Sound buttons it sits above so the header reads as
   one stack of controls, with two differences it earns: a presence dot on the
   icon, and a ringing state loud enough to catch an agent who is looking at
   the middle of an inbox thread. The dot is a badge ON the icon rather than a
   fourth leading glyph, so the collapsed 60px rail keeps both. */
/* The header already pads to 16px, same as .sidebar-agent-status above. */
.sidebar-phone { padding: 6px 0 0; }
.sidebar-phone-btn {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 4px 8px;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 4px;
  color: rgba(255,255,255,0.85);
  font: inherit;
  font-size: 11px;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  transition: background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.sidebar-phone-btn:hover { background: rgba(255,255,255,0.08); }
/* Open: the row reads as the panel's handle rather than a button that did
   nothing, which matters when the panel is anchored beside it and could
   otherwise look like it belongs to the page. */
.sidebar-phone-btn[aria-expanded="true"] {
  background: rgba(255,255,255,0.14);
  border-color: rgba(255,255,255,0.4);
}
.sidebar-phone-btn[aria-expanded="true"] .phone-caret { rotate: 180deg; opacity: 0.9; }
.sidebar-phone-btn:focus-visible {
  outline: 2px solid rgba(255,255,255,0.7);
  outline-offset: 1px;
}
.sidebar-phone .phone-icon {
  position: relative;
  display: flex;
  flex: 0 0 auto;
  color: rgba(255,255,255,0.75);
}
.sidebar-phone .phone-dot {
  position: absolute;
  right: -3px;
  bottom: -2px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #9ca3af;
  /* Ringed in the rail's own colour so the dot reads as a badge and never
     merges into the phone glyph behind it. */
  box-shadow: 0 0 0 2px var(--noble-primary);
}
.sidebar-phone .phone-name { color: rgba(255,255,255,0.6); }
.sidebar-phone .phone-status {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: 600;
  color: rgba(255,255,255,0.85);
}
.sidebar-phone .phone-caret {
  flex: 0 0 auto;
  margin-left: auto;
  opacity: 0.6;
  transition: rotate var(--dur-1) var(--ease-out);
}

/* Ready / on a call / ringing. Colours are the softphone's own dialer dots,
   lightened for a dark rail — the same state must not read as two different
   colours depending on which surface an agent is looking at. */
.sidebar-phone[data-state="ready"] .phone-dot { background: #4ade80; }
.sidebar-phone[data-state="ready"] .phone-status { color: #4ade80; }
.sidebar-phone[data-state="oncall"] .phone-dot { background: #60a5fa; }
.sidebar-phone[data-state="oncall"] .phone-status { color: #93c5fd; }
.sidebar-phone[data-state="error"] .phone-dot { background: #fb923c; }
.sidebar-phone[data-state="error"] .phone-status { color: #fdba74; }
.sidebar-phone[data-state="ringing"] .sidebar-phone-btn {
  background: rgba(74,222,128,0.18);
  border-color: #4ade80;
  color: #fff;
}
.sidebar-phone[data-state="ringing"] .phone-name,
.sidebar-phone[data-state="ringing"] .phone-icon { color: rgba(255,255,255,0.9); }
.sidebar-phone[data-state="ringing"] .phone-status { color: #bbf7d0; }
.sidebar-phone[data-state="ringing"] .phone-dot { background: #4ade80; }
@media (prefers-reduced-motion: no-preference) {
  .sidebar-phone[data-state="oncall"] .phone-dot { animation: phoneDotPulse 1.4s ease-in-out infinite; }
  .sidebar-phone[data-state="ringing"] .phone-dot { animation: phoneRingPulse 1s ease-in-out infinite; }
}
@keyframes phoneDotPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@keyframes phoneRingPulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--noble-primary), 0 0 0 2px rgba(74,222,128,0.55); }
  50% { box-shadow: 0 0 0 2px var(--noble-primary), 0 0 0 6px rgba(74,222,128,0); }
}

/* The phone panel is a child of the rail so it follows its trigger in the
   tab order. It opens in the top layer (phone-dock.js), so the rail's own
   z-index no longer caps it and needs no lift while it is open. */

/* On the Calls page the row is a shortcut to the phone below it, not a
   disclosure — no caret, and the panel it would open is the page itself. */
.sidebar-phone[data-here="true"] .phone-status { margin-left: auto; text-align: right; }

.sidebar-footer {
  border-top: 1px solid rgba(255,255,255,0.15);
  padding: 12px 0;
}
.sidebar-user {
  padding: 4px 16px 8px;
  font-size: 12px;
  color: rgba(255,255,255,0.5);
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-footer a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  color: rgba(255,255,255,0.6);
  text-decoration: none;
  font-size: 14px;
}
.sidebar-footer a:hover { color: white; }

.admin-main {
  flex: 1;
  margin-left: 220px;
  padding: 28px 32px;
  /* No max-width: data tables (Conversations, Leads, Contacts, etc.) need
     the full viewport. Forms keep their own narrower max-width
     (e.g. .site-form). */
}

/* ── Collapsible sidebar (manual icon-rail toggle) ──
   Driven by `.sidebar-collapsed` on <html> (set pre-paint + on click in
   base.html). Mirrors the <=900px responsive treatment further down so the
   collapsed look is identical whether triggered by width or by the button. */
.admin-sidebar { transition: width 0.2s ease; }
.admin-main { transition: margin-left 0.2s ease; }

.sidebar-header { position: relative; }
.sidebar-header .sidebar-user { padding-right: 32px; }

.sidebar-collapse-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 26px;
  height: 26px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 6px;
  color: rgba(255,255,255,0.7);
  cursor: pointer;
  transition: color 0.15s, background 0.15s;
}
.sidebar-collapse-btn:hover { color: white; background: rgba(255,255,255,0.12); }
.sidebar-collapse-btn svg { transition: transform 0.2s ease; }

html.sidebar-collapsed .admin-sidebar { width: 60px; }
html.sidebar-collapsed .admin-main { margin-left: 60px; }
html.sidebar-collapsed .sidebar-nav a span,
html.sidebar-collapsed .sidebar-footer a span,
html.sidebar-collapsed .sidebar-user,
html.sidebar-collapsed .sidebar-agent-status,
html.sidebar-collapsed .sidebar-search-btn span,
html.sidebar-collapsed .sidebar-search-btn kbd { display: none; }
/* Badges and the notify-scope widget are toggled visible via inline style
   by JS, so overriding them at 60px needs !important. */
html.sidebar-collapsed .nav-badge,
html.sidebar-collapsed #notifScopeWidget { display: none !important; }
html.sidebar-collapsed .sidebar-search-btn {
  width: 36px;
  margin: 4px auto 0;
  padding: 7px;
  justify-content: center;
}
/* The phone is the one header control that survives the collapse: every other
   one is a preference an agent can go and find, and this one is how they learn
   a customer is on the line right now. Icon and presence dot only. */
html.sidebar-collapsed .sidebar-phone { padding: 8px 0 0; }
html.sidebar-collapsed .sidebar-phone-btn {
  width: 36px;
  margin: 0 auto;
  padding: 7px;
  justify-content: center;
}
html.sidebar-collapsed .sidebar-phone .phone-name,
html.sidebar-collapsed .sidebar-phone .phone-status,
html.sidebar-collapsed .sidebar-phone .phone-caret { display: none; }
/* Same treatment for the one-click mute below it, which is why it is not on
   the hide list with the other header widgets: at 60px its label used to wrap
   and spill out of the rail, but hiding the control outright would put "how do
   I shut the sound off" back behind the settings page it was lifted out of. */
html.sidebar-collapsed #notifSoundWidget { margin-top: 8px !important; }
html.sidebar-collapsed #notifSoundBtn {
  width: 36px !important;
  margin: 0 auto;
  padding: 7px !important;
  justify-content: center;
}
html.sidebar-collapsed #notifSoundLabel { display: none; }
html.sidebar-collapsed .sidebar-collapse-btn svg { transform: rotate(180deg); }
/* Collapsed: the header content is hidden, so float the chevron back into
   normal flow and center it — otherwise the absolutely-positioned button
   overflows the now-tiny header and overlaps the search button below. */
html.sidebar-collapsed .sidebar-header { padding: 12px 0; text-align: center; }
html.sidebar-collapsed .sidebar-collapse-btn { position: static; }

/* ── VA Hours (Phase 6b) ──
   The certification tracker at /admin/leads/va-hours. Hours/wk is the only
   column carrying color weight — the amber divergence dot — everything else
   stays quiet, same discipline as the Leads table's Due column. */
.va-refs { margin-bottom: 14px; }
.va-refs > summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--gray-500);
  padding: 6px 2px;
}
.va-refs[open] > summary { margin-bottom: 8px; }
.va-refs-table td { font-size: 12.5px; }
.va-source {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  background: var(--gray-100);
  color: var(--gray-500);
}
/* An adjudicated number is a recorded human decision — the strongest
   provenance on the page, so it is the one source that reads as such. */
.va-source.is-adjudicated { background: var(--noble-light); color: var(--noble-primary); }
.va-note-flag {
  font-size: 11px;
  color: var(--gray-400);
  border-bottom: 1px dotted var(--gray-300);
  cursor: help;
}
.va-filters {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
/* The search reuses .filter-search (the leads bar's own wrapper + icon), so the
   two bars cannot drift apart; only the selects need styling here — same
   treatment as .filter-range select. This bar shipped with BARE browser
   controls (reported 2026-08-21): the panel treatment is scoped per bar, and a
   new bar gets nothing until it says so. */
.va-filters .filter-search { max-width: 300px; }
.va-filters select {
  padding: 7px 10px;
  font-size: 13px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: white;
  font-family: inherit;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.va-filters select:focus-visible {
  outline: none;
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.va-muted { color: var(--gray-500); font-size: 12.5px; }
.va-weeks { color: var(--gray-400); font-size: 11.5px; margin-left: 4px; }
.va-formula { color: var(--gray-400); font-size: 11px; cursor: help; }
.va-empty { text-align: center; color: var(--gray-400); padding: 28px 0; }
.va-unlinked {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  background: var(--gray-100);
  color: var(--gray-500);
  cursor: help;
}
.va-hours-cell { white-space: nowrap; }
.va-tier {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  background: var(--gray-100);
  color: var(--gray-600);
}
/* The one loud thing: the certified figure differs from the formula. Usually
   a vacation period the raw dates cannot see — real on 83 of the sheet's 109
   vacation rows — so it means "look", never "wrong". */
/* ⚠️ The explicit display beats the UA's [hidden] rule — the .calls-toolbar
   gotcha, which this page recreated on day one: every row showed the dot
   because `hidden` had lost the specificity fight. Every styled-display
   element that is ever toggled with the hidden attribute needs this. */
.va-diverges[hidden] { display: none; }
.va-diverges {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-left: 5px;
  border-radius: 50%;
  background: #d97706;
  vertical-align: 1px;
  cursor: help;
}
.va-outcome {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 11.5px;
  font-weight: 500;
}
.va-outcome.is-graduated { background: #dcfce7; color: #166534; }
.va-outcome.is-withdrawn { background: #fef3c7; color: #92400e; }
.va-outcome.is-terminated { background: #fee2e2; color: #991b1b; }
.va-outcome.is-open { background: var(--gray-100); color: var(--gray-500); }

@media (prefers-reduced-motion: reduce) {
  .admin-sidebar,
  .admin-main,
  .sidebar-collapse-btn svg { transition: none; }
}

/* ── Flash messages ── */
.flash-message {
  background: #eff6ff;
  border: 1px solid #bfdbfe;
  color: var(--blue);
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 16px;
  font-size: 14px;
}
@media (prefers-reduced-motion: no-preference) {
  /* Drops in just after the page settles, so the result of an action announces
     itself rather than being already-there. */
  .flash-message { animation: flashIn var(--dur-3) var(--ease-spring) both; }
  @keyframes flashIn {
    from { opacity: 0; transform: translateY(-8px); }
  }
}

/* ── Page header ── */
.page-header { margin-bottom: 24px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.page-header h1 {
  font-size: 22px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.page-header code { font-size: 16px; color: var(--gray-600); }
.page-count { color: var(--gray-500); font-size: 14px; margin-left: 8px; }
.back-link {
  color: var(--gray-500);
  text-decoration: none;
  font-size: 13px;
  display: block;
  width: 100%;
  margin-bottom: 4px;
}
.back-link:hover { color: var(--noble-primary); }

.meta-row {
  display: flex;
  gap: 20px;
  color: var(--gray-500);
  font-size: 13px;
  margin-top: 6px;
  width: 100%;
}

/* ── Stat cards ── */
.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 28px;
}

.stat-card {
  background: white;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 20px;
  transition: box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.stat-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

.stat-card.stat-alert { border-left: 4px solid var(--yellow); }
.stat-value { font-size: 28px; font-weight: 700; color: var(--gray-900); }
.stat-label { font-size: 13px; color: var(--gray-500); margin-top: 2px; }
.stat-detail { font-size: 12px; color: var(--gray-400); margin-top: 4px; }
.stat-link {
  display: inline-block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--noble-primary);
  text-decoration: none;
  font-weight: 500;
}
.stat-link:hover { text-decoration: underline; }

.meta-link { color: var(--blue); text-decoration: none; font-size: 12px; }
.meta-link:hover { text-decoration: underline; }

/* ── Sections ── */
.section { margin-bottom: 28px; }
.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.section-header h2 { font-size: 17px; }
.section-desc { color: var(--gray-500); font-size: 13px; margin-bottom: 12px; }

/* ── Tables ── */
.data-table {
  width: 100%;
  background: white;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  border-collapse: collapse;
  font-size: 14px;
  /* So the first/last rows are clipped by the rounded corners rather than
     squaring them off. */
  overflow: hidden;
}

.data-table th {
  text-align: left;
  padding: 10px 14px;
  font-weight: 500;
  color: var(--gray-500);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-bottom: 1px solid var(--gray-200);
}

.data-table td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--gray-100);
}

.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr { transition: background-color var(--dur-1) var(--ease-out); }

/* Rows arrive in a short stagger on load, so a long list resolves top-to-bottom
   instead of slamming in. Capped at the first 14 rows: past the fold the delay
   would just be latency the reader never sees. */
@media (prefers-reduced-motion: no-preference) {
  .data-table tbody tr {
    animation: rowIn var(--dur-3) var(--ease-out) both;
  }
  .data-table tbody tr:nth-child(n + 15) { animation-delay: 0.14s; }
  .data-table tbody tr:nth-child(1) { animation-delay: 0.00s; }
  .data-table tbody tr:nth-child(2) { animation-delay: 0.01s; }
  .data-table tbody tr:nth-child(3) { animation-delay: 0.02s; }
  .data-table tbody tr:nth-child(4) { animation-delay: 0.03s; }
  .data-table tbody tr:nth-child(5) { animation-delay: 0.04s; }
  .data-table tbody tr:nth-child(6) { animation-delay: 0.05s; }
  .data-table tbody tr:nth-child(7) { animation-delay: 0.06s; }
  .data-table tbody tr:nth-child(8) { animation-delay: 0.07s; }
  .data-table tbody tr:nth-child(9) { animation-delay: 0.08s; }
  .data-table tbody tr:nth-child(10) { animation-delay: 0.09s; }
  .data-table tbody tr:nth-child(11) { animation-delay: 0.10s; }
  .data-table tbody tr:nth-child(12) { animation-delay: 0.11s; }
  .data-table tbody tr:nth-child(13) { animation-delay: 0.12s; }
  .data-table tbody tr:nth-child(14) { animation-delay: 0.13s; }
  @keyframes rowIn {
    from { opacity: 0; transform: translateY(4px); }
  }
}

.clickable-row { cursor: pointer; }
.clickable-row:hover { background: var(--gray-50); }

/* ── Conversations list ──
   Rows are two lines instead of eleven columns: the question the visitor asked
   reads as the title, and the fields that were mostly em-dashes in their own
   columns (email, current page, feedback) sit under it where they cost nothing
   when absent. */
/* Fixed layout so the conversation column is "everything the others don't need"
   and its title truncates at the real column width. With auto layout the column
   sizes to its longest title instead, which is why the old markup had to hard-
   code a max-width per cell. */
.conv-table { table-layout: fixed; }
.conv-table td { vertical-align: middle; padding-top: 9px; padding-bottom: 9px; }
.conv-table .cell-check { width: 40px; }
.conv-table .cell-mode { width: 118px; }
.conv-table .cell-status { width: 118px; }
.conv-table .cell-assignee { width: 168px; }
.conv-table .cell-activity { width: 136px; }
/* One line for the meta, so every row is exactly two lines tall and the list
   scans as a column. The site never shrinks (flex: none); the email and page
   ellipsize into whatever is left. */
.conv-table .conv-id-meta { flex-wrap: nowrap; overflow: hidden; }
/* Feedback counts inherit the meta line's scale rather than the 13px they use
   when they have a column to themselves. */
/* flex: none — it's two characters, so the page link should absorb the shrink
   rather than the feedback losing its arrow. */
.conv-id-meta .feedback-inline { display: inline-flex; flex: none; gap: 6px; font-size: 11.5px; }
.conv-table .cell-activity { white-space: nowrap; text-align: right; }
.conv-table .cell-activity .conv-count { display: block; font-size: 12.5px; color: var(--gray-700); font-weight: 500; }
.conv-table .cell-activity .conv-started { display: block; font-size: 11.5px; color: var(--gray-400); }

.conv-row { cursor: pointer; }
/* The rail is drawn with an inset shadow rather than a border so marking a row
   never shifts the column widths. */
.conv-row > td:first-child { transition: box-shadow var(--dur-2) var(--ease-out); }
.conv-row:hover { background: var(--noble-light); }
/* Only rows that want something get a rail: a visitor mid-conversation with a
   human, or a flag someone raised. A quiet list is the point. */
.conv-row.conv-attention > td:first-child { box-shadow: inset 3px 0 0 var(--noble-primary); }
.conv-row.conv-flagged > td:first-child { box-shadow: inset 3px 0 0 var(--yellow); }
/* Which conversation the preview panel is showing, so the list keeps its place
   while the panel switches between rows. */
.conv-row.conv-open { background: var(--noble-light); }
.conv-row.conv-open > td:first-child { box-shadow: inset 3px 0 0 var(--noble-accent); }
.conv-row.conv-open .conv-id-title { color: var(--noble-primary); }

.truncate { max-width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.truncate-cell { max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.empty-state {
  text-align: center;
  color: var(--gray-400);
  padding: 32px 14px !important;
}

/* ── Badges ── */
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  /* Two-word badges ("Super admin") were wrapping mid-pill in narrow columns. */
  white-space: nowrap;
}
.badge-active { background: #dcfce7; color: var(--green); }
.badge-flagged { background: #fef3c7; color: var(--yellow); }
.badge-reviewed { background: #dbeafe; color: var(--blue); }
.badge-archived { background: var(--gray-100); color: var(--gray-500); }
.badge-takeover { background: #ecfdf5; color: #059669; }
/* Unified-contacts source badges (chat / email / both). */
.badge-chat { background: #dbeafe; color: var(--blue); }
.badge-email { background: #ede9fe; color: #7c3aed; }
.badge-both { background: #dcfce7; color: var(--green); }
.badge-calls { background: #ecfdf3; color: #067647; }
/* Applied to a program: a fourth source of a contact's identity, so it gets its
   own badge rather than hiding behind the generic "Contact" one. */
.badge-applicant { background: #fff1f2; color: #9f1239; }

/* Contact-profile tags (email-keyed, reuse the inbox tag vocabulary). */
.contact-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.tag-pill {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 2px 4px 2px 8px; border-radius: 10px; font-size: 12px; font-weight: 600;
}
.tag-pill-x {
  border: 0; background: transparent; color: inherit; cursor: pointer;
  font-size: 14px; line-height: 1; padding: 0 2px; opacity: 0.6;
}
.tag-pill-x:hover { opacity: 1; }

/* Inline email threads on the unified contact profile. */
.email-thread {
  border: 1px solid var(--gray-200, #e5e7eb); border-radius: 8px;
  margin-bottom: 10px; border-left: 3px solid #7c3aed; background: #fff;
}
.email-thread-summary {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 8px 10px; cursor: pointer; font-weight: 600;
}
.email-thread-summary::-webkit-details-marker { color: #9ca3af; }
.email-thread-subject { color: var(--blue, #2563eb); }
.email-thread-body { padding: 4px 10px 10px; }
.email-msg {
  border-top: 1px solid var(--gray-100, #f3f4f6); padding: 8px 0;
}
.email-msg-meta { display: flex; gap: 8px; align-items: baseline; font-size: 12px; margin-bottom: 3px; }
.email-msg-text { font-size: 13px; white-space: pre-wrap; word-break: break-word; color: #374151; }
.email-msg-in { /* customer */ }
.email-msg-out .email-msg-text { color: #1f2937; }
.email-msg-out { background: #faf5ff; border-radius: 6px; padding-left: 8px; padding-right: 8px; }
/* Unsent shared draft waiting on an inbox thread (chip in the summary row +
   a snippet block in the body) — amber, "needs finishing", not an error. */
.email-draft-chip {
  display: inline-flex; align-items: center;
  padding: 1px 8px; border-radius: 10px; font-size: 11px; font-weight: 700;
  background: #fef3c7; color: #92400e; border: 1px solid #fde68a;
  white-space: nowrap;
}
.email-draft-note {
  border-top: 1px dashed #fde68a; background: #fffbeb;
  border-radius: 6px; padding: 8px; margin-top: 6px;
}
.email-draft-note .email-msg-text { color: #6b7280; font-style: italic; }
.badge-new {
  background: #fee2e2;
  color: #b91c1c;
  animation: badge-new-pulse 1.4s ease-in-out infinite;
}
@keyframes badge-new-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* Highlight freshly-inserted rows for a few seconds */
tr.row-new-highlight {
  animation: row-new-fade 6s ease-out forwards;
}
@keyframes row-new-fade {
  0%   { background-color: #fef3c7; }
  100% { background-color: transparent; }
}

/* ── Feedback inline ── */
.feedback-inline { display: flex; gap: 8px; font-size: 13px; }
.fb-up { color: var(--green); }
.fb-down { color: var(--red); }
.text-muted { color: var(--gray-400); }

/* ── Conversation identity ──
   The same block renders in three places: a row in the conversations list, the
   preview panel's header, and the live-chat header. Title is what the visitor
   asked; the line under it is who they are and where they're standing. Keeping
   it identical is what makes moving between the three read as zooming in on one
   conversation rather than opening three unrelated pages. */
.conv-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.conv-id-title {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--gray-900);
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conv-id-title:where(a) { color: var(--gray-900); text-decoration: none; }
.conv-row:hover .conv-id-title:where(a) { text-decoration: underline; text-decoration-color: var(--gray-300); text-underline-offset: 2px; }
.conv-id-title-empty { color: var(--gray-400); font-weight: 400; font-style: italic; }
/* Two lines in the panel header, where there's room to read the whole question. */
.conv-id-lg .conv-id-title {
  font-size: 15px;
  font-weight: 600;
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-clamp: 2;
}
.conv-id-meta {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 7px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--gray-500);
  min-width: 0;
}
.conv-id-meta > span, .conv-id-meta > a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv-id-meta a { color: var(--accent-ink); text-decoration: none; }
.conv-id-meta a:hover { text-decoration: underline; }
.conv-id-meta .conv-id-sep { color: var(--gray-300); flex: none; }
.conv-id-site { font-weight: 500; color: var(--gray-600); flex: none; }

/* ── Pills ──
   Mode used to be a green pill for "Human" and bare gray text for "AI", so the
   same axis was drawn two different ways and only one of its values was legible
   at a glance. One shape for both; color and the dot carry the difference. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  vertical-align: middle;
  /* Neutral default so a status with no rule of its own still reads as a pill
     rather than as bare text. */
  background: var(--gray-100);
  color: var(--gray-600);
  box-shadow: inset 0 0 0 1px var(--gray-200);
}
.pill-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }
.pill-human { background: var(--msg-team-bg); color: #047857; box-shadow: inset 0 0 0 1px var(--msg-team-line); }
.pill-ai { background: var(--gray-100); color: var(--gray-500); box-shadow: inset 0 0 0 1px var(--gray-200); }
/* Status pills reuse the badge palette but drop the shouty uppercase. */
.pill-active { background: #dcfce7; color: #15803d; box-shadow: inset 0 0 0 1px #bbf7d0; }
.pill-flagged { background: #fef3c7; color: #b45309; box-shadow: inset 0 0 0 1px #fde68a; }
.pill-reviewed { background: #dbeafe; color: #1d4ed8; box-shadow: inset 0 0 0 1px #bfdbfe; }
.pill-archived { background: var(--gray-100); color: var(--gray-500); box-shadow: inset 0 0 0 1px var(--gray-200); }
.pill-snoozed { background: #fef3c7; color: #78350f; box-shadow: inset 0 0 0 1px #fcd34d; }
.pill-returning { background: var(--noble-light); color: var(--noble-primary); box-shadow: inset 0 0 0 1px rgba(85,7,43,0.14); }

/* ── Assignee chip ── */
.who { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--gray-700); min-width: 0; }
.who-avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--noble-primary) 0%, var(--noble-accent) 100%);
  color: white;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.who-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who-none { color: var(--gray-300); font-size: 13px; }

/* Numbers that sit in a column should line up. */
.nums { font-variant-numeric: tabular-nums; }

/* ── Buttons ── */
/* `display: inline-block` beats the UA's [hidden] rule (the calls-toolbar
   lesson) — without the explicit rule, a .btn set hidden from JS stays
   visible. */
.btn[hidden] { display: none; }
.btn {
  display: inline-block;
  padding: 8px 14px;
  background: var(--noble-primary);
  color: white;
  border: none;
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: background-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-spring);
}
.btn:hover { background: var(--noble-dark); transform: translateY(-1px); box-shadow: var(--shadow); }
/* Press lands the button back down, which is what makes a click feel answered. */
.btn:active { transform: translateY(0) scale(0.985); box-shadow: none; transition-duration: var(--dur-1); }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn[disabled], .btn:disabled { opacity: 0.5; pointer-events: none; }
.btn-sm { padding: 5px 10px; font-size: 12px; }
.btn-block { display: block; width: 100%; margin-bottom: 6px; }
.btn-ghost { background: var(--gray-100); color: var(--gray-700); }
.btn-ghost:hover { background: var(--gray-200); }
.btn-warning { background: var(--yellow); color: white; }
.btn-warning:hover { background: #b45309; }
.btn-success { background: var(--green); color: white; }
.btn-success:hover { background: #15803d; }

/* ── Filters ── */
.filters { margin-bottom: 16px; }
.filter-form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.filter-form select, .filter-form input {
  padding: 7px 10px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-size: 13px;
  outline: none;
}
.filter-form > input { flex: 1; max-width: 300px; }
.filter-form select:focus, .filter-form input:focus { border-color: var(--noble-primary); }

/* ── Pagination ── */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 16px;
}
.page-info { color: var(--gray-500); font-size: 13px; }

/* ── Conversation detail ── */
.detail-layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 24px;
}

/* ── Chat-style thread (mirrors user-facing widget) ── */
.chat-thread {
  background: #f8f9fb;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 24px 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.chat-row {
  display: flex;
  gap: 10px;
  align-items: flex-end;
}
.chat-row + .chat-row { margin-top: 10px; }

.chat-row-user { flex-direction: row-reverse; }
.chat-row-bot { flex-direction: row; }

/* Avatars */
.chat-avatar {
  width: 30px;
  height: 30px;
  border-radius: 10px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
}
.chat-avatar-bot {
  background: linear-gradient(135deg, var(--noble-primary) 0%, var(--noble-accent) 100%);
  box-shadow: 0 2px 8px rgba(85, 7, 43, 0.25);
}
.chat-avatar-user {
  background: linear-gradient(135deg, #1a1f2e 0%, #2d3348 100%);
  box-shadow: 0 2px 8px rgba(39, 46, 61, 0.2);
}

/* Message content wrapper */
.chat-msg-content {
  max-width: 78%;
  min-width: 60px;
}

/* Bubbles */
.chat-bubble {
  padding: 12px 16px;
  border-radius: 14px;
  line-height: 1.65;
  font-size: 14px;
  word-wrap: break-word;
  overflow-wrap: break-word;
  white-space: pre-wrap;
}

.chat-bubble-bot {
  background: white;
  color: var(--gray-700);
  border-bottom-left-radius: 6px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03);
  border: 1px solid #e8ecf1;
}

.chat-bubble-human {
  background: #f0fdf4;
  border-color: #bbf7d0;
}

.chat-bubble-user {
  background: linear-gradient(135deg, var(--noble-primary) 0%, var(--noble-accent) 100%);
  color: white;
  border-bottom-right-radius: 6px;
  box-shadow: 0 4px 14px -3px rgba(85, 7, 43, 0.3);
}

/* Meta line under bubble */
.chat-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 4px 0;
  font-size: 12px;
  color: var(--gray-400);
}
.chat-meta-user { justify-content: flex-end; }

.chat-sender { font-weight: 500; color: var(--gray-500); font-size: 12px; }
.chat-time { font-size: 11px; color: var(--gray-400); }

.chat-label {
  padding: 2px 7px;
  border-radius: 6px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.chat-label-ai { background: var(--gray-100); color: var(--gray-500); }
.chat-label-human { background: #dcfce7; color: #15803d; }

.chat-fb { font-size: 14px; }
.chat-fb-up { color: var(--green); }
.chat-fb-down { color: var(--red); }

/* Internal notes */
.chat-note {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: #fef3c7;
  border-left: 3px solid #f59e0b;
  border-radius: var(--radius);
  padding: 10px 14px;
  margin: 8px 40px;
}
.chat-note-icon {
  flex-shrink: 0;
  color: #92400e;
  margin-top: 2px;
}
.chat-note-content { flex: 1; }
.chat-note-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: #92400e;
}
.chat-note-time {
  font-size: 11px;
  color: #b45309;
  margin-left: 8px;
}
.chat-note-body {
  font-size: 13px;
  line-height: 1.5;
  color: #78350f;
  font-style: italic;
  margin-top: 4px;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── Detail sidebar ── */
.sidebar-section {
  background: white;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  margin-bottom: 16px;
}
.sidebar-section h3 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--gray-500);
  margin-bottom: 10px;
}

.sidebar-section input[type="text"],
.sidebar-section select,
.sidebar-section textarea {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-size: 13px;
  font-family: inherit;
  outline: none;
  background: white;
  color: var(--gray-800);
  transition: border-color 0.15s;
}
.sidebar-section textarea { resize: vertical; }
.sidebar-section input[type="text"]:focus,
.sidebar-section select:focus,
.sidebar-section textarea:focus {
  border-color: var(--noble-primary);
  box-shadow: 0 0 0 3px rgba(85,7,43,0.06);
}
.sidebar-section small {
  display: block;
  font-size: 11px;
  color: var(--gray-400);
  margin-top: 4px;
}
.sidebar-section .btn-sm {
  margin-top: 8px;
}

.tag-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 10px;
}
.tag-chip {
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  padding: 3px 9px;
  border-radius: 12px;
  background: #dcfce7;
  color: var(--green);
}

.action-buttons { display: flex; flex-direction: column; gap: 4px; }

/* ── Site Edit Form ── */
.site-form { max-width: 800px; }

.form-section {
  background: white;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 24px;
  margin-bottom: 20px;
}
.form-section h3 {
  font-size: 16px;
  margin-bottom: 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--gray-200);
}

.form-row {
  display: flex;
  gap: 16px;
  margin-bottom: 0;
}

.form-group {
  flex: 1;
  margin-bottom: 16px;
}
.form-group label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-700);
  margin-bottom: 4px;
}
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-size: 14px;
  font-family: inherit;
  outline: none;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--noble-primary);
  box-shadow: 0 0 0 3px rgba(85,7,43,0.08);
}
.form-group small {
  display: block;
  font-size: 12px;
  color: var(--gray-400);
  margin-top: 4px;
}
.form-group textarea {
  resize: vertical;
  font-family: 'SF Mono', 'Menlo', 'Monaco', monospace;
  font-size: 13px;
  line-height: 1.5;
}

.color-input-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
}
.color-input-wrap input[type="color"] {
  width: 40px;
  height: 36px;
  padding: 2px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  cursor: pointer;
}
.color-text {
  width: 80px !important;
  font-family: monospace;
  font-size: 13px !important;
  text-align: center;
}

.color-preview {
  margin-top: 12px;
}
.preview-header {
  height: 40px;
  border-radius: var(--radius);
  background: linear-gradient(135deg, #55072b, #6b1d6e, #3c00bd);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 13px;
  font-weight: 500;
}

.form-actions {
  display: flex;
  gap: 12px;
  padding-top: 8px;
}

/* ── Takeover Chat ── */
.takeover-layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 24px;
}

.takeover-chat {
  background: white;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  /* --tk-chat-h is measured from the header's real height on load and resize
     (see takeover.html), because the context bar wraps to a second row on a
     narrow window and any fixed offset is wrong at some width. The calc is the
     fallback for the frame before the script runs. */
  height: var(--tk-chat-h, calc(100vh - 210px));
  min-height: 400px;
  overflow: hidden;
}

.takeover-messages {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 18px 20px;
  scrollbar-width: thin;
}

/* ── Live-chat context bar ──
   The site, the page they're on, their browser and timezone used to run
   together as two lines of same-size gray text. Labelled chips so a glance
   finds the one field you're after. */
.ctx-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px 7px;
  margin: 2px 0 14px;
  font-size: 12px;
  width: 100%;
}
.ctx-item {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  max-width: 340px;
  min-width: 0;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: white;
  box-shadow: inset 0 0 0 1px var(--gray-200);
}
.ctx-key {
  flex: none;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--gray-400);
}
.ctx-val {
  min-width: 0;
  color: var(--gray-700);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ctx-val a { color: var(--accent-ink); text-decoration: none; }
.ctx-val a:hover { text-decoration: underline; }
/* The two chips an agent actually reads first. */
.ctx-item-strong { background: var(--noble-light); box-shadow: inset 0 0 0 1px rgba(85,7,43,0.12); }
.ctx-item-strong .ctx-key { color: var(--noble-accent); }

/* ── Message bubbles (live chat + conversations preview panel) ──
   One grammar for both agent-facing transcripts: the visitor on the left, us on
   the right, green when a teammate wrote it and violet when the AI did. The
   third transcript — conversation_detail — deliberately keeps the customer on
   the right because it is a mirror of the customer's own widget, not an agent
   view, so it is not part of this. */
.tk-msg,
.summary-msg {
  position: relative;
  max-width: 80%;
  padding: 9px 13px;
  border-radius: 14px;
  border: 1px solid transparent;
}
.tk-msg { margin-bottom: 10px; }

/* Visitor — left, neutral. The flattened bottom corner points at its side. */
.tk-user,
.summary-msg.msg-user {
  margin-right: auto;
  background: var(--msg-visitor-bg);
  border-color: var(--msg-visitor-line);
  border-bottom-left-radius: 5px;
}
/* AI — right, violet, matching the "AI draft" affordance in the composer. */
.tk-assistant,
.summary-msg.msg-assistant {
  margin-left: auto;
  background: var(--msg-ai-bg);
  border-color: var(--msg-ai-line);
  border-bottom-right-radius: 5px;
}
/* A teammate — right, green. Same green as the HUMAN pill, so "a person has
   this" is one color everywhere in the panel. */
.tk-human,
.summary-msg.msg-human {
  margin-left: auto;
  background: var(--msg-team-bg);
  border-color: var(--msg-team-line);
  border-bottom-right-radius: 5px;
}

/* Sender line. Names are sentence case: uppercasing "Tess" made every message
   shout the one thing that repeats most. */
.tk-msg-label,
.summary-msg-who {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--gray-600);
  margin-bottom: 3px;
}
.tk-msg-time,
.summary-msg-time {
  font-weight: 400;
  font-size: 11px;
  color: var(--gray-400);
  font-variant-numeric: tabular-nums;
  margin-left: auto;
  flex: none;
}
/* An AI row that never finished: dashed, so it doesn't read as a delivered
   answer, with the reason underneath the few words that were generated. */
.summary-msg.msg-assistant.is-interrupted {
  border-style: dashed;
  border-color: var(--gray-300);
  background: var(--gray-50);
}
.summary-msg-status {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.45;
  font-style: italic;
  color: var(--gray-600);
}
.tk-msg-body,
.summary-msg-body {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--gray-800);
  overflow-wrap: break-word;
  word-break: break-word;
}
.tk-msg-body { white-space: pre-wrap; }

@media (prefers-reduced-motion: no-preference) {
  .tk-msg, .summary-msg { animation: msgIn var(--dur-3) var(--ease-out) both; }
  @keyframes msgIn {
    from { opacity: 0; transform: translateY(6px); }
  }
}

/* ── Live-chat composer ──
   Toolbar, textarea, and the send row read as one control: the border and focus
   ring live on the wrapper, so tabbing into the textarea lights up the whole
   composer instead of a lone box inside a box. */
.takeover-composer {
  border-top: 1px solid var(--gray-200);
  padding: 10px 14px 12px;
  background: linear-gradient(180deg, var(--gray-50) 0%, white 60%);
}
.takeover-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 7px;
  font-size: 11.5px;
  color: var(--gray-500);
}
.takeover-tip { margin-left: 2px; line-height: 1.4; }

.takeover-input {
  position: relative;
  background: white;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  padding: 8px 10px;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.takeover-input:focus-within {
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.takeover-input textarea {
  display: block;
  width: 100%;
  border: none;
  padding: 2px 0;
  background: transparent;
  font-size: 14px;
  line-height: 1.55;
  font-family: inherit;
  color: var(--gray-800);
  resize: vertical;
  outline: none;
  min-height: 76px;
}
.takeover-input textarea::placeholder { color: var(--gray-400); }
.takeover-send-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.takeover-send-row .btn-send { margin-left: auto; padding: 7px 18px; }

/* Composer toolbar buttons: quiet chips, not full buttons — they assist the
   reply, they aren't the action. */
.tk-tool-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-pill);
  background: white;
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--gray-600);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out),
              background-color var(--dur-1) var(--ease-out), transform var(--dur-2) var(--ease-spring);
}
.tk-tool-btn:hover { border-color: var(--gray-400); color: var(--gray-900); }
.tk-tool-btn:active { transform: scale(0.97); }
.tk-tool-btn:focus-visible { outline: none; box-shadow: var(--ring); }
.tk-tool-btn:disabled { opacity: 0.55; pointer-events: none; }
.tk-tool-ai { color: #7c3aed; border-color: var(--msg-ai-line); }
.tk-tool-ai:hover { color: #6d28d9; border-color: #c4b5fd; background: var(--msg-ai-bg); }

.tk-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--gray-400);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.tk-icon-btn:hover { background: var(--gray-100); color: var(--gray-700); }
.tk-icon-btn:focus-visible { outline: none; box-shadow: var(--ring); }

.tk-draft-badge {
  font-size: 11px;
  color: #6d28d9;
  background: var(--msg-ai-bg);
  border: 1px solid var(--msg-ai-line);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
}

.tk-attach-preview { flex-wrap: wrap; gap: 6px; padding: 2px 0 8px; }

.tk-snippet-panel {
  position: absolute;
  bottom: 100%;
  left: -1px;
  right: -1px;
  margin-bottom: 6px;
  background: white;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  max-height: 260px;
  overflow-y: auto;
  z-index: 100;
}

/* Another agent is mid-reply — a warning, not a message, so it sits outside the
   transcript. */
.tk-typing {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0 14px 8px;
  padding: 6px 12px;
  font-size: 12.5px;
  color: #92400e;
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: var(--radius-pill);
}
.tk-typing-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #d97706;
  flex: none;
}
@media (prefers-reduced-motion: no-preference) {
  .tk-typing-dot { animation: tk-typing-pulse 1s infinite alternate; }
}
@keyframes tk-typing-pulse { from { opacity: 0.35; } to { opacity: 1; } }
/* Used by the attachment-upload spinner, which is built in JS. */
@keyframes tk-spin { to { transform: rotate(360deg); } }

/* Internal notes belong to neither side of the conversation, so they span the
   full width instead of picking a side. */
.tk-msg.tk-internal {
  max-width: none;
  margin: 4px 0 12px;
  background: #fffbeb;
  border-color: #fde68a;
  border-left: 3px solid var(--yellow);
  border-radius: var(--radius);
}
.tk-msg.tk-internal .tk-msg-label { color: #92400e; }
.tk-msg.tk-internal .tk-msg-time { color: #b45309; }
.tk-msg.tk-internal .tk-msg-body { color: #78350f; font-style: italic; }

/* Live-chat page header. */
.takeover-id { flex: 1 1 300px; min-width: 0; }
.takeover-id .conv-id-title { font-size: 19px; font-weight: 600; }
.takeover-head-pills { flex: none; display: flex; align-items: center; gap: 6px; }

.takeover-sidebar {
  align-self: start;
}

/* ── Internal notes styling ── */
.thread-internal {
  background: #fef3c7 !important;
  border-left: 3px solid #f59e0b !important;
}
.thread-internal .thread-msg-body {
  font-style: italic;
  color: #92400e;
}

/* ── Nav badge ── */
.nav-badge {
  background: #ef4444;
  color: white;
  font-size: 11px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 10px;
  margin-left: 6px;
  min-width: 18px;
  text-align: center;
}

/* ── Toggle switch ── */
.toggle-switch {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 24px;
  flex-shrink: 0;
}
.toggle-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}
.toggle-slider {
  position: absolute;
  cursor: pointer;
  top: 0; left: 0; right: 0; bottom: 0;
  background: var(--gray-300);
  border-radius: 24px;
  transition: background 0.2s;
}
.toggle-slider::before {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  left: 3px;
  bottom: 3px;
  background: white;
  border-radius: 50%;
  transition: transform 0.2s;
}
.toggle-switch input:checked + .toggle-slider {
  background: var(--primary, #6366f1);
}
.toggle-switch input:checked + .toggle-slider::before {
  transform: translateX(20px);
}

/* ── Snoozed badge ── */
.badge-snoozed {
  background: #fbbf24;
  color: #78350f;
}

/* ── Widget Preview (conversation detail) ── */
.widget-preview {
  border-radius: 12px;
  overflow: hidden;
}
/* Scope the widget styles so admin CSS doesn't bleed in */
.widget-preview .chat-header h1 {
  font-size: 15px;
  font-weight: 600;
}
.widget-preview .chat-messages {
  padding: 24px;
}

/* ── Add Note Bar ── */
.add-note-bar {
  margin-top: 12px;
  background: white;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 12px 16px;
}
.add-note-form {
  display: flex;
  gap: 8px;
  align-items: center;
}
.add-note-input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-size: 13px;
  font-family: inherit;
  outline: none;
}
.add-note-input:focus {
  border-color: var(--noble-primary);
  box-shadow: 0 0 0 3px rgba(85,7,43,0.08);
}

/* ── Multiselect Dropdown ── */
.multiselect-wrap {
  position: relative;
}
.multiselect-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-size: 13px;
  background: white;
  cursor: pointer;
  color: var(--gray-700);
  white-space: nowrap;
  font-family: inherit;
}
.multiselect-btn:hover { border-color: var(--gray-400); }
.multiselect-btn:focus { border-color: var(--noble-primary); outline: none; }

.multiselect-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 240px;
  max-height: 320px;
  overflow-y: auto;
  background: white;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  z-index: 100;
  padding: 6px 0;
}
.multiselect-dropdown.open { display: block; }

.multiselect-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  cursor: pointer;
  font-size: 13px;
  color: var(--gray-700);
}
.multiselect-option:hover { background: var(--gray-50); }
.multiselect-option input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--noble-primary);
}

.multiselect-actions {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  padding: 8px 14px 4px;
  border-top: 1px solid var(--gray-100);
  margin-top: 4px;
}

/* ── Active Filter Chips ── */
.active-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 16px;
}
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  background: #eff6ff;
  color: var(--blue);
  border-radius: 20px;
  font-size: 12px;
  font-weight: 500;
}
.filter-chip a {
  color: var(--blue);
  text-decoration: none;
  font-weight: 700;
  font-size: 14px;
  line-height: 1;
}
.filter-chip a:hover { color: var(--red); }

/* ── Live Conversations Dashboard ── */
.live-pulse-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--green);
  background: #f0fdf4;
  border: 1px solid #bbf7d0;
  padding: 3px 10px;
  border-radius: 20px;
  margin-left: 12px;
}
.live-dot {
  width: 8px;
  height: 8px;
  background: var(--green);
  border-radius: 50%;
  animation: livePulse 2s ease-in-out infinite;
}
@keyframes livePulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.5; transform: scale(0.8); }
}

.live-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 16px;
}

.live-card {
  display: block;
  background: white;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  text-decoration: none;
  color: inherit;
  border-left: 4px solid var(--gray-200);
  transition: all 0.15s;
}
.live-card:hover {
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  transform: translateY(-1px);
}
.live-card-human { border-left-color: #059669; }
.live-card-flagged { border-left-color: var(--yellow); }

.live-card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.live-card-site {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: var(--noble-primary);
}
.live-card-time {
  font-size: 11px;
  color: var(--gray-400);
}

.live-card-preview {
  font-size: 14px;
  color: var(--gray-700);
  line-height: 1.5;
  margin-bottom: 10px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.live-card-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.live-card-stats {
  display: flex;
  gap: 12px;
  font-size: 12px;
  color: var(--gray-400);
}
.live-card-stats span {
  display: flex;
  align-items: center;
  gap: 4px;
}
.live-card-badges {
  display: flex;
  gap: 4px;
}

.badge-ai-mode {
  background: var(--gray-100);
  color: var(--gray-500);
}

.live-card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--gray-100);
}
.live-tag {
  font-size: 10px;
  font-weight: 500;
  padding: 2px 7px;
  border-radius: 10px;
  background: #f3f4f6;
  color: var(--gray-600);
}

.empty-state-card {
  text-align: center;
  padding: 60px 20px;
  color: var(--gray-400);
}
.empty-state-card svg { margin-bottom: 16px; }
.empty-state-card h3 {
  font-size: 18px;
  color: var(--gray-600);
  margin-bottom: 8px;
}
.empty-state-card p {
  font-size: 14px;
  max-width: 400px;
  margin: 0 auto;
}

/* ── Responsive ── */
@media (max-width: 900px) {
  /* The collapsed sidebar visuals (60px width, hidden labels, 60px main
     margin) are driven by the .sidebar-collapsed class, which JS applies
     automatically at this breakpoint — so only the responsive padding lives
     here now. */
  .admin-main { padding: 20px 16px; }
  .detail-layout, .takeover-layout { grid-template-columns: 1fr; }
  .stat-cards { grid-template-columns: repeat(2, 1fr); }
  .form-row { flex-direction: column; gap: 0; }
  .live-grid { grid-template-columns: 1fr; }
  .chat-note { margin: 8px 10px; }
  .bulk-bar { position: fixed; bottom: 12px; left: 72px; right: 12px; }
}

/* ── Collision Detection: Viewer Warning ── */

.viewer-warning {
  display: flex;
  align-items: center;
  gap: 8px;
  background: #fef3c7;
  border: 1px solid #fcd34d;
  color: #92400e;
  padding: 8px 14px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  margin-bottom: 16px;
}
.viewer-warning svg {
  flex-shrink: 0;
  color: #d97706;
}

/* ── Bulk Actions ── */

.bulk-bar {
  position: sticky;
  bottom: 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  background: white;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  padding: 10px 16px;
  margin-top: 12px;
  z-index: 100;
}

.bulk-bar > span {
  font-size: 13px;
  font-weight: 600;
  color: #334155;
  margin-right: 4px;
  white-space: nowrap;
}

.bulk-select {
  padding: 5px 10px;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  font-size: 12px;
  outline: none;
  background: white;
  cursor: pointer;
}
.bulk-select:focus {
  border-color: var(--noble-primary, #55072b);
}

.btn-warning {
  background: #fef2f2;
  color: #dc2626;
  border: 1px solid #fecaca;
}
.btn-warning:hover {
  background: #fee2e2;
}

/* Checkbox styling in data tables */
.data-table input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
  accent-color: var(--noble-primary, #55072b);
}

/* Clickable session links (replacing row onclick) */
.data-table a code {
  color: var(--noble-primary, #55072b);
  text-decoration: none;
}
.data-table a:hover code {
  text-decoration: underline;
}

/* Team inbox: assigned/unassigned badges */
.badge-assigned {
  background: #dbeafe;
  color: #1e40af;
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
}
.badge-unassigned {
  background: #fef3c7;
  color: #92400e;
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
}

/* SLA response time badges */
.badge-sla-good {
  background: #dcfce7;
  color: #166534;
  border: 1px solid #bbf7d0;
}
.badge-sla-ok {
  background: #fef9c3;
  color: #854d0e;
  border: 1px solid #fde68a;
}
.badge-sla-slow {
  background: #fee2e2;
  color: #991b1b;
  border: 1px solid #fecaca;
}

/* ──────────────────────────────────────────────────────────────────
   Compact Admin — density overrides
   Applies to ALL admin pages. Tightens sidebar groups, form sections,
   page headers, tables, and conversation-detail specific components.
   ────────────────────────────────────────────────────────────────── */

/* Page headers: shrink title + margins */
.page-header { margin-bottom: 14px; gap: 6px; }
.page-header h1 { font-size: 18px; }
.page-header code { font-size: 14px; }
.meta-row { gap: 12px; font-size: 12px; color: var(--gray-500); }

/* Shared card sections (used on lots of admin pages) */
.form-section { padding: 16px; margin-bottom: 12px; }
.form-section h3 { font-size: 13px; margin-bottom: 10px; }

/* Legacy sidebar-section (still used on contact_detail, lead_detail) */
.sidebar-section { padding: 12px 14px; margin-bottom: 10px; }
.sidebar-section h3 { font-size: 11px; margin-bottom: 6px; }
.sidebar-section input[type="text"],
.sidebar-section select,
.sidebar-section textarea { padding: 6px 8px; font-size: 12px; }
.sidebar-section .btn-sm { margin-top: 6px; }

/* ── New compact sidebar used on conversation_detail ──
   One continuous card, thin dividers between groups, tiny labels. */
.detail-sidebar-compact {
  background: white;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0;
  overflow: hidden;
}
.detail-sidebar-compact .sidebar-group {
  padding: 10px 12px;
  border-bottom: 1px solid var(--gray-100);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.detail-sidebar-compact .sidebar-group:last-child { border-bottom: none; }
.detail-sidebar-compact .sidebar-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--gray-400);
  font-weight: 600;
}
.detail-sidebar-compact input[type="text"],
.detail-sidebar-compact select,
.detail-sidebar-compact textarea {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--gray-200);
  border-radius: 6px;
  font-size: 12px;
  font-family: inherit;
  outline: none;
  background: white;
  color: var(--gray-800);
}
.detail-sidebar-compact textarea { resize: vertical; }
.detail-sidebar-compact input[type="text"]:focus,
.detail-sidebar-compact select:focus,
.detail-sidebar-compact textarea:focus {
  border-color: var(--noble-primary);
  box-shadow: 0 0 0 2px rgba(85,7,43,0.08);
}
.detail-sidebar-compact form { margin: 0; display: flex; flex-direction: column; gap: 4px; }
/* Flex-wrap rather than a fixed 2-column grid: the number of status buttons
   changes with the conversation's current status, and a grid left a visible hole
   whenever that count was odd. Buttons grow to fill whatever row they land in. */
.detail-sidebar-compact .action-buttons {
  display: flex;
  /* Explicit: the enclosing `.detail-sidebar-compact form` rule sets column, and
     inheriting it here stretched each button to fill the sidebar vertically. */
  flex-direction: row;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 2px;
}
.detail-sidebar-compact .action-buttons .btn { margin: 0; flex: 1 1 68px; }
/* A quieter second-level label inside a sidebar group. */
.sidebar-sublabel {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--gray-400);
  margin: 8px 0 4px;
}
.detail-sidebar-compact .snooze-grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 3px; }
.detail-sidebar-compact .snooze-grid .btn { padding: 4px 2px; font-size: 11px; }
/* flex-direction is explicit for the same reason as .action-buttons above: the
   enclosing form rule sets column, which stacked the note input and its Add
   button into two full-width rows even though `flex: 1` on the input only makes
   sense in a row. */
.detail-sidebar-compact .inline-form { display: flex; flex-direction: row; gap: 4px; align-items: stretch; }
.detail-sidebar-compact .inline-form input[type="text"] { flex: 1; }
.detail-sidebar-compact .inline-form .btn { margin: 0; white-space: nowrap; }
.detail-sidebar-compact .sidebar-links { display: flex; flex-direction: column; gap: 4px; }
.detail-sidebar-compact .sidebar-links .btn { margin: 0; }
.detail-sidebar-compact .snooze-status {
  font-size: 11px;
  color: #d97706;
  background: #fffbeb;
  border: 1px solid #fed7aa;
  border-radius: 4px;
  padding: 4px 6px;
}
.detail-sidebar-compact .tag-chips { margin-bottom: 2px; }
.detail-sidebar-compact .tag-chip { font-size: 10px; padding: 2px 7px; }
.detail-sidebar-compact .btn { font-size: 12px; padding: 6px 10px; }
.detail-sidebar-compact .btn-sm { font-size: 11px; padding: 5px 8px; }

/* Tables: slightly denser rows across all list pages */
.admin-table th, .admin-table td { padding: 8px 12px; }
.admin-table th { font-size: 11px; }
.admin-table td { font-size: 13px; }

/* Detail-layout gap (conversation_detail + contact_detail) */
.detail-layout { gap: 14px; }

/* Generic .card (canned_responses, site_schedule etc) */
.card {
  background: white;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px;
}

/* Dashboard stat tiles — if any page uses .stat-card */
.stat-card { padding: 14px 16px !important; }
.stat-card h3 { font-size: 11px !important; margin-bottom: 4px !important; }
.stat-card .stat-value { font-size: 22px !important; }

/* how_it_works guide sections — they use heavy inline paddings */
.guide-toc { padding: 14px 18px !important; margin-bottom: 16px !important; }
.guide-toc h2 { font-size: 15px !important; margin-bottom: 8px !important; }
.guide-callout { padding: 8px 12px !important; margin: 10px 0 !important; font-size: 12px !important; }


/* ── Sidebar Search Button ── */
.sidebar-search-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: calc(100% - 24px);
  margin: 4px 12px 0;
  padding: 7px 10px;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 8px;
  color: rgba(255,255,255,0.55);
  font-size: 13px;
  cursor: pointer;
  transition: all 0.15s;
}
.sidebar-search-btn:hover {
  background: rgba(255,255,255,0.14);
  color: rgba(255,255,255,0.85);
  border-color: rgba(255,255,255,0.25);
}
.sidebar-search-btn kbd {
  margin-left: auto;
  font-size: 10px;
  background: rgba(255,255,255,0.1);
  padding: 1px 5px;
  border-radius: 4px;
  font-family: inherit;
  line-height: 1.5;
}
@media (max-width: 900px) {
  .sidebar-search-btn span,
  .sidebar-search-btn kbd { display: none; }
  .sidebar-search-btn { width: 36px; margin: 4px auto 0; padding: 7px; justify-content: center; }
}

/* ── Spotlight Search Overlay ── */
/* Kept mounted and faded rather than display-toggled, so the scrim and the panel
   can animate on the way in AND on the way out. `visibility` must flip to
   visible INSTANTLY on open — while it's still hidden the search input refuses
   focus(), forcing a click before typing — so it's only delayed on the way out
   (held hidden-flip until the opacity fade finishes). */
.spotlight-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(17,24,39,0.42);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-2) var(--ease-out),
              visibility 0s linear var(--dur-2);
}
.spotlight-overlay.active {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-2) var(--ease-out),
              visibility 0s;
}

.spotlight-modal {
  width: 560px;
  max-width: calc(100vw - 32px);
  background: white;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg), 0 0 0 1px rgba(17,24,39,0.05);
  overflow: hidden;
  transform: scale(0.97) translateY(-10px);
  transition: transform var(--dur-3) var(--ease-spring);
}
.spotlight-overlay.active .spotlight-modal { transform: scale(1) translateY(0); }

.spotlight-input-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid #e5e7eb;
}
.spotlight-input-wrap input {
  flex: 1;
  border: none;
  outline: none;
  font-size: 16px;
  background: transparent;
  color: #1f2937;
  font-family: inherit;
}
.spotlight-input-wrap input::placeholder { color: #9ca3af; }
.spotlight-esc {
  font-size: 10px;
  background: #f3f4f6;
  border: 1px solid #e5e7eb;
  padding: 2px 6px;
  border-radius: 4px;
  color: #6b7280;
  font-family: inherit;
  line-height: 1.4;
}

.spotlight-results {
  max-height: 380px;
  overflow-y: auto;
  padding: 6px 0;
}

.spotlight-item {
  display: block;
  padding: 10px 16px;
  text-decoration: none;
  color: #1f2937;
  cursor: pointer;
  transition: background 0.08s;
}
.spotlight-item:hover,
.spotlight-item.active {
  background: #f0f4ff;
}
.spotlight-item-label {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 2px;
}
.spotlight-item-desc {
  font-size: 12px;
  color: #6b7280;
}
.spotlight-item mark {
  background: #fef08a;
  color: inherit;
  border-radius: 2px;
  padding: 0 1px;
}
.spotlight-empty {
  padding: 24px 16px;
  text-align: center;
  color: #9ca3af;
  font-size: 14px;
}

/* ============ Course cards (admin takeover view) ============
   Mirrors the customer widget's .course-card styles so cards
   render properly when admins view live conversations. */
.tk-msg-body .course-card,
.tk-msg-body a.course-card {
  display: block;
  margin: 10px 0;
  padding: 14px 16px;
  border-radius: 12px;
  border: 1px solid #e2e8f0;
  background: linear-gradient(135deg, #fafbff 0%, #f5f7ff 100%);
  text-decoration: none !important;
  color: #1e293b;
  transition: all 0.2s ease;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
  cursor: pointer;
}
.tk-msg-body a.course-card:hover {
  border-color: #6366f1;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  transform: translateY(-1px);
}
.tk-msg-body .course-card .cc-header {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}
.tk-msg-body .course-card .cc-title-wrap { flex: 1; min-width: 0; }
.tk-msg-body .course-card .cc-name {
  font-weight: 700;
  font-size: 14px;
  color: #1e293b;
  line-height: 1.3;
  text-decoration: none;
}
.tk-msg-body .course-card .cc-desc {
  font-size: 12.5px;
  color: #64748b;
  margin-top: 2px;
  line-height: 1.4;
  text-decoration: none;
}
.tk-msg-body .course-card .cc-meta {
  display: flex;
  gap: 8px 14px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid #e2e8f0;
  flex-wrap: wrap;
  align-items: center;
}
.tk-msg-body .course-card .cc-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: #475569;
  font-weight: 500;
  text-decoration: none;
}
.tk-msg-body .course-card .cc-meta-item svg {
  color: #6366f1;
  opacity: 0.7;
  flex-shrink: 0;
}
.tk-msg-body .course-card .cc-price {
  font-weight: 700;
  color: #6366f1;
}
.tk-msg-body .course-card .cc-prop-item svg { color: #22c55e; opacity: 1; }
.tk-msg-body .course-card .cc-cta {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 10px;
  font-size: 12.5px;
  font-weight: 600;
  color: #6366f1;
  text-decoration: none;
}

/* ── Sheet dialog ──
   Native <dialog> so focus trapping, Escape and the top layer come for free.
   Entry/exit are transitioned with @starting-style + allow-discrete, which is
   what lets a display:none element animate in both directions. */
dialog.sheet {
  width: min(560px, calc(100vw - 32px));
  max-height: min(86vh, 780px);
  /* Explicit, so centering doesn't rely on the UA sheet's margin rule surviving
     the display switch below. */
  margin: auto;
  padding: 0;
  border: none;
  border-radius: var(--radius-lg);
  background: white;
  color: var(--gray-800);
  box-shadow: var(--shadow-lg), 0 0 0 1px rgba(17,24,39,0.05);
  overflow: hidden;

  opacity: 0;
  transform: translateY(10px) scale(0.98);
  transition-property: opacity, transform, display, overlay;
  transition-duration: var(--dur-3);
  transition-timing-function: var(--ease-spring);
  /* Written after the longhands on purpose: a shorthand here would drop it. */
  transition-behavior: allow-discrete;
}
dialog.sheet[open] {
  /* Column flex only while open, so the UA's `display: none` still applies when
     closed. This is what lets the body scroll while the header and footer stay
     pinned; without it the footer overlaps long content. */
  display: flex;
  flex-direction: column;
  opacity: 1;
  transform: translateY(0) scale(1);

  @starting-style {
    opacity: 0;
    transform: translateY(10px) scale(0.98);
  }
}
dialog.sheet::backdrop {
  background: rgba(17,24,39,0);
  backdrop-filter: blur(0px);
  transition:
    background-color var(--dur-3) var(--ease-out),
    backdrop-filter var(--dur-3) var(--ease-out),
    display var(--dur-3) allow-discrete,
    overlay var(--dur-3) allow-discrete;
}
dialog.sheet[open]::backdrop {
  background: rgba(17,24,39,0.42);
  backdrop-filter: blur(6px);

  @starting-style {
    background: rgba(17,24,39,0);
    backdrop-filter: blur(0px);
  }
}

/* min-height:0 at every level of the column — without it a flex item refuses to
   shrink below its content and the scroll container never scrolls. */
dialog.sheet form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.sheet-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--gray-100);
}
.sheet-head h3 { font-size: 16px; font-weight: 600; }
.sheet-close {
  margin-left: auto;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--gray-400);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.sheet-close:hover { background: var(--gray-100); color: var(--gray-700); }

.sheet-body {
  flex: 1 1 auto;
  min-height: 0;
  /* Extra bottom padding so the last control isn't flush against the footer
     rule when the body is scrolled all the way down. */
  padding: 18px 20px 24px;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.sheet-foot {
  flex: none;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 20px 16px;
  border-top: 1px solid var(--gray-100);
  background: var(--gray-50);
}

/* ── Form fields (shared by the sheet) ── */
.field { display: block; border: none; padding: 0; }
/* The explicit display beats the UA's [hidden] rule — the same trap as
   .calls-toolbar and .va-diverges, hit a third time by the VA Hours add
   dialog's conditional "new programme" field, which rendered despite its
   hidden attribute. Any component that sets display and is ever toggled with
   the hidden attribute needs this line. */
.field[hidden] { display: none; }
.field-row { display: flex; gap: 12px; flex-wrap: wrap; }
.field-row > .field { flex: 1 1 200px; }
.field-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--gray-700);
  margin-bottom: 6px;
  padding: 0;
}
.field-hint { font-size: 12px; color: var(--gray-500); margin-top: 6px; }
.field input[type="email"],
.field input[type="text"],
.field input[type="number"],
.field input[type="date"],
.field input[type="search"],
.field textarea,
.field select {
  width: 100%;
  padding: 8px 11px;
  font-size: 14px;
  font-family: inherit;
  color: var(--gray-800);
  background: white;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.field select[multiple] { padding: 4px; }

/* ── Segmented control ──
   A two-up radio group that reads as one control. The knob is the checked
   label's own background, so there is no extra element to keep in sync. */
.segmented {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  background: var(--gray-100);
  border-radius: var(--radius-pill);
}
.segmented label {
  position: relative;
  cursor: pointer;
}
.segmented input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.segmented span {
  display: block;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-600);
  white-space: nowrap;
  transition: background-color var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.segmented label:has(input:checked) span {
  background: white;
  color: var(--gray-900);
  box-shadow: var(--shadow-sm);
}
.segmented label:has(input:focus-visible) span { box-shadow: var(--ring); }
.segmented label:has(input:disabled) { cursor: not-allowed; }
.segmented label:has(input:disabled) span { opacity: 0.45; }

/* ── App chips (workspace access picker) ── */
.access-panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 12px;
  padding: 14px;
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
}
.access-panel[hidden] { display: none; }
.access-presets { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.access-presets .field-hint { margin-top: 0; }

.app-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.app-chip { position: relative; cursor: pointer; }
.app-chip input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.app-chip span {
  display: block;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--gray-300);
  background: white;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--gray-600);
  transition: background-color var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out),
              transform var(--dur-2) var(--ease-spring);
}
.app-chip:hover span { border-color: var(--gray-400); color: var(--gray-800); }
.app-chip:has(input:checked) span {
  background: var(--noble-primary);
  border-color: var(--noble-primary);
  color: white;
}
.app-chip:active span { transform: scale(0.96); }
.app-chip:has(input:focus-visible) span { box-shadow: var(--ring); }

/* Read-only access summary in the admins table. */
.access-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.access-chip {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--noble-light);
  border: 1px solid rgba(85,7,43,0.12);
  color: var(--noble-primary);
  font-size: 11.5px;
  font-weight: 500;
}
/* A privilege is a grant, not an app — amber so a row carrying one reads
   differently from a row that simply has a short app list. */
.access-chip.is-privilege {
  background: #fef3c7;
  border-color: rgba(180,83,9,0.22);
  color: #92400e;
}

/* ── Leads ──
   The page's job is "who do I owe a follow-up, and when", so the follow-up
   state is the only thing here carrying color weight. Everything else stays
   quiet: identity stacks name over email, classification stacks lead type over
   subject area, and the rest is neutral text. Previously every one of the 14
   columns competed at the same volume, which is why a table of real work read
   as a wall of empty date stubs. */

/* Pushes the page's primary action to the right edge of the header row. */
.page-header .push-right { margin-left: auto; }

/* ── Lead sending settings ── */
.sender-table { min-width: 720px; }
.sender-site { vertical-align: top; }
.sender-count { display: block; font-size: 11.5px; color: var(--gray-400); margin-top: 2px; }
.sender-origin { white-space: nowrap; }
.sender-why { display: block; font-size: 11.5px; color: var(--gray-400); margin-top: 2px; }
.sender-none { font-size: 12.5px; color: var(--gray-500); }

/* ── Lead detail drawer ──
   The record for one lead: everything the row cannot show, plus the history
   that turns a row of current state into an account of how it got there.
   Wider than the default sheet because it carries a two-column fact list. */
/* Wider than the base sheet: the follow-up composer's thread picker names the
   thread by the subject the customer sees, and at 680px a real subject line
   forced the whole drawer to scroll sideways (Joe, 2026-08-24: "make this box
   a little wider so you don't have to scroll to the right to see the end").
   The width buys room for the common case; the min-width:0 on the picker rows
   below is what guarantees the drawer never scrolls sideways again. */
.lead-drawer { width: min(800px, calc(100vw - 32px)); }
.drawer-loading, .drawer-empty {
  color: var(--gray-400);
  font-size: 13px;
  margin: 0;
}

.drawer-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.drawer-chip {
  display: inline-block;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 11.5px;
  font-weight: 500;
}
/* Status in the drawer is the table's own control, not a chip: it carries
   .status-select and so inherits the whole colour scale already defined for
   it further down. Only the size differs — it has to sit in a row of 11.5px
   pills without towering over them. */
.drawer-chips .status-select {
  padding-top: 1px;
  padding-bottom: 1px;
  font-size: 11.5px;
  max-width: 11rem;
}

/* Category is a control too now, but it is not a lifecycle: Individual /
   Corporate / VA are populations, not stages, so it deliberately keeps the
   neutral .drawer-chip palette instead of borrowing the status colour scale.
   It should read as the chip it replaced, one you can open. */
.drawer-chips .category-select {
  padding-top: 1px;
  padding-bottom: 1px;
  font-size: 11.5px;
  font-weight: 500;
  max-width: 11rem;
  border-radius: var(--radius-pill);
  border-color: transparent;
  background-color: var(--gray-100);
  color: var(--gray-600);
}
.drawer-chips .category-select:hover { border-color: var(--gray-400); }

/* The due date in the chip row is the table's own cell, so it inherits the
   whole [data-urgency] colour scale defined for .due-cell further down. Only
   the layout differs: the row stacks the "2 days overdue" label UNDER the
   input, which in a single-line chip row has to sit beside it instead. */
.due-cell.is-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* The two follow-up dates, kept together on their own line of the chip row.
   flex-basis:100% rather than a separate block so they stay inside
   .drawer-chips and inherit its gap — and so the pair can never be split
   across a wrap, which is what the due date was doing on its own. */
.drawer-dates {
  flex: 0 0 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
}

.followup-cell.is-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.followup-cell.is-chip .inline-date {
  padding-top: 1px;
  padding-bottom: 1px;
  font-size: 11.5px;
  min-width: 0;
}

/* Says what the date is. Same caption treatment as the panel's own field
   labels (.drawer-field dt) so it reads as a label rather than as a word
   somebody left in the row. */
.followup-cell.is-chip .due-tag,
.due-cell.is-chip .due-tag {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--gray-400);
}
.due-cell.is-chip .inline-date {
  padding-top: 1px;
  padding-bottom: 1px;
  font-size: 11.5px;
  min-width: 0;
}
.due-cell.is-chip .due-label {
  display: inline;
  margin-top: 0;
  font-size: 11px;
}
/* Nothing due: no empty gap where a label would be. */
.due-cell.is-chip .due-label:empty { display: none; }

/* The status chip borrows the table's colour scale, so a lead reads the same
   in both places. */
.drawer-chip.is-status[data-status="new"]           { background: #eff6ff; color: #1d4ed8; }
.drawer-chip.is-status[data-status="1st-follow-up"] { background: #fffbeb; color: #b45309; }
.drawer-chip.is-status[data-status="2nd-follow-up"] { background: #fff7ed; color: #c2410c; }
.drawer-chip.is-status[data-status="3rd-follow-up"] { background: #fff1f2; color: #be123c; }
.drawer-chip.is-status[data-status="abandoned"]     { background: var(--gray-50); color: var(--gray-400); }
.drawer-chip.is-status[data-status="converted"]     { background: #ecfdf5; color: #047857; }

.drawer-links { display: flex; flex-wrap: wrap; gap: 6px; }
.drawer-link {
  padding: 5px 11px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-pill);
  background: white;
  color: var(--gray-700);
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  transition: border-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out),
              background-color var(--dur-1) var(--ease-out);
}
.drawer-link:hover { border-color: var(--noble-primary); color: var(--noble-primary); background: var(--noble-light); }
.drawer-link:focus-visible { outline: none; box-shadow: var(--ring); }

/* The link row became a tab row. Three of its four buttons used to leave the
   page — two navigations and a mailto: — which is the whole "don't take me off
   this screen" complaint in one control. */
.drawer-link.is-tab { font-weight: 600; }
.drawer-link.is-tab.is-current {
  border-color: var(--noble-primary);
  color: var(--noble-primary);
  background: var(--noble-light);
}
.drawer-panel[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .drawer-panel:not([hidden]) {
    animation: drawer-panel-in var(--dur-2) var(--ease-out);
  }
}
@keyframes drawer-panel-in {
  from { opacity: 0; translate: 0 4px; }
}

/* The chat transcript, read inside the drawer instead of on its own page.
   Deliberately plainer than the full conversation view: this is for the
   sentence of context an agent needs before writing a follow-up, not for
   working the conversation. */
/* Why the chat half of the thread is missing, above the emails that remain.
   Deliberately not styled as a message: it is the panel talking, not somebody
   in the exchange, and a reader scanning the thread must not take it for a
   turn. Same muted weight as .drawer-empty, which is the same voice. */
.conv-note {
  margin: 0 0 10px;
  padding: 8px 10px;
  border-left: 2px solid var(--gray-200);
  color: var(--gray-400);
  font-size: 12.5px;
  line-height: 1.45;
}

.conv-thread {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}
.conv-msg {
  padding: 9px 12px;
  border-radius: var(--radius);
  background: var(--gray-50);
  border: 1px solid var(--gray-100);
}
.conv-msg[data-role="user"] {
  background: var(--noble-light);
  border-color: transparent;
}
/* ⚠️ This line used to be uppercased, back when it only ever said VISITOR or
   ASSISTANT. It carries people's names now, and TUNISHA WELLS is shouting at
   somebody who did nothing wrong. The chips keep the uppercase — they are
   labels, not names. */
.conv-meta {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--gray-400);
  margin-bottom: 3px;
}
.conv-body {
  font-size: 13.5px;
  color: var(--gray-800);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* A body chat-render.js produced: newlines are already <br> and blocks are
   markup, so pre-wrap would double every line break. */
.conv-body.is-rendered { white-space: normal; }
.conv-body.is-rendered p { margin: 0 0 6px; }
.conv-body.is-rendered p:last-child { margin-bottom: 0; }
.conv-body.is-rendered ul,
.conv-body.is-rendered ol { margin: 4px 0 6px; padding-left: 20px; }
.conv-body.is-rendered h3 { font-size: 13.5px; margin: 6px 0 2px; }
.conv-body.is-rendered a { color: var(--noble-primary); }
/* The course card the visitor was offered — the widget's .course-card
   (public/styles.css), trimmed for a side panel. */
.conv-body .course-card {
  display: block;
  margin: 8px 0;
  padding: 10px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--gray-200);
  background: #fff;
  color: inherit;
  text-decoration: none;
}
.conv-body a.course-card:hover { border-color: var(--noble-primary); text-decoration: none; }
.conv-body .cc-name { font-weight: 700; font-size: 13.5px; color: var(--gray-900); line-height: 1.3; }
.conv-body .cc-desc { font-size: 12.5px; color: var(--gray-600); margin-top: 2px; line-height: 1.4; }
.conv-body .cc-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--gray-100);
  font-size: 12px;
  color: var(--gray-700);
}
.conv-body .cc-meta-item { display: inline-flex; align-items: center; gap: 4px; }
.conv-body .cc-price { font-weight: 600; color: var(--gray-900); }
.conv-body .cc-cta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--noble-primary);
}
/* A form the bot offered is a record here, not a control: the fields show
   what was prefilled, the buttons would submit nothing. */
.conv-body .cf-btn,
.conv-body .lf-submit { display: none; }
.conv-full { display: inline-block; margin-top: 12px; }

/* An email in the thread. Marked by a left rule and a small channel label
   rather than a different colour: the existing us/them colouring already
   carries the direction, and a second colour axis on top of it would make the
   panel read as four kinds of thing instead of two. */
.conv-msg[data-channel="email"] { border-left: 3px solid var(--gray-300); }
.conv-msg[data-channel="email"][data-role="user"] { border-left-color: var(--noble-primary); }
.conv-channel {
  display: inline-block;
  padding: 1px 5px;
  margin-right: 4px;
  border-radius: 3px;
  background: var(--gray-200);
  color: var(--gray-600);
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
/* A note between colleagues, which the visitor was never sent. It sat in this
   thread indistinguishable from a reply to them until 2026-08-29 — dashed, so
   it reads as attached to the exchange rather than part of it. */
.conv-msg[data-channel="internal"] {
  background: transparent;
  border-style: dashed;
  border-color: var(--gray-200);
}
/* Which lead this turn arrived on, when a merge put two chats in one thread.
   Same chip shape as the channel tag beside it, quieter, because it explains
   the thread rather than being part of it. */
.conv-merged {
  display: inline-block;
  padding: 1px 5px;
  margin-left: 6px;
  border-radius: 3px;
  background: var(--gray-100);
  color: var(--gray-500);
  text-transform: uppercase;
}
.conv-subject {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--gray-700);
  margin-bottom: 3px;
}

/* Editing the person without leaving the queue. */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 11px 16px;
}
.contact-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.contact-field .inline-notes-input,
.contact-field .inline-select { width: 100%; }

.drawer-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 12px 20px;
  margin: 0;
}
.drawer-field dt {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--gray-400);
  margin-bottom: 2px;
}
.drawer-field dd {
  margin: 0;
  font-size: 13.5px;
  color: var(--gray-800);
  overflow-wrap: anywhere;
}

/* ── Edgar record panel (lead drawer) ──
   Read-only by design: Edgar owns the student and the VA enrollment, so every
   card here links back to the page that edits it rather than editing in place.

   The `is-warn` variant is load-bearing rather than decorative. A dead Edgar
   token and a student with no GI Bill record both produce an empty panel, and
   if they looked alike an expired credential would read as "no benefits" on
   every VA lead while appearing to work. */
.drawer-empty.is-warn {
  color: #b45309;
  background: #fffbeb;
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}

.edgar-people {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
}
.edgar-person {
  display: flex;
  align-items: baseline;
  gap: 7px;
}
.edgar-person-meta { font-size: 12px; color: var(--gray-400); }
.edgar-note {
  margin: 0 0 10px;
  font-size: 12.5px;
  color: var(--gray-500);
}

.va-cards { display: grid; gap: 10px; }
.va-card {
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
  background: white;
  box-shadow: var(--shadow);
}
.va-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 9px;
}
.va-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--gray-800);
  overflow-wrap: anywhere;
}
.va-status {
  flex: none;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 11.5px;
  font-weight: 500;
}
/* auto-fit rather than a fixed column count: the drawer is 680px at most and
   narrower on a laptop, and a benefits percentage should never be the thing
   that makes the panel scroll sideways. */
.va-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 9px 16px;
  margin: 0;
}
.va-field dt {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--gray-400);
  margin-bottom: 2px;
}
.va-field dd {
  margin: 0;
  font-size: 13px;
  color: var(--gray-800);
  overflow-wrap: anywhere;
}
.va-edit { display: inline-block; margin-top: 10px; }

/* Course history: context for the conversation, collapsed so it cannot push
   the GI Bill record it sits under off the screen. */
.edgar-history { margin-top: 12px; }
.edgar-history summary {
  cursor: pointer;
  font-size: 12.5px;
  color: var(--gray-600);
  padding: 4px 0;
}
.edgar-history summary:focus-visible { outline: none; box-shadow: var(--ring); }
.edgar-history h5 {
  margin: 10px 0 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--gray-400);
}
.hist-list { list-style: none; margin: 0; padding: 0; }
.hist-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 12px;
  padding: 4px 0;
  border-bottom: 1px solid var(--gray-100);
}
.hist-list li:last-child { border-bottom: 0; }
.hist-name { font-size: 13px; color: var(--gray-800); min-width: 0; overflow-wrap: anywhere; }
.hist-meta { flex: none; font-size: 12px; color: var(--gray-400); }

.drawer-section h4 {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--gray-400);
  margin-bottom: 8px;
}
/* Consecutive sections had nothing between them: the h4 above carries a
   margin-BOTTOM and `.drawer-section` itself carried no margin at all, so every
   heading after the first landed flush against whatever ended the section
   before it — "FILED UNDER" sitting directly on the Company input, reported
   2026-08-20. Spacing the JOIN rather than each section keeps the first one
   tight to the tab row above it, and fixes the record panel's stack (GI Bill,
   Edgar record, Notes, History) in the same rule. */
.drawer-section + .drawer-section { margin-top: 18px; }
.drawer-quote {
  margin: 0;
  padding: 10px 12px;
  border-left: 3px solid var(--gray-200);
  background: var(--gray-50);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: 13.5px;
  color: var(--gray-700);
  white-space: pre-wrap;
}
.drawer-notes {
  width: 100%;
  font-family: inherit;
  font-size: 13.5px;
  color: var(--gray-800);
  padding: 9px 11px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  resize: vertical;
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.drawer-notes:focus { border-color: var(--noble-primary); box-shadow: var(--ring); }

/* ── Follow-up composer ──
   Lives in the drawer, because the follow-up is about this one lead and the
   context it needs (what they asked about, what was said before) is already
   on screen above it. */
.followup-from {
  margin: 0 0 8px;
  font-size: 12px;
  color: var(--gray-500);
}
.followup-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}
.followup-row .inline-select {
  flex: 1 1 200px;
  /* A select's intrinsic width is its widest option, and a flex item refuses
     to shrink below intrinsic width without this — so one long email subject
     in the thread picker made the select wider than the drawer and put a
     horizontal scrollbar on the whole sheet. Truncate the closed control
     instead; the opened dropdown still shows every option in full. */
  min-width: 0;
  max-width: 100%;
  text-overflow: ellipsis;
}
/* The subject and the "Write a new subject" button share a row, so the way out
   of a thread-owned subject sits against the field it unlocks. */
.followup-subject-row {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}
.followup-subject-row .followup-subject {
  flex: 1 1 auto;
  min-width: 0;
  margin-bottom: 0;
}
.followup-subject-row .btn { flex: 0 0 auto; white-space: nowrap; }
.followup-subject {
  width: 100%;
  margin-bottom: 8px;
  padding: 8px 11px;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--gray-800);
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  outline: none;
  box-sizing: border-box;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.followup-subject:focus { border-color: var(--noble-primary); box-shadow: var(--ring); }
/* A locked subject has to LOOK locked. Kimberly reported the field as
   uneditable-but-not-obviously-so: browser default greying is faint at this
   weight, so a reply's subject read as a box she should be able to type in. */
.followup-subject:disabled {
  background: var(--gray-50, #f9fafb);
  color: var(--gray-500);
  cursor: default;
}
/* Unfilled tokens are shown rather than blanked: a sentence with a hole in it
   that nobody notices is worse than an obvious one. */
/* Deliberately quieter than .followup-warn below: nothing is wrong, the agent
   is being told where the words came from. Reads as a caption on the textarea
   rather than as an alert about it. */
.followup-ai-note {
  margin: 6px 0 0;
  padding: 6px 9px;
  border-radius: var(--radius-sm);
  background: var(--gray-50, #f9fafb);
  border: 1px solid var(--gray-200);
  color: var(--gray-600);
  font-size: 11.5px;
  line-height: 1.45;
}

.followup-warn {
  margin: 8px 0 0;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  background: #fffbeb;
  border: 1px solid #fde68a;
  color: #92400e;
  font-size: 12px;
}
.followup-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
}
.followup-status { font-size: 12px; color: var(--gray-500); }

/* ── Shared rich-text editor (static/rich-text.js) ──
   Toolbar + contenteditable + link dialog, used by the lead follow-up
   composer. Styled as ONE control: the toolbar is the lid of the box the
   editor sits in, so the pair reads as a field rather than as a row of
   buttons that happens to sit above a textarea. */
.rt-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px;
  row-gap: 4px;
  padding: 5px 7px;
  background: var(--gray-50);
  border: 1px solid var(--gray-300);
  border-bottom: 0;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.rt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 5px;
  background: transparent;
  color: var(--gray-700);
  font: inherit;
  font-size: 13.5px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.rt-btn:hover { background: var(--gray-200); }
.rt-btn:focus-visible { outline: 2px solid var(--noble-primary); outline-offset: 1px; }
.rt-btn.is-active {
  background: rgba(85, 7, 43, 0.08);
  color: var(--noble-primary);
  border-color: rgba(85, 7, 43, 0.35);
}
/* The B/I/U glyphs ARE the button labels, so they carry the weight/slant they
   name — the <i> must not inherit the toolbar's upright font. */
.rt-btn b, .rt-btn i, .rt-btn u { font-style: normal; font-weight: 700; }
.rt-btn i { font-style: italic; font-weight: 600; }
.rt-btn u { font-weight: 600; }
.rt-sep { width: 1px; height: 18px; background: var(--gray-300); margin: 0 4px; }

.rt-editor {
  width: 100%;
  min-height: 150px;
  max-height: 320px;
  overflow-y: auto;
  padding: 9px 11px;
  box-sizing: border-box;
  background: #fff;
  border: 1px solid var(--gray-300);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  font-family: inherit;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--gray-800);
  /* A pasted URL is one unbreakable token; without this it pushes the drawer
     into a horizontal scroll it can never be scrolled back from. */
  overflow-wrap: anywhere;
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.rt-editor:focus { border-color: var(--noble-primary); box-shadow: var(--ring); }
/* Class-driven, not :empty — an "empty" contenteditable usually still holds a
   stray <br> the browser left behind. */
.rt-editor.is-empty::before {
  content: attr(data-placeholder);
  color: var(--gray-400);
  pointer-events: none;
}
.rt-editor p { margin: 0 0 8px; }
.rt-editor ul, .rt-editor ol { margin: 0 0 8px; padding-inline-start: 22px; }
.rt-editor li { margin: 0 0 2px; }
.rt-editor a { color: var(--noble-primary); text-decoration: underline; cursor: pointer; }
.rt-editor img { max-width: 100%; height: auto; }

/* Link dialog. Built once by rich-text.js and parked on <body>; it opens with
   showModal() from inside the lead drawer, which is itself a modal — both live
   in the top layer, so the nesting is fine. */
.rt-link-dialog {
  width: min(420px, calc(100vw - 32px));
  /* ⚠️ The `* { margin: 0 }` reset at the top of this file kills the UA
     stylesheet's `margin: auto` on <dialog>, which is the ONLY thing centering
     a modal one — without this the dialog opens pinned to the top-left corner. */
  margin: auto;
  padding: 20px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  color: var(--gray-800);
}
.rt-link-dialog::backdrop { background: rgba(15, 23, 42, 0.35); }
.rt-link-title { margin: 0 0 14px; font-size: 15px; font-weight: 600; }
.rt-link-label {
  display: block;
  margin-bottom: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.rt-link-input {
  width: 100%;
  margin-bottom: 12px;
  padding: 8px 10px;
  box-sizing: border-box;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13.5px;
  color: var(--gray-800);
  outline: none;
}
.rt-link-input:focus { border-color: var(--noble-primary); box-shadow: var(--ring); }
.rt-link-error { margin: -6px 0 10px; color: #b91c1c; font-size: 12px; }
.rt-link-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* The history. A rail with a dot per entry, so a long list reads as one
   sequence rather than a stack of unrelated lines. */
.activity-list {
  list-style: none;
  margin: 0;
  padding: 0 0 0 18px;
  border-left: 1px solid var(--gray-200);
}
.activity-item { position: relative; padding: 0 0 14px 4px; }
.activity-item:last-child { padding-bottom: 0; }
.activity-item::before {
  content: "";
  position: absolute;
  left: -23px;
  top: 5px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--gray-300);
  box-shadow: 0 0 0 3px white;
}
.activity-item[data-kind="created"]::before { background: var(--blue); }
.activity-item[data-kind="status"]::before { background: var(--noble-primary); }
.activity-item[data-kind="followed_up"]::before,
.activity-item[data-kind="email_out"]::before { background: var(--green); }
.activity-item[data-kind="due"]::before { background: var(--yellow); }
.activity-item[data-kind="merged"]::before,
.activity-item[data-kind="unmerged"]::before,
.activity-item[data-kind="email_changed"]::before { background: var(--accent-ink); }
.activity-line { font-size: 13.5px; color: var(--gray-800); overflow-wrap: anywhere; }
.activity-meta { font-size: 11.5px; color: var(--gray-400); margin-top: 2px; }

/* Undo sits ON the entry that records the merge, not in a menu somewhere: the
   reversal belongs beside the thing it reverses, and a merge nobody can find
   is a merge nobody can undo. Only entries whose merge is still standing get
   one — the template checks that before rendering the button. */
.activity-undo {
  margin-left: 8px;
  padding: 1px 7px;
  font-size: 11.5px;
  font-family: inherit;
  color: var(--gray-600);
  background: white;
  border: 1px solid var(--gray-300);
  border-radius: 999px;
  cursor: pointer;
}
.activity-undo:hover { border-color: var(--gray-400); color: var(--gray-800); }
.activity-undo:disabled { opacity: .5; cursor: default; }

/* ── Duplicates banner + merge dialog ──
   The banner is the everyday path into a merge: you open a lead, and it tells
   you who else looks like this person. Deliberately above the record and
   deliberately quiet — most duplicates are two genuine enquiries months apart
   and want leaving alone, so this states a fact and offers a look, rather than
   pressing anyone to act. */
.dupe-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 12px 0 0;
  padding: 10px 12px;
  border: 1px solid #fde68a;
  background: #fffbeb;
  border-radius: var(--radius-sm);
}
.dupe-copy { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.dupe-copy strong { font-size: 13px; color: var(--gray-800); }
.dupe-list { font-size: 12px; color: var(--gray-600); overflow-wrap: anywhere; }
.dupe-banner .btn { flex: none; }

.merge-sheet { width: min(620px, calc(100vw - 32px)); }
.merge-block { display: flex; flex-direction: column; gap: 8px; }
.merge-block h4 {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--gray-500);
}
.merge-quiet { font-size: 12.5px; color: var(--gray-500); }

.merge-options { display: flex; flex-direction: column; gap: 6px; }
.merge-option {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
}
.merge-option:hover { border-color: var(--gray-300); }
.merge-option.is-keeping { border-color: var(--noble-primary); background: var(--noble-light); }
/* Ticked off: still readable, visibly not part of the merge. A person who
   exists on two brands is usually two correct records, so this is a normal
   resting state and not a warning. */
.merge-option.is-skipped { border-style: dashed; }
.merge-option.is-skipped .merge-option-body { opacity: 0.55; }
.merge-option input { margin-top: 3px; }
/* Two controls per row, so each gets its own label: the radio chooses which
   lead survives, the checkbox whether this one is in the merge at all. */
.merge-pick {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  flex: 1 1 auto;
  min-width: 0;
  cursor: pointer;
}
.merge-include {
  display: flex;
  gap: 5px;
  align-items: center;
  flex: none;
  font-size: 12px;
  color: var(--gray-500);
  cursor: pointer;
  white-space: nowrap;
}
.merge-include input { margin-top: 0; }
.merge-option-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.merge-option-top {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--gray-800);
}
.merge-tag {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  padding: 1px 6px;
  border-radius: 999px;
  color: var(--gray-600);
  background: var(--gray-100);
}
.merge-option.is-keeping .merge-tag { color: white; background: var(--noble-primary); }
.merge-option-meta { font-size: 11.5px; color: var(--gray-500); }

/* Both rows carry a real, different value here, so the software refuses to
   choose — the server rejects a merge that arrives without a pick. */
.merge-conflict {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
  padding: 8px 10px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
}
.merge-conflict-label {
  flex: 0 0 100%;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--gray-600);
}
.merge-conflict label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--gray-800);
  cursor: pointer;
}

.merge-changes { display: flex; flex-direction: column; gap: 4px; }
.merge-changes li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  font-size: 13px;
  padding: 4px 0;
  border-bottom: 1px solid var(--gray-100);
}
.merge-changes li span { color: var(--gray-500); }
.merge-changes li b { color: var(--gray-800); font-weight: 600; text-align: right; }
.merge-losses { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--gray-700); }
.merge-cross {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  font-size: 12.5px;
  color: var(--gray-700);
  padding: 8px 10px;
  border: 1px solid #fde68a;
  background: #fffbeb;
  border-radius: var(--radius-sm);
}
.merge-cross input { margin-top: 2px; }

/* ── Follow-up queue bar ──
   Saved views, each carrying its count. Links rather than buttons because each
   one is a real URL an agent can bookmark or send to a teammate. The count is
   the point of the control, so it is never hidden at zero: "Overdue 0" is the
   answer people come here for. */
.queue-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--gray-200);
}
.queue-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  border-radius: var(--radius-pill);
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-600);
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
.queue-tab:hover { background: var(--gray-100); color: var(--gray-800); }
.queue-tab:focus-visible { outline: none; box-shadow: var(--ring); }
.queue-tab.is-active {
  background: var(--noble-primary);
  color: white;
}
.queue-count {
  min-width: 20px;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 11.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
.queue-tab:hover .queue-count { background: var(--gray-200); }
.queue-tab.is-active .queue-count { background: rgba(255,255,255,0.22); color: white; }
/* Overdue carries the same red the overdue rows do, but only when there is
   something overdue — an empty queue is good news and shouldn't shout. */
.queue-tab.is-urgent:not(.is-active) { color: var(--red); }
.queue-tab.is-urgent:not(.is-active) .queue-count { background: #fef2f2; color: var(--red); }
.queue-tab.is-urgent:not(.is-active):hover { background: #fef2f2; color: #b91c1c; }

/* The table is wide by nature (two selects, three dates, a notes field), so it
   scrolls in its own box and the page body never scrolls sideways. */
.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border-radius: var(--radius-lg);
}

/* `.admin-main` is a flex item of `.admin-layout`, so its `min-width: auto`
   floor is min-content: it grows to fit a wide table instead of letting the
   table scroll, and the whole PAGE ends up scrolling sideways with the sidebar
   sliding out of view. Releasing that floor is what makes `overflow-x` above
   actually do anything.

   Scoped with :has() to pages that opt in by using .table-scroll rather than
   applied to .admin-main outright, so no existing page's layout shifts. The
   corporate board is the same shape (a wide flex row that must scroll in its
   own box), so it opts in too. */
body:has(.table-scroll) .admin-main,
body:has(.board) .admin-main { min-width: 0; }
.leads-table { min-width: 1120px; }
.leads-table td { vertical-align: middle; }

/* Identity: the name leads, the address is support. */
.lead-identity { min-width: 190px; }
/* A button, not a link: it opens the record in place rather than navigating.
   Styled as text so the table still reads as a table. */
.lead-name {
  display: block;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-weight: 600;
  color: var(--gray-900);
  text-align: left;
  cursor: pointer;
  transition: color var(--dur-1) var(--ease-out);
}
.lead-name:hover { color: var(--noble-primary); text-decoration: underline; }
.lead-name:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--radius-sm); }
.lead-name-empty { color: var(--gray-400); font-weight: 500; }
.lead-email {
  display: block;
  font-size: 12px;
  color: var(--gray-500);
  margin-top: 1px;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lead-phone { font-size: 12px; color: var(--gray-500); }

/* In the drawer this chip is a BUTTON: it is the affordance for changing which
   website a lead is filed under, because the control itself lives a tab away
   under "Filed under" and went unfound (reported 2026-08-20). Styled as the
   same pill either way so the table's read-only chips are unaffected. */
button.lead-site {
  border: 1px dashed var(--gray-300);
  cursor: pointer;
  font-family: inherit;
  transition: background var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
button.lead-site::after {
  content: "";
  display: inline-block;
  width: 0.32em;
  height: 0.32em;
  margin-left: 0.45em;
  vertical-align: 0.18em;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  rotate: 45deg;
}
button.lead-site:hover,
button.lead-site:focus-visible {
  background: var(--noble-light);
  border-color: var(--noble-primary);
  color: var(--noble-primary);
}
.lead-site {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 11.5px;
  font-weight: 500;
  white-space: nowrap;
}

/* Interest is a select like the two classification fields beside it, so it
   shares their styling and just gets more room — its vocabulary entries are
   the longest of the three. */
.lead-interest { min-width: 170px; max-width: 210px; color: var(--gray-700); }
.lead-interest .classify-select { width: 100%; font-size: 12.5px; color: var(--gray-700); }
/* Both cells hold editable controls now. The two classification selects stack,
   which keeps the column the width of one of them rather than both. */
.lead-class { min-width: 150px; }
.lead-class .classify-select { display: block; width: 100%; }
.lead-class .classify-select + .classify-select { margin-top: 3px; }
/* Quieter than Status: these are attributes of the lead, not its lifecycle,
   and two saturated pills per row would compete with the Due column that the
   page exists to make readable. */
.classify-select { font-size: 11.5px; color: var(--gray-600); }
/* ⚠️ background-COLOR, not `background`. The shorthand also clears
   background-image, which is where .inline-select keeps its chevron — and a
   select with no chevron and no border reads as static text, so nobody
   discovers the column is editable at all. */
.classify-select:not(:hover):not(:focus) {
  border-color: transparent;
  background-color: transparent;
}

.lead-blank { color: var(--gray-300); }

.lead-created { font-size: 12.5px; color: var(--gray-500); white-space: nowrap; }

/* ── Inline controls ──
   Native select and date inputs, styled rather than replaced, so keyboard
   behavior and the platform date picker stay exactly as they are. */
.inline-select,
.inline-date,
.inline-notes-input {
  font-family: inherit;
  font-size: 12.5px;
  color: var(--gray-800);
  background: white;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  padding: 5px 8px;
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              background-color var(--dur-1) var(--ease-out);
}
.inline-select:hover,
.inline-date:hover,
.inline-notes-input:hover { border-color: var(--gray-400); }
.inline-select:focus,
.inline-date:focus,
.inline-notes-input:focus {
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.inline-date { min-width: 132px; }
.inline-notes-input { width: 190px; }
.inline-notes-input::placeholder { color: var(--gray-300); }

/* Status reads as a pill that happens to be a select: appearance:none plus our
   own chevron, colored by the lifecycle stage the row is actually in. */
.inline-select {
  appearance: none;
  padding-right: 24px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%239ca3af' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  cursor: pointer;
}
.status-select {
  font-weight: 600;
  border-radius: var(--radius-pill);
  padding-left: 10px;
}
.status-select[data-status="new"]           { background-color: #eff6ff; border-color: #bfdbfe; color: #1d4ed8; }
.status-select[data-status="existing"]      { background-color: var(--gray-100); border-color: var(--gray-200); color: var(--gray-600); }
.status-select[data-status="1st-follow-up"] { background-color: #fffbeb; border-color: #fde68a; color: #b45309; }
.status-select[data-status="2nd-follow-up"] { background-color: #fff7ed; border-color: #fed7aa; color: #c2410c; }
.status-select[data-status="3rd-follow-up"] { background-color: #fff1f2; border-color: #fecdd3; color: #be123c; }
.status-select[data-status="abandoned"]     { background-color: var(--gray-50); border-color: var(--gray-200); color: var(--gray-400); }
.status-select[data-status="converted"]     { background-color: #ecfdf5; border-color: #a7f3d0; color: #047857; }

/* Assigned-to reads quieter when nobody owns the lead, so a screenful of
   unassigned rows doesn't shout. */
.assign-select[data-assigned="0"] { color: var(--gray-400); }

/* ── The signature: follow-up urgency ──
   The one place on the page with real color weight, because it encodes the one
   question the page exists to answer. */
.due-cell { position: relative; white-space: nowrap; }
.due-label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  margin-top: 3px;
  letter-spacing: 0.2px;
}
.due-cell[data-urgency="overdue"] .inline-date {
  border-color: #fecaca;
  background-color: #fef2f2;
  color: #b91c1c;
}
.due-cell[data-urgency="overdue"] .due-label { color: var(--red); }
.due-cell[data-urgency="soon"] .inline-date {
  border-color: #fde68a;
  background-color: #fffbeb;
  color: #b45309;
}
.due-cell[data-urgency="soon"] .due-label { color: #b45309; }
.due-cell[data-urgency="none"] .due-label { color: var(--gray-400); font-weight: 500; }
/* A date outside any plausible range — a mistyped year. Says what to do about
   it instead of rendering "666287 days overdue" and sorting to the top. */
.due-cell[data-urgency="invalid"] .inline-date {
  border-color: #fde68a;
  background-color: #fffbeb;
  color: #92400e;
}
.due-cell[data-urgency="invalid"] .due-label { color: #b45309; }

/* A due date someone typed outranks the computed one, and says so. */
.inline-date[data-overridden="1"] { border-color: var(--accent-ink); }

/* A last-follow-up still equal to the creation date means nobody has actually
   made contact yet. Muted rather than hidden: the date is real, its provenance
   just isn't a human. */
.followup-cell[data-seeded="1"] .inline-date { color: var(--gray-400); }

.leads-table tbody tr[data-urgency="overdue"] td:first-child {
  box-shadow: inset 3px 0 0 var(--red);
}

/* ── Save feedback ──
   Inline edits used to reload the whole page on success and alert() on failure.
   Now the row confirms itself and errors surface in a toast, so a run of edits
   never loses scroll position. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes lead-row-saved {
    from { background-color: rgba(22,163,74,0.16); }
    to   { background-color: transparent; }
  }
  .leads-table tbody tr.is-saved td { animation: lead-row-saved 1.1s var(--ease-out); }
}
.leads-table tbody tr.is-saving { opacity: 0.55; }
.leads-table tbody tr:hover td { background-color: var(--gray-50); }

.toast-stack {
  position: fixed;
  bottom: 20px;
  left: 50%;
  translate: -50% 0;
  z-index: 1200;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(460px, calc(100vw - 32px));
  padding: 10px 14px;
  border-radius: var(--radius);
  background: var(--gray-900);
  color: white;
  font-size: 13px;
  box-shadow: var(--shadow-md);
  pointer-events: auto;
  transition: opacity var(--dur-2) var(--ease-out),
              translate var(--dur-3) var(--ease-spring);
}
.toast[data-tone="error"] { background: #7f1d1d; }
@starting-style {
  .toast { opacity: 0; translate: 0 10px; }
}
.toast.is-leaving { opacity: 0; translate: 0 6px; }
.toast-retry {
  border: none;
  background: rgba(255,255,255,0.16);
  color: inherit;
  font: inherit;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out);
}
.toast-retry:hover { background: rgba(255,255,255,0.28); }

/* ── Filter bar ── */
.filter-group {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--gray-600);
  white-space: nowrap;
}
.filter-group input[type="date"] {
  padding: 6px 8px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 12.5px;
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.filter-group input[type="date"]:focus {
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.filter-sep { color: var(--gray-300); }

.page-sort { color: var(--gray-400); font-weight: 400; }

/* ── Leads filter bar ──
   One wrapping row of equals: search, four identical pickers, two ranges and
   Clear. Clear used to sit on a line of its own because the date inputs ate
   the row; the ranges collapse to a single select until somebody asks for a
   custom one, which is what gave the row back. */
.filter-form { row-gap: 8px; }

.filter-search {
  position: relative;
  display: flex;
  align-items: center;
  /* Sized so the whole bar fits one row on a 1440px laptop. It grows into
     spare space and shrinks before the pickers do, because a search box that
     is a little narrow still works and a wrapped filter row is what this
     redesign set out to remove. */
  flex: 1 1 200px;
  min-width: 170px;
  max-width: 260px;
}
.filter-search-icon {
  position: absolute;
  left: 9px;
  width: 15px;
  height: 15px;
  color: var(--gray-400);
  pointer-events: none;
}
.filter-search input {
  width: 100%;
  padding: 7px 10px 7px 30px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 13px;
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.filter-search input:focus {
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.filter-search input::placeholder { color: var(--gray-400); }

/* A picker that is filtering has to look different from one that is not, or a
   collapsed menu hides the reason the table is short. */
.dropdown-filter.is-active .dropdown-toggle {
  border-color: var(--noble-primary);
  color: var(--accent-ink, var(--noble-primary));
  background: var(--noble-light);
}

/* The two ranges travel together: they are one question ("when"), and letting
   them split left a single control stranded on its own row. */
.filter-when {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 14px;
}
.filter-range {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--gray-600);
  white-space: nowrap;
}
.filter-range-label { color: var(--gray-500); }
.filter-range select {
  padding: 7px 10px;
  font-size: 13px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: white;
  font-family: inherit;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.filter-range select:focus-visible {
  outline: none;
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.filter-custom { display: flex; align-items: center; gap: 6px; }
/* `hidden` on a flex container needs saying explicitly, the same trap the
   calls toolbar hit. */
.filter-custom[hidden] { display: none; }
.filter-custom input[type="date"] {
  padding: 6px 8px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 12.5px;
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.filter-custom input[type="date"]:focus {
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
@media (prefers-reduced-motion: no-preference) {
  .filter-custom {
    animation: filter-custom-in var(--dur-2) var(--ease-out);
  }
}
@keyframes filter-custom-in {
  from { opacity: 0; translate: -4px 0; }
}

.filter-clear { margin-left: auto; }

.dropdown-filter { position: relative; display: inline-block; }
.dropdown-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  font-family: inherit;
  font-size: 13px;
  color: var(--gray-700);
  background: white;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.dropdown-toggle:hover { border-color: var(--gray-400); }
.dropdown-toggle:focus-visible { outline: none; border-color: var(--noble-primary); box-shadow: var(--ring); }
.dropdown-toggle .caret { font-size: 9px; color: var(--gray-400); }

/* Entry and exit both transition, which needs allow-discrete for `display`. */
.dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 100;
  min-width: 190px;
  padding: 5px 0;
  background: white;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  text-align: left;
  opacity: 1;
  translate: 0 0;
  transition-property: opacity, translate, display;
  transition-duration: var(--dur-2);
  transition-timing-function: var(--ease-out);
  transition-behavior: allow-discrete;
}
@starting-style {
  .dropdown-menu { opacity: 0; translate: 0 -6px; }
}
.dropdown-menu[hidden] {
  display: none;
  opacity: 0;
  translate: 0 -6px;
}
.dropdown-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 13px;
  font-size: 13px;
  color: var(--gray-700);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out);
}
.dropdown-item:hover { background: var(--gray-50); }
.dropdown-item input[type="checkbox"] {
  flex: none;
  width: 15px;
  height: 15px;
  accent-color: var(--noble-primary);
  cursor: pointer;
}
/* The Status picker's default is not "everything" — it hides the three
   statuses nobody is working — so its "all" item says so underneath. Its own
   line, because side by side it would set the menu's width to that of both
   lines together; the item still never wraps mid-phrase. */
.dropdown-item:has(.dropdown-hint) { flex-wrap: wrap; }
.dropdown-hint {
  flex-basis: 100%;
  padding-left: 24px; /* checkbox (15) + the item's gap (9) */
  font-size: 11px;
  color: var(--gray-500);
}

/* ── Bulk bar ──
   Slides up on the spring easing instead of appearing between frames. */
.bulk-bar {
  translate: 0 0;
  opacity: 1;
  transition-property: opacity, translate, display;
  transition-duration: var(--dur-3);
  transition-timing-function: var(--ease-spring);
  transition-behavior: allow-discrete;
}
@starting-style {
  .bulk-bar { opacity: 0; translate: 0 14px; }
}
/* Beats the UA `[hidden] { display: none }` rule, which a bare attribute
   selector would lose to `.bulk-bar { display: flex }`. */
.bulk-bar[hidden] {
  display: none;
  opacity: 0;
  translate: 0 14px;
}

/* ── Lead category tabs (All / Individual / Corporate / VA) ──
   A segmented control of links: one visual unit, each tab a real URL. Visually
   distinct from the queue pills below it — this picks WHICH business, the
   queue picks which slice of its work. */
.cat-bar {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-bottom: 12px;
  padding: 3px;
  width: fit-content;
  max-width: 100%;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-pill);
  background: var(--gray-50);
}
.cat-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border-radius: var(--radius-pill);
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-600);
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-1) var(--ease-out);
}
.cat-tab:hover { background: var(--gray-100); color: var(--gray-800); }
.cat-tab:focus-visible { outline: none; box-shadow: var(--ring); }
.cat-tab.is-active {
  background: white;
  color: var(--gray-900);
  box-shadow: var(--shadow);
}
.cat-count {
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--gray-200);
  color: var(--gray-600);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.cat-tab.is-active .cat-count { background: var(--gray-100); }
/* The List | Board switch, corporate only. */
.cat-layouts {
  display: inline-flex;
  gap: 2px;
  margin-left: 10px;
  padding-left: 10px;
  border-left: 1px solid var(--gray-200);
}
.cat-layout {
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--gray-500);
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
.cat-layout:hover { background: var(--gray-100); color: var(--gray-800); }
.cat-layout:focus-visible { outline: none; box-shadow: var(--ring); }
.cat-layout.is-active { background: var(--noble-primary); color: white; }

/* ── Corporate list bits ── */
.lead-company, .lead-value { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lead-value { font-variant-numeric: tabular-nums; }
.stage-select[data-stage="won"]  { background-color: #ecfdf5; border-color: #a7f3d0; color: #047857; }
.stage-select[data-stage="lost"] { background-color: var(--gray-50); border-color: var(--gray-200); color: var(--gray-400); }
.drawer-chip.is-stage[data-stage="won"]  { background: #ecfdf5; color: #047857; }
.drawer-chip.is-stage[data-stage="lost"] { background: var(--gray-50); color: var(--gray-400); }

/* ── The corporate board ──
   The pipeline as columns. The board scrolls sideways in its own box (the
   page body never does); each column scrolls its cards vertically. */
.board {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: 8px;
}
.board-col {
  flex: 0 0 250px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  background: var(--gray-50);
}
.board-col-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px 8px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.board-count {
  min-width: 20px;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--gray-200);
  color: var(--gray-600);
  font-size: 11px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.board-cards {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 60px;
  max-height: calc(100vh - 320px);
  overflow-y: auto;
  padding: 2px 8px 10px;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  transition: background-color var(--dur-1) var(--ease-out);
}
.board-cards.is-over { background: #eff6ff; }
.board-card {
  padding: 10px 12px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: white;
  box-shadow: var(--shadow);
  cursor: grab;
  transition: box-shadow var(--dur-2) var(--ease-out),
              translate var(--dur-2) var(--ease-spring),
              opacity var(--dur-1) var(--ease-out);
}
.board-card:hover { box-shadow: var(--shadow-md); translate: 0 -1px; }
.board-card:focus-visible { outline: none; box-shadow: var(--ring); }
.board-card.is-dragging { opacity: 0.45; cursor: grabbing; }
.board-card[data-urgency="overdue"] { box-shadow: inset 3px 0 0 var(--red), var(--shadow); }
.board-card[data-urgency="overdue"]:hover { box-shadow: inset 3px 0 0 var(--red), var(--shadow-md); }
.board-card-top { font-size: 13px; font-weight: 600; color: var(--gray-900); }
.board-card-name { font-size: 12px; color: var(--gray-600); margin-top: 1px; }
.board-card-meta {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 5px;
  font-size: 12px;
  color: var(--gray-500);
}
.board-card-topic { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-card-value { font-variant-numeric: tabular-nums; color: var(--gray-700); font-weight: 500; }
.board-card-due { margin-top: 5px; font-size: 11.5px; color: var(--gray-500); }
.board-card-due[data-urgency="overdue"] { color: var(--red); font-weight: 500; }
.board-card-due[data-urgency="today"] { color: #b45309; font-weight: 500; }
@media (prefers-reduced-motion: no-preference) {
  @keyframes board-card-saved {
    from { background-color: rgba(22,163,74,0.14); }
    to   { background-color: white; }
  }
  .board-card.is-saved { animation: board-card-saved 1.1s var(--ease-out); }
}
.board-foot { margin-top: 10px; font-size: 12.5px; color: var(--gray-500); }
.board-foot a { color: var(--gray-700); }

/* ── Drawer: the Deal field group ── */
.deal-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 14px;
}
.deal-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.deal-field .inline-select, .deal-field .inline-notes-input { width: 100%; }

/* ── Drawer: the VA field group ──
   OUR pre-enrollment answers, distinct from the live Edgar panel below it.
   auto-fit rather than the Deal block's fixed two columns because this group
   is eleven fields, not seven, and several are one word ("Yes") next to a
   label that is three. */
.va-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px 14px;
}
.va-input { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.va-input .inline-select,
.va-input .inline-notes-input,
.va-input .drawer-notes { width: 100%; }
/* Notes and the application link get the whole row: a two-line textarea in a
   150px column is unusable, and the link is a sentence. */
.va-input.is-wide { grid-column: 1 / -1; }
.va-hint {
  margin: 9px 0 0;
  font-size: 12px;
  color: var(--gray-400);
}

/* ── Corporate sheet import report (Leads → Import sheet dialog) ──
   The dry-run preview: headline counts, a sample of what would be created,
   and the per-row warnings. The warnings list scrolls in its own box so a
   messy sheet can't push the Apply button off screen. */
.import-report {
  margin-top: 4px;
  padding: 12px 14px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: var(--gray-50);
  font-size: 12.5px;
}
.import-summary { display: flex; flex-wrap: wrap; gap: 6px 16px; }
.import-stat { color: var(--gray-600); }
.import-stat strong { color: var(--gray-900); font-variant-numeric: tabular-nums; }
.import-section-label {
  margin: 12px 0 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.import-list {
  margin: 0;
  padding-left: 18px;
  max-height: 180px;
  overflow-y: auto;
  color: var(--gray-700);
}
.import-list li { margin: 2px 0; }
.import-meta { color: var(--gray-500); }
.import-warnings { color: #b45309; }

/* ── Reduced motion ──
   Last in the file so it wins over the motion added above. Scoped to what this
   pass introduced rather than a blanket `*` reset, so existing status indicators
   (the live-session pulse, the new-row highlight) keep behaving as they do today.
   Entry animations are already wrapped in `prefers-reduced-motion: no-preference`
   and never run here; what's left is to drop the movement from hover/press and
   from the two overlays, keeping the fade so open/close still reads. */
@media (prefers-reduced-motion: reduce) {
  .btn:hover, .btn:active,
  .stat-card:hover,
  .app-chip:active span,
  .tk-tool-btn:active,
  .summary-status-btn:active,
  .sidebar-nav a:hover svg { transform: none; }
  /* The preview drawer fades instead of sliding; open/close still reads. The
     visibility pair is what keeps the closed panel out of the way, since there
     is no longer an off-screen transform doing that job. */
  .summary-panel {
    transform: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-2) var(--ease-out), visibility var(--dur-2) var(--ease-out);
  }
  .summary-panel.open { transform: none; opacity: 1; visibility: visible; }
  .summary-backdrop.open { backdrop-filter: none; }
  dialog.sheet, dialog.sheet[open] { transform: none; transition-duration: var(--dur-1); }
  dialog.sheet::backdrop, dialog.sheet[open]::backdrop { backdrop-filter: none; }
  .spotlight-modal, .spotlight-overlay.active .spotlight-modal { transform: none; }
  /* Neutralize the entry offset too, or the dialog would snap from it. */
  @starting-style {
    dialog.sheet[open] { transform: none; }
  }

  /* Leads: keep the fades so open/close and arrival still read, drop the
     travel. The saved-row flash is already inside a no-preference block. */
  .dropdown-menu, .dropdown-menu[hidden],
  .bulk-bar, .bulk-bar[hidden],
  .toast, .toast.is-leaving { translate: none; }
  @starting-style {
    .dropdown-menu { translate: none; }
    .bulk-bar { translate: none; }
    .toast { translate: none; }
  }
}

/* ── mm/dd/yyyy date fields (static/us-date.js) ──
   A native <input type="date"> renders in the browser's locale, which on macOS
   comes from System Settings → Region and cannot be set by this app (verified:
   `lang` on the input and on <html> both make no difference). On a Mac set to
   en_CO that is dd/mm/yyyy, so "08/06/2026" is ambiguous between two readings —
   not acceptable on a follow-up queue. The native input stays as the value
   holder; this is the text field the agent actually reads and types into. */
.usdate {
  display: inline-flex;
  align-items: center;
  position: relative;
}
.usdate-text { padding-right: 26px; }
.usdate-text.is-invalid {
  border-color: var(--red);
  background: #fef2f2;
  color: #b91c1c;
}
.usdate-open {
  position: absolute;
  right: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--gray-400);
  cursor: pointer;
  transition: color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out);
}
.usdate-open:hover { color: var(--noble-primary); background: var(--gray-100); }
/* Clipped rather than display:none — showPicker() needs a rendered element. */
.usdate-native {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

/* ── Sortable column header ──
   Shared by every table that sorts from its header (Applications, Leads), and
   deliberately in ONE place: a second copy would be a second opinion about
   what "this column is the sort" looks like, and the two pages would drift.
   The arrow is always in the layout (a faint ⇅ when unsorted) so the header
   doesn't shift width as you cycle through the sort states. */
.th-sort { white-space: nowrap; }
.th-sort > a {
  color: inherit; text-decoration: none;
  display: inline-flex; align-items: center; gap: 5px;
}
.th-sort > a:hover { color: var(--noble-primary, #55072b); }
.th-sort > a:focus-visible {
  outline: none; box-shadow: var(--ring, 0 0 0 3px rgba(85, 7, 43, .25));
  border-radius: 3px;
}
.th-sort-arrow { font-size: 11px; color: #c3c9d2; }
.th-sort > a:hover .th-sort-arrow { color: #6b7280; }
.th-sort.is-sorted > a { color: var(--noble-primary, #55072b); }
.th-sort.is-sorted .th-sort-arrow { color: currentColor; }

/* ── Applications: one line per cell, so every row is the same height ──
   The table carries a dozen columns and admissions scans it top to bottom.
   A three-line "High School Diploma or GED" in one row and a one-line
   "Bachelor's Degree" in the next makes that scan much harder than the extra
   text is worth, so long values clip to a line and the full text is on hover. */
.data-table td.clip { max-width: 170px; white-space: nowrap; overflow: hidden;
                      text-overflow: ellipsis; }
.data-table td.clip > a { display: inline; }

/* Is this applicant booked in? The first thing looked for on a row. */
.app-pill { display: inline-block; padding: 2px 8px; border-radius: 999px;
            font-size: 12px; font-weight: 600; white-space: nowrap; }
.app-pill.is-booked { background: #ecfdf5; color: #065f46; border: 1px solid #a7f3d0; }
.app-pill.is-invited { background: #fffbeb; color: #92400e; border: 1px solid #fde68a; }
.app-pill.is-declined { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
.app-pill.is-failed { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
.app-pill.is-warn { background: #fffbeb; color: #b45309; border: 1px solid #fde68a; }
.app-pill-none { color: #d1d5db; }


/* ── Applications: the status filter popover ──
   Styled to sit in the filter row like the selects around it; the panel floats
   so ticking boxes doesn't reflow the table underneath. */
.status-filter { position: relative; display: inline-block; }
.status-filter > summary { list-style: none; cursor: pointer; padding: 6px 26px 6px 10px;
  border: 1px solid var(--gray-300, #d1d5db); border-radius: 6px; background: #fff;
  font-size: 13px; color: #374151; white-space: nowrap; }
.status-filter > summary::-webkit-details-marker { display: none; }
.status-filter > summary::after { content: "▾"; position: absolute; right: 9px; color: #9ca3af; }
.status-filter[open] > summary { border-color: #9ca3af; }
.status-pop { position: absolute; z-index: 40; top: calc(100% + 4px); left: 0; min-width: 220px;
  background: #fff; border: 1px solid #e5e7eb; border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.12); padding: 10px 12px; }
.status-modes { display: flex; gap: 12px; padding-bottom: 8px; margin-bottom: 8px;
  border-bottom: 1px solid #f3f4f6; font-size: 12.5px; color: #374151; }
.status-modes label, .status-choice { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.status-choice { font-size: 13px; color: #111827; padding: 3px 0; }
.status-pop small { display: block; margin-top: 8px; color: #9ca3af; font-size: 11.5px; }

/* ── Change a contact's email address (templates/admin/contact_change_email.html) ──
   A confirmation screen, so it reads as one narrow column of prose and counts
   rather than a form to fill in quickly. The measure is capped for the same
   reason: the warnings on this page are meant to be read, and a 1400px line of
   body text is not. */
.rekey { max-width: 62ch; display: flex; flex-direction: column; gap: 16px; }
.rekey-card {
  background: #fff;
  border: 1px solid var(--gray-200, #e5e7eb);
  border-radius: var(--radius, 10px);
  box-shadow: var(--shadow);
  padding: 20px;
}
.rekey-actions { margin-top: 14px; }
.rekey-title { font-size: 15px; font-weight: 600; color: var(--gray-800); margin: 0 0 12px; }
.rekey-moves { list-style: none; margin: 0 0 4px; padding: 0; }
.rekey-moves li {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 7px 0; font-size: 14px; color: var(--gray-800);
  border-bottom: 1px solid var(--gray-100, #f3f4f6);
}
.rekey-moves li:last-child { border-bottom: none; }
/* Tabular figures so the counts line up as a column even though the rows are
   flex — the eye reads "how much is moving" off this edge. */
.rekey-count {
  min-width: 2.5ch; text-align: right; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--noble-primary, #55072b);
}
.rekey-target { font-size: 12.5px; color: var(--gray-500, #6b7280); }
.rekey-note {
  margin: 14px 0 0; padding: 11px 13px;
  font-size: 13px; line-height: 1.55;
  color: var(--gray-700, #374151);
  background: var(--gray-50, #f9fafb);
  border-radius: var(--radius-sm, 6px);
  border-left: 3px solid var(--gray-300, #d1d5db);
}
.rekey-note.is-merge { border-left-color: var(--blue, #2563eb); }
/* Amber, not red: nothing here is an error, and the two cases it covers (a
   shared inbox contact, a meeting whose invite is already out) are both "know
   this before you continue" rather than "you cannot". */
.rekey-note.is-warn {
  border-left-color: var(--yellow, #d97706);
  background: #fffbeb;
  color: #78350f;
}
.rekey-confirm {
  display: flex; justify-content: flex-end; align-items: center; gap: 8px;
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--gray-100, #f3f4f6);
}

/* A value that is shown, not typed. Same box as the fields around it so the
   form still reads as one column, but visibly not a place to put a cursor —
   the complaint that produced the change-email screen was someone clicking
   into a field that looked editable and finding it inert, and an explanation
   underneath is only half the fix. */
/* The selector is doubled to out-specify `.sidebar-section input[type="text"]`,
   which sets a white background further up the file. */
.input-readonly,
.sidebar-section input[type="text"].input-readonly {
  background: var(--gray-50, #f9fafb);
  color: var(--gray-600, #4b5563);
  cursor: default;
}
.sidebar-section input[type="text"].input-readonly:focus {
  border-color: var(--gray-300, #d1d5db);
  box-shadow: none;
}
/* The action the read-only field hands off to. A link rather than a button
   because it opens a screen, and coloured because the default blue underline
   is the one link style nothing else in this panel uses. */
.field-action {
  color: var(--noble-primary, #55072b);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ══════════════════════════════════════════════════════════════════════════
   UI sweep (2026-09-14) — the controls that had no rule, and the states that
   snapped.

   Two problems, found by diffing every class used in a template or a JS string
   against every class this file defines, and every `:hover`/`:focus` rule
   against whether its base selector declares a transition:

     1. Controls rendering with browser-default chrome in the middle of a styled
        panel — five `.btn-*` variants with no rule at all, `input[type="time"]`
        and `input[type="file"]`, and native checkboxes wearing the OS accent
        instead of this panel's.
     2. 41 state rules in this file changing colour, border or shadow with no
        transition, so hover and focus arrived as a hard cut.

   Everything below is one of those two. It is appended rather than folded into
   the sections above so the change is reviewable as a block; the reduced-motion
   addendum at the very end is the counterpart to the motion added here.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Button variants ──
   `.btn-primary`, `.btn-secondary`, `.btn-danger`, `.btn-link` and
   `.btn-link-danger` are used on fourteen admin screens and NONE of them had a
   rule. The first three are always written as `btn btn-secondary`, so they all
   inherited `.btn` and every one of them rendered as the dark primary button:
   a Cancel looked exactly like the Save beside it, and — the reason this is a
   bug rather than a blemish — the five `.btn-danger` Delete/Cancel-handoff
   buttons were pixel-identical to the Save they sit next to. The two
   `.btn-link*` buttons had no `.btn` either and were carrying a hand-written
   `style=` attribute apiece to look like anything at all.

   ⚠️ `.btn-secondary`'s outline is a `box-shadow`, not a `border`: `.btn` sets
   `border: none` and pads to a fixed height, so a real border would make the
   one secondary button in a row 2px taller than its neighbours. box-shadow is
   already in `.btn`'s transition list, which is also why the three state rules
   below have to restate it — `.btn:hover`, `:active` and `:focus-visible` each
   set `box-shadow` and would otherwise wipe the outline off mid-interaction. */
.btn-primary { background: var(--noble-primary); color: #fff; }
.btn-primary:hover { background: var(--noble-dark); }

.btn-secondary {
  background: #fff;
  color: var(--gray-700);
  box-shadow: inset 0 0 0 1px var(--gray-300);
}
.btn-secondary:hover {
  background: var(--gray-50);
  color: var(--gray-900);
  box-shadow: inset 0 0 0 1px var(--gray-400), var(--shadow);
}
.btn-secondary:active { box-shadow: inset 0 0 0 1px var(--gray-400); }
.btn-secondary:focus-visible { box-shadow: inset 0 0 0 1px var(--gray-400), var(--ring); }

.btn-danger { background: var(--red); color: #fff; }
.btn-danger:hover { background: #b91c1c; }

/* The two link-shaped buttons (Delete a note, Complete a task, Archive a
   thread) on the contact page. Underlined on hover rather than permanently, so
   a row of them does not read as a paragraph of links, and given a real focus
   ring — as bare `<button>`s carrying inline styles they had none at all, which
   on a destructive action is the worst place to lose keyboard feedback. */
.btn-link,
.btn-link-danger {
  appearance: none;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--blue);
  cursor: pointer;
  border-radius: 3px;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: transparent;
  transition: color var(--dur-1) var(--ease-out),
              text-decoration-color var(--dur-1) var(--ease-out);
}
.btn-link-danger { color: var(--red); }
.btn-link:hover, .btn-link-danger:hover { text-decoration-color: currentColor; }
.btn-link:hover { color: #1d4ed8; }
.btn-link-danger:hover { color: #b91c1c; }
.btn-link:focus-visible, .btn-link-danger:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
  text-decoration-color: currentColor;
}

/* ── Native controls the panel never covered ──
   `.field` enumerates the input types it styles and the list was written before
   these existed on any screen, so a `type="time"` next to a styled mm/dd/yyyy
   field (contact tasks) rendered as raw browser chrome. Added here rather than
   edited into the list above so the omission stays visible. */
.field input[type="time"],
.field input[type="tel"],
.field input[type="url"],
.field input[type="password"] {
  width: 100%;
  padding: 8px 11px;
  font-size: 14px;
  font-family: inherit;
  color: var(--gray-800);
  background: white;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}

/* A bare `<input type="time">` anywhere in the panel. Scoped to `.admin-main`
   and left at single-class specificity on purpose: the Inbox and the Calls page
   render inside this same main element and both style their own time fields
   (`.task-due-time`, `.hours-day input[type="time"]`), and those rules out-rank
   this one, so this reaches exactly the fields nobody had claimed. */
.admin-main input[type="time"] {
  padding: 7px 10px;
  font: inherit;
  font-size: 13px;
  color: var(--gray-800);
  background: #fff;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.admin-main input[type="time"]:focus {
  border-color: var(--noble-primary);
  box-shadow: var(--ring);
}
.admin-main input[type="time"]:disabled {
  background: var(--gray-50);
  color: var(--gray-400);
  border-color: var(--gray-200);
}

/* A file input is the one control a browser draws entirely itself, and on eight
   screens (CSV imports, knowledge uploads, the evaluation harness) that meant a
   grey OS button sitting inside an otherwise finished form. Only the button
   half is restyled: the filename text beside it is the UA's and stays, because
   replacing the whole control means a label-and-hidden-input dance that then has
   to reimplement "no file chosen" in every place it is used. */
.admin-main input[type="file"] {
  font: inherit;
  font-size: 13px;
  color: var(--gray-600);
  max-width: 100%;
}
.admin-main input[type="file"]::file-selector-button {
  margin-right: 10px;
  padding: 7px 13px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--gray-100);
  color: var(--gray-700);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
.admin-main input[type="file"]::file-selector-button:hover {
  background: var(--gray-200);
  color: var(--gray-900);
}

/* Native checkboxes and radios, in the panel's colour rather than the OS blue.
   Three places already set this by hand (`.multiselect-option`, `.data-table`,
   `.dropdown-item`); this makes it the default so the next one does not have to
   remember. Inherited, so a component that wants its own — the Calls line
   pickers use green, the Inbox tasks blue — keeps it by setting `accent-color`
   on its own container, which is what those already do. */
.admin-main { accent-color: var(--noble-primary); }

/* ── Detail layouts ──
   `.detail-layout` and `.takeover-layout` are `1fr 280px` grids and their two
   children had no rule at all. A grid item defaults to `min-width: auto`, which
   means its widest unbreakable content — a wide table, a long unbroken URL in a
   transcript, an email body from somebody else's mail client — sets the floor
   for the `1fr` track and pushes the sidebar off the page. `min-width: 0` lets
   the track do its job and the content scroll or wrap inside it. */
.detail-main,
.detail-sidebar,
.takeover-layout > * { min-width: 0; }

/* ── Motion: the state rules that snapped ──
   Everything in this block already changed colour, border or shadow on hover or
   focus and did it in a single frame. They are grouped by what they transition
   rather than by where they live, so a new component can join the right list
   instead of inventing a fourth duration.

   Colour changes are NOT wrapped in `prefers-reduced-motion`: a 120ms tint is
   not motion, and dropping it there would leave those users with the hard cut
   this block exists to remove. What that preference does drop — travel and
   lift — is handled in the addendum at the end of the file. */
.login-card input,
.login-card button,
.sidebar-header .sidebar-brand,
.sidebar-footer a,
.back-link,
.clickable-row,
.conv-row,
.tag-pill-x,
.btn-ghost,
.btn-warning,
.btn-success,
.filter-form select,
.filter-form input,
.form-group input,
.form-group select,
.form-group textarea,
.tk-tool-ai,
.add-note-input,
.multiselect-btn,
.multiselect-option,
.filter-chip a,
.bulk-select,
.detail-sidebar-compact input[type="text"],
.detail-sidebar-compact select,
.detail-sidebar-compact textarea,
.app-chip,
.drawer-chips .category-select,
.conv-body a.course-card,
.rt-link-input,
.activity-undo,
.merge-option,
.queue-tab,
.classify-select,
.leads-table tbody tr td,
.filter-group input[type="date"],
.filter-custom input[type="date"],
.sidebar-section input[type="text"],
.copy-down,
.time-input,
.day-check,
.sched-close,
.sched-input,
.sched-slot,
.sched-mini,
.sched-btn,
.sched-tbtn {
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              opacity var(--dur-1) var(--ease-out);
}

/* The two that lift as well as tint, so they get the spring the buttons use. */
.segmented label,
.edgar-history summary,
.board-card {
  transition: background-color var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-spring);
}

/* ── The notification-scope menu in the sidebar ──
   A popover that appeared and vanished on a `hidden` attribute with nothing in
   between, and whose trigger had no hover or focus state at all — it was built
   entirely from a `style=` attribute in base.html. The look moves here so it can
   have states; the open/close borrows the leads `.dropdown-menu` pattern
   (`transition-behavior: allow-discrete` plus `@starting-style`), which is how
   every other menu in this panel animates. */
/* The wrapper the menu is positioned against. Both sidebar widgets (scope and
   sound) use it; `position: relative` was an inline style on one of them and
   absent from the other, which is the kind of thing that works until a second
   popover is added. */
.sidebar-notif-scope { position: relative; margin-top: 6px; }
.notif-sound-icon { display: flex; align-items: center; }

.notif-scope-summary {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  padding: 4px 8px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-sm);
  color: rgba(255, 255, 255, 0.85);
  font: inherit;
  font-size: 11px;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
.notif-scope-summary:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.28);
  color: #fff;
}
.notif-scope-summary:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.6);
  outline-offset: 1px;
}
.notif-scope-caret { margin-left: auto; opacity: 0.7; }

.notif-scope-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  max-height: 260px;
  padding: 8px 10px;
  overflow-y: auto;
  background: #1a1a2e;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  font-size: 12px;
  color: rgba(255, 255, 255, 0.9);
  opacity: 1;
  translate: 0 0;
  transition-property: opacity, translate, display;
  transition-duration: var(--dur-2);
  transition-timing-function: var(--ease-out);
  transition-behavior: allow-discrete;
}
@starting-style {
  .notif-scope-menu { opacity: 0; translate: 0 -6px; }
}
/* `display: none` stays, exactly as `.dropdown-menu[hidden]` above does it:
   `transition-behavior: allow-discrete` keeps the element painted for the
   duration of the close, and holding it at `opacity: 0` instead would leave a
   closed menu in the accessibility tree and in the tab order. */
.notif-scope-menu[hidden] {
  display: none;
  opacity: 0;
  translate: 0 -6px;
}
.notif-scope-title {
  margin-bottom: 4px;
  font-size: 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  opacity: 0.6;
}
.notif-scope-menu hr {
  margin: 8px 0;
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.notif-scope-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 0;
  cursor: pointer;
  border-radius: 4px;
  transition: color var(--dur-1) var(--ease-out);
}
.notif-scope-row:hover { color: #fff; }
/* A light accent, not the panel's maroon: this menu is a dark surface, and
   #55072b on #1a1a2e is a checked box you cannot tell from an unchecked one. */
.notif-scope-menu input[type="checkbox"] { accent-color: #fff; }
.notif-scope-all {
  display: block;
  margin-top: 8px;
  color: rgba(255, 255, 255, 0.65);
  font-size: 11px;
  text-decoration: none;
  transition: color var(--dur-1) var(--ease-out);
}
.notif-scope-all:hover { color: #fff; text-decoration: underline; }

/* ── Reduced motion, addendum ──
   Counterpart to the block above: the colour transitions stay (they are not
   motion), the travel and the lift go. */
@media (prefers-reduced-motion: reduce) {
  .board-card:hover,
  .segmented label:active,
  .edgar-history summary:hover { transform: none; }
  .notif-scope-menu, .notif-scope-menu[hidden] { translate: none; }
  @starting-style {
    .notif-scope-menu { translate: none; }
  }
}
