/* ═══════════════════════════════════════════════════════
   CATEO — Design System (Refined Dark)
   Brand-aligned: dark navy + warm orange
   ═══════════════════════════════════════════════════════ */

:root {
  /* Palette — Refined dark (not pitch-black, not bright) */
  --bg-0: #151924;          /* page background — deep navy */
  --bg-1: #1c2133;          /* cards, panels */
  --bg-2: #232840;          /* inputs, table headers, stat cards */
  --bg-3: #2c3350;          /* hover fills, code blocks */
  --bg-hover: #262e48;
  --border: #2e3654;
  --border-light: #3b4568;
  --text-0: #e8eaf0;        /* headings — warm white, not stark */
  --text-1: #b8bcc8;        /* body text */
  --text-2: #7d849a;        /* secondary / labels */
  --text-3: #545b73;        /* muted / captions */

  /* Accent — FBM warm orange (softened, less neon) */
  --accent: #e8762e;
  --accent-hover: #f08a48;
  --accent-bg: rgba(232, 118, 46, 0.08);
  --accent-border: rgba(232, 118, 46, 0.22);

  /* Semantic */
  --success: #34c785;
  --success-bg: rgba(52, 199, 133, 0.08);
  --warning: #e5a820;
  --warning-bg: rgba(229, 168, 32, 0.07);
  --danger: #e85656;
  --danger-bg: rgba(232, 86, 86, 0.07);
  --info: #5a9cf5;

  /* Sidebar — slightly darker than main */
  --sb-bg: #111520;
  --sb-bg-2: #1a1f30;
  --sb-border: #252c42;
  --sb-text-0: #e8eaf0;
  --sb-text-1: #b0b5c4;
  --sb-text-2: #717896;
  --sb-text-3: #4a506a;
  --sb-hover: #222840;

  /* Typography */
  --font: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;

  /* Layout */
  --sidebar-w: 240px;
  --sidebar-collapsed: 64px;
  --top-bar-h: 56px;
  /* The page gutter. Anything that has to line up with the page edge, or
     break back out to it, derives from these two — never hardcode them.
     .run-layout cancels them with negative margins. */
  --page-pad-x: 28px;
  --page-pad-y: 28px;
  /* How wide content may get. Raised in steps on big monitors further down;
     below ~1600px it never binds anyway, because the sidebar leaves less
     than this much room. */
  --content-max: 1280px;
  /* Comfortable line length for generated prose. */
  --measure: 78ch;
  /* A text field wider than this is just harder to use. Applied only on
     large screens so laptop layouts are untouched. */
  --field-max: 620px;
  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;

  /* Shadows — subtle on dark */
  --shadow-sm: 0 1px 4px rgba(0,0,0,.25);
  --shadow: 0 4px 16px rgba(0,0,0,.25);
  --shadow-lg: 0 8px 32px rgba(0,0,0,.3);
}

/* ── Reset ────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 15px; }
body { font-family: var(--font); background: var(--bg-0); color: var(--text-1); line-height: 1.5; -webkit-font-smoothing: antialiased; }
a { color: var(--accent); text-decoration: none; transition: color .15s; }
a:hover { color: var(--accent-hover); }
button { cursor: pointer; font-family: inherit; }
code { font-family: var(--font-mono); font-size: 0.87em; background: var(--bg-3); padding: 2px 6px; border-radius: 4px; }
hr { border: none; border-top: 1px solid var(--border); margin: 1.5rem 0; }

/* ── App Shell ────────────────────────────────────── */
.app-shell {
  display: flex;
  min-height: 100vh;
}

/* ── Sidebar (DARK) ──────────────────────────────── */
.sidebar {
  width: var(--sidebar-w);
  background: var(--sb-bg);
  border-right: 1px solid var(--sb-border);
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0; left: 0; bottom: 0;
  z-index: 100;
  transition: width .2s ease;
}
.sidebar.collapsed { width: var(--sidebar-collapsed); overflow-x: hidden; }
.sidebar.collapsed .logo-text,
.sidebar.collapsed .nav-item span,
.sidebar.collapsed .user-info,
.sidebar.collapsed .theme-toggle-label,
.sidebar.collapsed .theme-toggle-track,
.sidebar.collapsed .nav-divider::after,
.sidebar.collapsed .nav-section-label,
.sidebar.collapsed .client-switcher,
.sidebar.collapsed .user-name,
.sidebar.collapsed .user-role { display: none; }
.sidebar.collapsed .nav-item { justify-content: center; padding: 9px 0; position: relative; }
.sidebar.collapsed .nav-item svg { margin: 0; }
.sidebar.collapsed .nav-item.logout { justify-content: center; }
.sidebar.collapsed .sidebar-nav { padding: 12px 4px; }
.sidebar.collapsed .sidebar-footer { padding: 8px 4px; align-items: center; }
.sidebar.collapsed .user-badge { justify-content: center; padding: 6px 0; }
.sidebar.collapsed .theme-toggle { justify-content: center; padding: 7px 0; }
.sidebar.collapsed .sidebar-header { justify-content: center; padding: 16px 8px; flex-wrap: wrap; gap: 4px; }
.sidebar.collapsed .sidebar-toggle { padding: 4px; display: block !important; cursor: pointer; }
/* Tooltips on collapsed nav items */
.sidebar.collapsed .nav-item[data-tip]:hover::after {
  content: attr(data-tip);
  position: absolute; left: calc(100% + 8px); top: 50%; transform: translateY(-50%);
  background: var(--bg-3); color: var(--text-0); padding: 4px 10px; border-radius: 6px;
  font-size: 12px; white-space: nowrap; z-index: 999; pointer-events: none;
  box-shadow: 0 2px 8px rgba(0,0,0,.15);
}

.sidebar-header {
  padding: 16px;
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--sb-border);
  min-height: 56px;
}
.logo { display: flex; align-items: center; gap: 10px; }
.logo-icon-svg {
  width: 32px; height: 32px; flex-shrink: 0;
}
.logo-text { font-family: 'Oxanium', sans-serif; font-weight: 800; font-size: 17px; color: var(--sb-text-0); white-space: nowrap; letter-spacing: 2px; }
.sidebar-toggle {
  background: none; border: none; color: var(--sb-text-2);
  padding: 4px; border-radius: 6px; transition: background .15s;
}
.sidebar-toggle:hover { background: var(--sb-bg-2); color: var(--sb-text-0); }

.sidebar-nav {
  flex: 1; padding: 12px 8px; display: flex; flex-direction: column; gap: 2px;
  overflow-y: auto;
}
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: var(--radius-sm);
  color: var(--sb-text-2); font-size: 14px; font-weight: 500;
  transition: all .15s; white-space: nowrap;
}
.nav-item:hover { background: var(--sb-hover); color: var(--sb-text-0); }
.nav-item.active {
  background: rgba(232, 118, 46, 0.1); color: var(--accent);
  border: 1px solid rgba(232, 118, 46, 0.2);
}
.nav-item svg { flex-shrink: 0; }
.nav-divider {
  height: 1px; background: var(--sb-border); margin: 8px 12px;
  position: relative;
}
.nav-section-label {
  font-size: 10px; font-weight: 700; color: var(--sb-text-3);
  text-transform: uppercase; letter-spacing: .8px;
  padding: 4px 12px; margin-top: 4px;
}
.client-switcher {
  width: calc(100% - 16px); margin: 4px 8px;
  padding: 7px 10px; border-radius: var(--radius-sm);
  background: var(--sb-bg-2); border: 1px solid var(--sb-border);
  color: var(--sb-text-1); font-family: var(--font); font-size: 13px;
  cursor: pointer; transition: border-color .15s;
}
.client-switcher:hover { border-color: var(--sb-text-3); }
.client-switcher:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(232,118,46,.12); }
.client-switcher option { background: var(--sb-bg); color: var(--sb-text-1); }

.sidebar-footer {
  padding: 12px; border-top: 1px solid var(--sb-border);
  display: flex; flex-direction: column; gap: 8px;
}
.user-badge { display: flex; align-items: center; gap: 10px; padding: 6px; }
.user-avatar {
  width: 32px; height: 32px; background: var(--sb-bg-2);
  border-radius: 8px; display: grid; place-items: center;
  font-weight: 600; font-size: 13px; color: var(--accent);
  flex-shrink: 0;
}
.user-info { display: flex; flex-direction: column; overflow: hidden; }
.user-name { font-size: 13px; font-weight: 600; color: var(--sb-text-0); overflow: hidden; text-overflow: ellipsis; }
.user-role { font-size: 11px; color: var(--sb-text-3); text-transform: uppercase; letter-spacing: .5px; }
.nav-item.logout { color: var(--sb-text-3); font-size: 13px; }
.nav-item.logout:hover { color: var(--danger); background: rgba(232,86,86,.08); }

/* ── Staging banner ──────────────────────────────── */
/* Fixed, because .sidebar is fixed at top:0 and would otherwise cover it.
   z-index sits above the sidebar's 100. Both shells get padded down to match. */
.staging-banner {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 22px;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--warning);
  color: #14161a;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  user-select: none;
}
body.has-staging-banner .app-shell,
body.has-staging-banner .login-shell { padding-top: 22px; }
body.has-staging-banner .sidebar { top: 22px; }

/* ── Main Content ────────────────────────────────── */
.main-content {
  margin-left: var(--sidebar-w);
  flex: 1;
  /* A flex item defaults to min-width:auto and so refuses to shrink below its
     content's min-content width — one wide table was enough to push the whole
     page sideways. .run-main already does this; .main-content never did. */
  min-width: 0;
  min-height: 100vh;
  transition: margin-left .2s ease;
}
.sidebar.collapsed ~ .main-content { margin-left: var(--sidebar-collapsed); }

.top-bar {
  height: var(--top-bar-h);
  padding: 0;
  border-bottom: 1px solid var(--border);
  background: var(--bg-1);
  position: sticky; top: 0; z-index: 50;
}
/* Keeps the breadcrumbs and top actions aligned with the content column
   rather than hugging the far left edge of a wide monitor. */
.top-bar-inner {
  max-width: var(--content-max);
  margin-inline: auto;
  padding: 0 var(--page-pad-x);
  height: 100%;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
}
.breadcrumbs { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.crumb { color: var(--text-2); }
.crumb.current { color: var(--text-0); font-weight: 600; }
.crumb-sep { color: var(--text-3); }

.page-content {
  padding: var(--page-pad-y) var(--page-pad-x);
  max-width: var(--content-max);
  margin-inline: auto;
}

/* ── Cards ────────────────────────────────────────── */
.card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 20px;
  transition: border-color .15s, box-shadow .15s;
}
.card:hover { border-color: var(--border-light); }
.card-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 16px;
}
.card-title { font-size: 16px; font-weight: 600; color: var(--text-0); }
.card-subtitle { font-size: 13px; color: var(--text-2); margin-top: 2px; }

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

/* Client card */
.client-card { cursor: pointer; }
.client-card:hover { border-color: var(--accent-border); box-shadow: 0 0 0 1px var(--accent-border); }
.client-domain { font-family: var(--font-mono); font-size: 13px; color: var(--text-2); }
.client-meta { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-3); }
.client-meta .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--success); margin-right: 4px; display: inline-block; }

/* Stat cards */
.stat-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 24px; }
.stat-card {
  background: var(--bg-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px;
}
.stat-label { font-size: 12px; color: var(--text-3); text-transform: uppercase; letter-spacing: .5px; margin-bottom: 4px; }
.stat-value { font-size: 24px; font-weight: 700; color: var(--text-0); }
.stat-change { font-size: 12px; margin-top: 2px; }
.stat-change.up { color: var(--success); }
.stat-change.down { color: var(--danger); }

/* ── Tables ───────────────────────────────────────── */
.data-table {
  width: 100%; border-collapse: collapse; font-size: 13px;
}
.data-table th {
  background: var(--bg-2); color: var(--text-2);
  font-weight: 600; font-size: 11px;
  text-transform: uppercase; letter-spacing: .5px;
  padding: 10px 14px; text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.data-table td {
  padding: 10px 14px; border-bottom: 1px solid var(--border);
  color: var(--text-1); vertical-align: middle;
}
.data-table tbody tr:hover { background: var(--bg-hover); }
.data-table .num { text-align: right; font-family: var(--font-mono); font-size: 12px; }

/* ── Tabs ─────────────────────────────────────────── */
.tab-bar {
  display: flex; gap: 0; border-bottom: 1px solid var(--border);
  margin-bottom: 20px; overflow-x: auto;
}
.tab-item {
  padding: 10px 18px; font-size: 13px; font-weight: 500;
  color: var(--text-2); border-bottom: 2px solid transparent;
  transition: all .15s; white-space: nowrap;
}
.tab-item:hover { color: var(--text-0); }
.tab-item.active { color: var(--accent); border-bottom-color: var(--accent); }

/* ── Buttons ──────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 16px; border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 600;
  border: 1px solid transparent; transition: all .15s;
}
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); color: #fff; }
.btn-secondary { background: var(--bg-3); color: var(--text-1); border-color: var(--border); }
.btn-secondary:hover { background: var(--bg-hover); border-color: var(--border-light); color: var(--text-0); }
.btn-ghost { background: transparent; color: var(--text-2); }
.btn-ghost:hover { background: var(--bg-3); color: var(--text-0); }
.btn-danger { background: var(--danger-bg); color: var(--danger); border-color: rgba(232,86,86,.18); }
.btn-danger:hover { background: rgba(232,86,86,.12); }
.btn-sm { padding: 5px 10px; font-size: 12px; }
.btn-lg { padding: 12px 24px; font-size: 15px; }

/* ── Forms ────────────────────────────────────────── */
.form-group { margin-bottom: 16px; }
.form-label {
  display: block; font-size: 13px; font-weight: 500;
  color: var(--text-2); margin-bottom: 6px;
}
.form-input, .form-select, .form-textarea {
  width: 100%; padding: 10px 14px;
  background: var(--bg-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text-0);
  font-family: inherit; font-size: 14px;
  transition: border-color .15s;
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
.form-textarea { resize: vertical; min-height: 80px; }

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

/* ── Badges / Pills ───────────────────────────────── */
.badge {
  display: inline-flex; align-items: center;
  padding: 3px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 600;
  letter-spacing: .3px;
}
.badge-success { background: var(--success-bg); color: var(--success); }
.badge-warning { background: var(--warning-bg); color: var(--warning); }
.badge-danger { background: var(--danger-bg); color: var(--danger); }
.badge-info { background: rgba(90,156,245,.1); color: var(--info); }

/* ── AI Section ───────────────────────────────────── */
.ai-panel {
  background: var(--bg-2); border: 1px solid var(--accent-border);
  border-radius: var(--radius-lg); padding: 20px; margin-top: 16px;
}
/* Strip panel chrome when cards are inside — let cards stand on their own */
.ai-panel:has(.ai-cards) {
  background: transparent; border: none; padding: 20px 0;
}
.ai-panel-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px;
}
.ai-label {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; color: var(--accent);
}
.ai-content {
  font-size: 14px; line-height: 1.65; color: var(--text-1);
  max-width: var(--measure);
}
/* Fallback: if AI returns raw text (not cards), preserve whitespace */
.ai-content-raw { white-space: pre-wrap; }

/* ── AI Recommendation Cards ─────────────────────── */
.ai-cards { display: flex; flex-direction: column; gap: 12px; }
.ai-card {
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px 20px;
}
.ai-card-header { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.ai-card-title {
  font-size: 14px; font-weight: 600; color: var(--text-0);
  margin: 0 0 8px;
}
.ai-card-body {
  font-size: 13px; color: var(--text-1); line-height: 1.6; margin: 0 0 12px;
  max-width: var(--measure);
}
.ai-card-body:last-child { margin-bottom: 0; }
.ai-card-footer {
  background: var(--bg-2); border-radius: var(--radius-sm);
  padding: 10px 14px; font-size: 12px; color: var(--text-2); line-height: 1.5;
}
.ai-card-footer strong { color: var(--text-0); font-weight: 600; }

/* Severity badges */
.ai-badge {
  font-size: 11px; font-weight: 600; padding: 2px 10px;
  border-radius: var(--radius-sm); display: inline-block;
}
.ai-badge-critical { background: var(--danger-bg); color: var(--danger); }
.ai-badge-high { background: var(--warning-bg); color: var(--warning); }
.ai-badge-medium { background: rgba(90,156,245,.1); color: var(--info); }
.ai-badge-low { background: var(--success-bg); color: var(--success); }
.ai-badge-note { background: var(--bg-3); color: var(--text-2); }
.ai-badge-category {
  font-size: 11px; padding: 2px 10px; border-radius: var(--radius-sm);
  background: var(--bg-3); color: var(--text-2); font-weight: 500;
}

/* Before / After comparison grid */
.ai-compare { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
.ai-compare-box {
  border-radius: var(--radius-sm); padding: 10px 14px;
  border: 1px solid var(--border);
}
.ai-compare-box--current {}
.ai-compare-box--improved { border-color: rgba(90,156,245,.3); }
.ai-compare-label {
  font-size: 11px; font-weight: 600; margin: 0 0 4px;
}
.ai-compare-label--current { color: var(--danger); }
.ai-compare-label--improved { color: var(--success); }
.ai-compare-text { font-size: 12px; color: var(--text-1); line-height: 1.5; margin: 0; }
.ai-compare-meta { font-size: 11px; color: var(--text-3); margin: 4px 0 0; }

/* Responsive: stack comparison on narrow screens */
@media (max-width: 600px) {
  .ai-compare { grid-template-columns: 1fr; }
}

/* Embedded table inside cards */
.ai-card-table-wrap { overflow-x: auto; margin-bottom: 12px; }
.ai-card-table {
  width: 100%; border-collapse: collapse; font-size: 12px;
}
.ai-card-table th {
  background: var(--bg-2); font-weight: 600; color: var(--text-0);
  padding: 8px 12px; text-align: left; white-space: nowrap;
  border-bottom: 2px solid var(--border);
}
.ai-card-table td {
  padding: 8px 12px; color: var(--text-1);
  border-bottom: 1px solid var(--border);
}
.ai-card-table tbody tr:last-child td { border-bottom: none; }

/* Bullet / numbered list inside cards */
.ai-card-list {
  padding-left: 20px; margin: 0 0 12px; font-size: 13px;
  color: var(--text-1); line-height: 1.6;
}
.ai-card-list li { margin-bottom: 6px; }
.ai-card-list li:last-child { margin-bottom: 0; }
.ai-card-list li::marker { color: var(--accent); }

/* ── Spinner ──────────────────────────────────────── */
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
  width: 18px; height: 18px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .6s linear infinite;
}
.spinner-pill {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--accent-bg); border: 1px solid var(--accent-border);
  border-radius: 999px; padding: 6px 14px;
  font-size: 13px; color: var(--accent);
}
.spinner-pill[hidden] { display: none; }

/* ── Empty States ─────────────────────────────────── */
.empty-state {
  text-align: center; padding: 48px 24px; color: var(--text-3);
}
.empty-state svg { margin-bottom: 12px; opacity: .4; }
.empty-state p { font-size: 14px; margin-bottom: 16px; }

/* ── Login (stays dark + dramatic) ───────────────── */
.login-shell {
  min-height: 100vh; display: flex; align-items: stretch;
  background: var(--sb-bg);
}
.login-hero {
  flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center;
  padding: 60px 48px;
  position: relative; overflow: hidden;
  background:
    radial-gradient(ellipse at 30% 0%, rgba(232,118,46,.10) 0%, transparent 60%),
    radial-gradient(ellipse at 70% 100%, rgba(90,156,245,.06) 0%, transparent 60%),
    linear-gradient(135deg, #0c0f18 0%, #131826 100%);
}
.login-hero::before {
  content: ''; position: absolute; inset: 0;
  background:
    repeating-linear-gradient(90deg, rgba(232,118,46,.02) 0px, rgba(232,118,46,.02) 1px, transparent 1px, transparent 80px),
    repeating-linear-gradient(0deg, rgba(232,118,46,.02) 0px, rgba(232,118,46,.02) 1px, transparent 1px, transparent 80px);
  mask-image: radial-gradient(ellipse at center, black 30%, transparent 70%);
  -webkit-mask-image: radial-gradient(ellipse at center, black 30%, transparent 70%);
}
.login-hero-content {
  position: relative; z-index: 1; max-width: 520px; text-align: center;
}
.login-logo {
  display: flex; align-items: center; gap: 14px;
  justify-content: center; margin-bottom: 40px;
}
.login-logo-svg {
  height: 120px; width: auto;
  filter: drop-shadow(0 0 60px rgba(232,118,46,.30));
}
.login-logo-text {
  font-size: 28px; font-weight: 800; color: #e8eaf0;
  letter-spacing: -.02em;
}
.login-logo-text .accent { color: var(--accent); }
.login-headline {
  font-size: 40px; font-weight: 800; color: #e8eaf0;
  line-height: 1.15; margin-bottom: 16px;
  letter-spacing: -.03em;
}
.login-headline .accent-word {
  background: linear-gradient(135deg, var(--accent), #f0965a);
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
  background-clip: text;
}
.login-tagline {
  font-size: 17px; color: #7d849a; line-height: 1.6;
  margin-bottom: 40px; max-width: 420px; margin-left: auto; margin-right: auto;
}
.login-features {
  display: flex; gap: 32px; justify-content: center; flex-wrap: wrap;
  margin-bottom: 48px;
}
.login-feature {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: #7d849a; white-space: nowrap;
}
.login-feature-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px rgba(232,118,46,.35);
}
.google-btn {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 14px 32px;
  background: #fff; color: #333; font-weight: 600; font-size: 15px;
  border-radius: 12px; border: none;
  box-shadow: 0 4px 24px rgba(0,0,0,.12), 0 0 0 1px rgba(255,255,255,.08);
  transition: all .2s; text-decoration: none;
}
.google-btn:hover {
  box-shadow: 0 8px 32px rgba(0,0,0,.2), 0 0 0 1px rgba(255,255,255,.12);
  color: #111; transform: translateY(-1px);
}
.login-footer {
  position: relative; z-index: 1;
  margin-top: 48px; font-size: 12px; color: #545b73;
}
.login-footer a { color: var(--accent); text-decoration: none; }

/* Animated glow orbs */
.login-orb {
  position: absolute; border-radius: 50%; filter: blur(60px);
  animation: orb-float 8s ease-in-out infinite;
}
.login-orb-1 {
  width: 300px; height: 300px; top: -80px; right: -60px;
  background: rgba(232,118,46,.06);
}
.login-orb-2 {
  width: 250px; height: 250px; bottom: -60px; left: -40px;
  background: rgba(90,156,245,.05);
  animation-delay: -4s;
}
@keyframes orb-float {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(20px, -20px) scale(1.05); }
}

@media (max-width: 640px) {
  .login-hero { padding: 40px 24px; }
  .login-logo-svg { height: 80px; }
  .login-headline { font-size: 28px; }
  .login-tagline { font-size: 15px; }
  .login-features { gap: 16px; }
}

/* ── Toast ────────────────────────────────────────── */
#toast-container {
  position: fixed; bottom: 24px; right: 24px; z-index: 9999;
  display: flex; flex-direction: column; gap: 8px;
}
.toast {
  padding: 12px 20px; border-radius: var(--radius);
  font-size: 13px; font-weight: 500;
  box-shadow: var(--shadow); animation: slideIn .3s ease;
}
.toast-success { background: var(--bg-1); border-left: 3px solid var(--success); color: var(--text-0); }
.toast-error { background: var(--bg-1); border-left: 3px solid var(--danger); color: var(--text-0); }
@keyframes slideIn { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: none; } }

/* ── Responsive ───────────────────────────────────── */

/* Large screens.
   The old flat 1280px cap is why the app looked stranded on a big monitor:
   at 2560px it used 1520px of 2560 and left the rest empty. These steps let
   the content column grow, then stop it — a 3000px-wide data table is no
   more readable than a stranded one. Nothing here fires below 1600px,
   because under that the sidebar already leaves less room than the cap,
   so laptop layouts are untouched. */
@media (min-width: 1600px) {
  :root { --content-max: 1560px; --page-pad-x: 36px; --page-pad-y: 32px; }
  /* A text field the width of a 4K panel is worse, not better. Large
     screens only, so nothing shifts on a laptop. */
  .form-input, .form-select, .form-textarea, .ss-trigger { max-width: var(--field-max); }
  .form-row .form-input, .form-row .form-select, .form-row .form-textarea,
  .form-row .ss-trigger { max-width: none; }
}
@media (min-width: 1920px) {
  :root { --content-max: 1780px; --page-pad-x: 40px; }
}
@media (min-width: 2560px) {
  :root { --content-max: 2200px; --page-pad-x: 48px; --page-pad-y: 36px; }
}
@media (min-width: 3200px) {
  :root { --content-max: 2560px; }
}

/* Tablet: collapse sidebar to icon rail */
@media (max-width: 768px) {
  :root {
    --sidebar-w: var(--sidebar-collapsed);
    --page-pad-x: 16px;
    --page-pad-y: 16px;
  }
  .sidebar {
    width: var(--sidebar-collapsed) !important;
    overflow-x: hidden;
  }
  .sidebar .logo-text,
  .sidebar .nav-item span,
  .sidebar .user-info,
  .sidebar .theme-toggle-label,
  .sidebar .theme-toggle-track,
  .sidebar .nav-section-label,
  .sidebar .client-switcher,
  .sidebar .user-name,
  .sidebar .user-role { display: none !important; }
  .sidebar .nav-item { justify-content: center; padding: 9px 0; }
  .sidebar .nav-item svg { margin: 0; }
  .sidebar .nav-item.logout { justify-content: center; }
  .sidebar .sidebar-nav { padding: 12px 4px; }
  .sidebar .sidebar-footer { padding: 8px 4px; align-items: center; }
  .sidebar .user-badge { justify-content: center; padding: 6px 0; }
  .sidebar .theme-toggle { justify-content: center; padding: 7px 0; }
  .sidebar .sidebar-header { justify-content: center; padding: 16px 8px; }
  .sidebar .sidebar-toggle { display: block !important; }
  .main-content { margin-left: var(--sidebar-collapsed) !important; }
  .form-row { grid-template-columns: 1fr; }
  .stat-row { grid-template-columns: 1fr 1fr; }
}

/* Mobile: tighter spacing, stack everything */
@media (max-width: 480px) {
  :root { --page-pad-x: 12px; --page-pad-y: 12px; }
  .stat-row { grid-template-columns: 1fr; }
  .stat-card { padding: 12px; }
  .stat-value { font-size: 20px; }
  .page-title { font-size: 18px; }
  .page-subtitle { font-size: 13px; }
  .top-bar { height: auto; min-height: var(--top-bar-h); padding: 8px 0; }
  .top-bar-inner { height: auto; flex-wrap: wrap; gap: 8px; }
  .top-actions { width: 100%; }
  .top-actions > div { flex-wrap: wrap; width: 100%; }
  .top-actions .btn { flex: 1; min-width: 0; justify-content: center; font-size: 12px; padding: 6px 10px; }
  .breadcrumbs { font-size: 13px; }
  .card { padding: 12px; border-radius: var(--radius); }
  .card-grid { grid-template-columns: 1fr; }
  /* Make tables scroll horizontally */
  .data-table { font-size: 12px; }
  .data-table th { padding: 8px 10px; font-size: 10px; }
  .data-table td { padding: 8px 10px; }
  .btn { padding: 6px 12px; font-size: 12px; }
  .btn-sm { padding: 4px 8px; font-size: 11px; }
  .section-title { font-size: 15px; }
  /* Empty state more compact */
  .empty-state { padding: 32px 16px; }
}

/* ── Audit Run Sidebar Layout ─────────────────────── */
.run-layout {
  display: flex; gap: 0; min-height: calc(100vh - 80px);
  margin: calc(var(--page-pad-y) * -1) calc(var(--page-pad-x) * -1);
}
.run-sidebar {
  width: 220px; min-width: 220px;
  background: var(--bg-1);
  border-right: 1px solid var(--border);
  padding: 0; overflow-y: auto;
  position: sticky; top: 0; height: calc(100vh - 64px);
}
.run-sidebar-header {
  padding: 16px 16px 12px; border-bottom: 1px solid var(--border);
}
.run-sidebar-title {
  font-size: 14px; font-weight: 700; color: var(--text-0);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.run-sidebar-meta {
  font-size: 11px; color: var(--text-3); margin-top: 4px; line-height: 1.5;
}
.run-nav { padding: 8px 0; }
.run-nav-group { margin-bottom: 2px; }
.run-nav-divider {
  height: 1px; background: var(--border); margin: 4px 16px;
  opacity: .5;
}
.run-nav-group-label {
  padding: 8px 16px 4px; font-size: 11px; font-weight: 600;
  color: var(--text-3); text-transform: uppercase; letter-spacing: .04em;
}
.run-nav-item {
  display: block; padding: 6px 16px 6px 28px;
  font-size: 13px; color: var(--text-2);
  transition: all .12s; border-left: 2px solid transparent;
  text-decoration: none;
}
.run-nav-item:hover {
  color: var(--text-0); background: var(--bg-hover);
}
.run-nav-item.active {
  color: var(--accent); background: rgba(232,118,46,.05);
  border-left-color: var(--accent); font-weight: 600;
}
.run-sidebar-focus {
  padding: 8px 0; margin: 4px 0;
  border-top: 1px solid var(--border);
}
.focus-urls { padding: 0 16px; }
.focus-url {
  font-size: 11px; color: var(--text-3); padding: 2px 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.run-main {
  flex: 1; min-width: 0;
  padding: var(--page-pad-y) var(--page-pad-x);
}

/* Responsive: collapse sidebar on small screens */
@media (max-width: 900px) {
  .run-layout { flex-direction: column; }
  .run-sidebar {
    width: 100%; min-width: 100%; position: static;
    height: auto; border-right: none; border-bottom: 1px solid var(--border);
  }
  .run-nav { display: flex; flex-wrap: wrap; gap: 0; padding: 4px 8px; }
  .run-nav-group { display: contents; }
  .run-nav-group-label { display: none; }
  .run-nav-divider { display: none; }
  .run-nav-item { padding: 6px 12px; border-left: none; border-bottom: 2px solid transparent; }
  .run-nav-item.active { border-left-color: transparent; border-bottom-color: var(--accent); }
  .run-sidebar-focus { display: none; }
  .run-main { padding: 16px; }
}
@media (max-width: 480px) {
  .run-main { padding: 12px; }
  .run-nav-item { padding: 5px 8px; font-size: 11px; }
  .run-sidebar-header { padding: 10px 12px 8px; }
  .run-sidebar-title { font-size: 13px; }
}

/* ── Page Title ───────────────────────────────────── */
.page-title {
  font-size: 22px; font-weight: 700; color: var(--text-0);
  margin-bottom: 4px;
}
.page-subtitle { font-size: 14px; color: var(--text-2); margin-bottom: 20px; }

/* ── Section ──────────────────────────────────────── */
.section { margin-bottom: 32px; margin-top: 24px; }
.section-title {
  font-size: 16px; font-weight: 700; color: var(--text-0);
  margin-bottom: 12px; display: flex; align-items: center; gap: 8px;
}

/* Backlinks by page list */
.bl-page-item {
  display: block; padding: 10px 14px; border-bottom: 1px solid var(--border);
  text-decoration: none; color: var(--text-1); transition: background .15s;
}
.bl-page-item:hover { background: var(--bg-2); }
.bl-page-item.active { background: var(--accent-bg); border-left: 3px solid var(--accent); }
.bl-page-url { font-size: 13px; font-family: var(--mono); color: var(--text-1); margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bl-page-meta { display: flex; gap: 8px; align-items: center; font-size: 11px; color: var(--text-3); }

/* Rename input inline */
.rename-inline { display: inline-flex; gap: 6px; align-items: center; }
.rename-inline input { background: var(--bg-2); border: 1px solid var(--border); color: var(--text-1); padding: 4px 8px; border-radius: var(--radius-sm); font-size: 13px; width: 250px; }
.rename-inline button { font-size: 12px; }

/* Limit banners */
.limit-banner {
  padding: 10px 16px; margin-bottom: 16px; border-radius: var(--radius-sm);
  background: var(--warning-bg); border: 1px solid rgba(229,168,32,.18);
  color: var(--warning); font-size: 13px; line-height: 1.5;
}
.limit-banner a { color: var(--accent); text-decoration: underline; }
.stat-cap { font-size: 11px; color: var(--warning); margin-top: 4px; }

/* Sortable table headers */
.data-table thead th[style*="cursor"] { position: relative; }
.data-table thead th:hover { color: var(--accent); }
.sort-indicator { font-size: 10px; color: var(--accent); margin-left: 2px; }

/* Tool cards on dashboard */
.tool-card { transition: border-color .15s; }
.tool-card:hover { border-color: var(--accent-border); }
.tool-card a:hover { border-color: var(--accent-border); }

/* ── Focus Keyword Highlighting ──────────────────── */
.focus-kw-row {
  background: var(--accent-bg) !important;
  border-left: 3px solid var(--accent);
}
.focus-kw-row td:first-child { padding-left: 13px; }
.focus-kw-row code { color: var(--accent); font-weight: 600; }
[data-theme="light"] .focus-kw-row { background: rgba(214, 106, 36, 0.08) !important; }

/* ── Delete Toggle (hidden by default) ───────────── */
.delete-col { display: none; }
.delete-mode-active .delete-col { display: table-cell; }
.delete-mode-active thead .delete-col { display: table-cell; }
.btn-delete-toggle {
  background: var(--bg-3); color: var(--text-2); border: 1px solid var(--border);
  font-size: 12px; padding: 5px 12px; border-radius: var(--radius-sm);
  cursor: pointer; transition: all .15s; display: inline-flex; align-items: center; gap: 5px;
}
.btn-delete-toggle:hover { background: var(--danger-bg); color: var(--danger); border-color: rgba(232,86,86,.18); }
.btn-delete-toggle.active { background: var(--danger-bg); color: var(--danger); border-color: rgba(232,86,86,.25); }

/* ── Table mobile overflow ───────────────────────── */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Most tables sit in a .table-scroll, but a handful are dropped straight into
   a card. Those pushed the whole page sideways at narrow widths instead of
   scrolling themselves. Popovers are unaffected: the searchable-dropdown
   panel is mounted on <body>, so a scroll container here can't clip it. */
.card:has(> .data-table) { overflow-x: auto; }

/* ── Scrollbar ───────────────────────────────────── */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--border-light); }

/* ═══════════════════════════════════════════════════════
   LIGHT THEME OVERRIDE
   Sidebar stays dark navy for brand consistency.
   Main content area goes light.
   ═══════════════════════════════════════════════════════ */
[data-theme="light"] {
  /* Main area — warm light */
  --bg-0: #eef0f4;
  --bg-1: #ffffff;
  --bg-2: #f4f5f8;
  --bg-3: #e8eaef;
  --bg-hover: #eceef3;
  --border: #d4d8e1;
  --border-light: #bfc4d0;
  --text-0: #1a1f36;
  --text-1: #333b52;
  --text-2: #6b7489;
  --text-3: #8f96aa;

  /* Accent — slightly deeper for light bg readability */
  --accent: #d66a24;
  --accent-hover: #e47d38;
  --accent-bg: rgba(214, 106, 36, 0.06);
  --accent-border: rgba(214, 106, 36, 0.18);

  /* Semantic — richer for light bg */
  --success: #1a9e5c;
  --success-bg: rgba(26, 158, 92, 0.07);
  --warning: #c4900a;
  --warning-bg: rgba(196, 144, 10, 0.06);
  --danger: #d63a3a;
  --danger-bg: rgba(214, 58, 58, 0.06);
  --info: #3574d4;

  /* Shadows — more visible on light */
  --shadow-sm: 0 1px 3px rgba(0,0,0,.05);
  --shadow: 0 4px 12px rgba(0,0,0,.06);
  --shadow-lg: 0 8px 24px rgba(0,0,0,.08);
}

/* Light theme: cards get subtle shadow for depth */
[data-theme="light"] .card { box-shadow: var(--shadow-sm); }
[data-theme="light"] .card:hover { box-shadow: var(--shadow); }
[data-theme="light"] .stat-card { box-shadow: var(--shadow-sm); }
[data-theme="light"] .ai-panel { background: var(--bg-1); box-shadow: var(--shadow-sm); }
[data-theme="light"] .ai-panel:has(.ai-cards) { background: transparent; box-shadow: none; }
[data-theme="light"] .ai-card { background: #fff; box-shadow: var(--shadow-sm); }
[data-theme="light"] .ai-card:hover { box-shadow: var(--shadow); }
[data-theme="light"] .run-sidebar { background: var(--bg-1); }

/* Light theme: form inputs get white bg */
[data-theme="light"] .form-input,
[data-theme="light"] .form-select,
[data-theme="light"] .form-textarea { background: #fff; }

/* Light theme: limit banner text darker for readability */
[data-theme="light"] .limit-banner { color: #8a6b00; }

/* Light theme: scrollbar lighter */
[data-theme="light"] ::-webkit-scrollbar-thumb { background: #c8cdd6; }
[data-theme="light"] ::-webkit-scrollbar-thumb:hover { background: #b0b6c2; }

/* ── Theme Toggle ────────────────────────────────── */
.theme-toggle {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 12px; border-radius: var(--radius-sm);
  color: var(--sb-text-2); font-size: 13px; font-weight: 500;
  cursor: pointer; transition: all .15s;
  background: none; border: none; width: 100%;
}
.theme-toggle:hover { background: var(--sb-hover); color: var(--sb-text-0); }
.theme-toggle-track {
  width: 36px; height: 20px; border-radius: 10px;
  background: var(--sb-border); position: relative;
  transition: background .2s; flex-shrink: 0;
}
.theme-toggle-thumb {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--sb-text-2);
  position: absolute; top: 2px; left: 2px;
  transition: transform .2s, background .2s;
}
/* Active state: dark mode ON */
[data-theme="dark"] .theme-toggle-track { background: var(--accent); }
[data-theme="dark"] .theme-toggle-thumb {
  transform: translateX(16px);
  background: #fff;
}

/* ══════════════════════════════════════════════════ */
/*  Sales snapshot                                     */
/*  Only rendered on the sales deployment (IS_SALES).  */
/*  Built on the existing :root tokens so it inherits  */
/*  both themes without a second palette.              */
/* ══════════════════════════════════════════════════ */

.sales-banner { background: var(--info); color: #fff; }

/* ── Header ──────────────────────────────────────── */
.sales-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 24px;
}
.sales-head h1 { font-size: 26px; font-weight: 700; margin: 0 0 4px; color: var(--text-0); }
.sales-head-sub { margin: 0; font-size: 14px; color: var(--text-2); }
.sales-head-sub a { color: var(--accent); text-decoration: none; }
.sales-head-sub a:hover { text-decoration: underline; }
.sales-head-sub .dot { margin: 0 8px; color: var(--text-3); }

/* ── Form ────────────────────────────────────────── */
.sales-form-card { padding: 24px; }
.sales-form-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(3, 1fr);
}
.sales-form-grid .field-wide { grid-column: 1 / -1; }
.sales-form .field { display: flex; flex-direction: column; gap: 6px; }
.sales-form .field > span {
  font-size: 12px; font-weight: 600; color: var(--text-1);
  text-transform: uppercase; letter-spacing: .04em;
}
.sales-form .field em {
  font-style: normal; font-weight: 400; text-transform: none;
  letter-spacing: 0; color: var(--text-3);
}
/* Typed selector, so every type has to be listed: an API key field is
   type="password" and matched nothing here, rendering as a raw browser
   input. Same shape as .kw-dive targeting a class that never existed. */
.sales-form input[type="text"],
.sales-form input[type="password"] {
  background: var(--bg-0); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 11px 13px;
  color: var(--text-0); font-family: var(--font); font-size: 14px;
}
.sales-form input[type="text"]:focus,
.sales-form input[type="password"]:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
.sales-form-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-top: 20px; padding-top: 18px;
  border-top: 1px solid var(--border); flex-wrap: wrap;
}
.checkline { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--text-1); cursor: pointer; }
.checkline em { font-style: normal; color: var(--text-3); }
.sales-form-actions { display: flex; align-items: center; gap: 16px; }
.quota { font-size: 12px; color: var(--text-3); font-family: var(--font-mono); }
/* .btn-lg was redefined here, unscoped, and beat :354 app-wide on this branch
   -- the same shape as the .stat-row incident. It lives in the design layer
   now, scoped. */

/* ── Alerts ──────────────────────────────────────── */
.sales-alert {
  background: var(--danger-bg, rgba(232,86,86,.1));
  border: 1px solid var(--danger); color: var(--text-0);
  padding: 12px 16px; border-radius: var(--radius);
  font-size: 14px; margin-bottom: 20px;
}
.sales-alert-soft {
  background: var(--warning-bg, rgba(229,168,32,.1));
  border-color: var(--warning);
}
.sales-alert code { font-family: var(--font-mono); color: var(--accent); }

/* ── Progress reel ───────────────────────────────── */
.sales-progress {
  display: flex; align-items: center; gap: 14px;
  background: var(--bg-2); border: 1px solid var(--accent-border);
  border-radius: var(--radius); padding: 16px 20px; margin-bottom: 24px;
  font-size: 15px; color: var(--text-0);
}

/* ── Headline stats ────────────────────────────────
   Scoped to .stat-tile on purpose. These four rules were unscoped and sit
   later in the file than the generic .stat-row / .stat-label / .stat-value at
   the top, so they silently restyled every .stat-card page in the app —
   client_detail, ppq_campaigns, ppc_audit_detail, ppq_export, ppq_competitors
   and run_onpage_seo all got the 4-column grid and the 34px mono value. They
   also overrode the ≤768px and ≤480px rules, leaving .stat-row two-up at
   320px. Scoping restores those pages to the generic rules without changing
   anything the sales pages render. */
.stat-row:has(> .stat-tile) {
  display: grid; gap: 14px; margin-bottom: 24px;
  grid-template-columns: repeat(4, 1fr);
}
.stat-tile {
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px;
  display: flex; flex-direction: column; gap: 4px;
}
.stat-tile .stat-label {
  font-size: 11px; font-weight: 600; color: var(--text-2);
  text-transform: uppercase; letter-spacing: .06em;
}
.stat-tile .stat-value {
  font-size: 34px; font-weight: 700; line-height: 1.1;
  color: var(--text-0); font-family: var(--font-mono);
}
.stat-note { font-size: 12px; color: var(--text-3); }

/* ── The one big number ──────────────────────────── */
.sales-hero {
  text-align: center; padding: 36px 24px; margin-bottom: 24px;
  background: linear-gradient(160deg, var(--bg-2), var(--bg-1));
  border: 1px solid var(--accent-border);
}
.sales-hero-label {
  font-size: 11px; font-weight: 700; color: var(--accent);
  text-transform: uppercase; letter-spacing: .12em;
}
.sales-hero-number {
  font-size: 64px; font-weight: 700; line-height: 1;
  margin: 12px 0 10px; color: var(--text-0); font-family: var(--font-mono);
}
.sales-hero-text { margin: 0; font-size: 16px; color: var(--text-1); }
.sales-hero-text strong { color: var(--text-0); }

/* ── Comparison bars ─────────────────────────────── */
/* No chart library in this app and no build step, so these are plain
   divs. Widths are computed server-side against the highest score. */
.cmp { margin: 20px 0 24px; display: flex; flex-direction: column; gap: 10px; }
.cmp-row { display: grid; grid-template-columns: 200px 1fr 44px; align-items: center; gap: 14px; }
.cmp-name { font-size: 13px; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmp-name em { font-style: normal; color: var(--accent); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; margin-left: 4px; }
.cmp-bar { background: var(--bg-0); border-radius: 999px; height: 22px; overflow: hidden; }
.cmp-bar i { display: block; height: 100%; background: var(--text-3); border-radius: 999px; transition: width .5s ease; }
.cmp-you .cmp-name { color: var(--text-0); font-weight: 600; }
.cmp-you .cmp-bar i { background: var(--accent); }
.cmp-val { font-family: var(--font-mono); font-size: 14px; font-weight: 600; color: var(--text-0); text-align: right; }
.row-you { background: var(--accent-bg); }
.tag {
  font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--accent); border: 1px solid var(--accent-border);
  border-radius: 4px; padding: 1px 5px; margin-left: 6px;
}

/* ── Position pills ──────────────────────────────── */
.pos {
  display: inline-block; min-width: 26px; text-align: center;
  font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  padding: 2px 7px; border-radius: 999px;
}
.pos-good { background: var(--success-bg, rgba(52,199,133,.12)); color: var(--success); }
.pos-ok { background: var(--warning-bg, rgba(229,168,32,.12)); color: var(--warning); }
.pos-poor { background: var(--bg-3); color: var(--text-2); }

/* ── Site speed ──────────────────────────────────── */
.psi-row { display: flex; gap: 28px; align-items: center; flex-wrap: wrap; margin-top: 16px; }
.psi-shot {
  width: 190px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); box-shadow: var(--shadow);
}
.psi-scores { display: flex; gap: 32px; }
.psi-score { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.psi-num { font-size: 40px; font-weight: 700; font-family: var(--font-mono); line-height: 1; }
.psi-label { font-size: 11px; color: var(--text-2); text-transform: uppercase; letter-spacing: .06em; }
.psi-good { color: var(--success); }
.psi-ok { color: var(--warning); }
.psi-poor { color: var(--danger); }

/* ── Prospect list ───────────────────────────────── */
.sales-list { display: flex; flex-direction: column; gap: 8px; }
.sales-list-row {
  display: grid; grid-template-columns: 1fr auto auto auto;
  align-items: center; gap: 16px;
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 13px 18px;
}
.sales-list-row:hover { border-color: var(--border-light); background: var(--bg-hover); }
.sales-list-main { display: flex; flex-direction: column; gap: 2px; text-decoration: none; min-width: 0; }
.sales-list-company { font-size: 14px; font-weight: 600; color: var(--text-0); }
.sales-list-domain { font-size: 12px; color: var(--text-2); }
.sales-list-date { font-size: 12px; color: var(--text-3); font-family: var(--font-mono); white-space: nowrap; }
.btn-icon {
  background: none; border: none; cursor: pointer; padding: 5px;
  color: var(--text-3); border-radius: var(--radius-sm); display: flex;
}
.btn-icon:hover { color: var(--danger); background: var(--danger-bg, rgba(232,86,86,.1)); }
.pill-ok { background: var(--success-bg, rgba(52,199,133,.12)); color: var(--success); }
.pill-warn { background: var(--warning-bg, rgba(229,168,32,.12)); color: var(--warning); }
.pill-bad { background: var(--danger-bg, rgba(232,86,86,.12)); color: var(--danger); }
.section-sub { font-size: 13px; color: var(--text-2); margin: 2px 0 0; }
.page-sub { font-size: 14px; color: var(--text-2); margin: 4px 0 0; }
.muted { color: var(--text-3); }

/* ── Services: chips + scan ───────────────────────── */
/* The services field is optional, so it has to make the alternative obvious:
   type them, or read them off the prospect's site. Chips carry the selection;
   a hidden textarea carries the value, same mechanism as city_autocomplete. */

.sales-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.sales-chips:empty { display: none; }

.sales-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--accent); color: #fff;
  padding: 5px 6px 5px 12px; border-radius: 20px;
  font-size: 13px; font-weight: 500; line-height: 1.3;
}
.sales-chip-x {
  background: none; border: 0; cursor: pointer;
  color: #fff; opacity: .75;
  font-size: 15px; line-height: 1; padding: 0 4px;
  /* Never dim a control below the text it sits next to -- a faded control
     reads as disabled. See the excluded-keyword-row fix. */
}
.sales-chip-x:hover { opacity: 1; }

.sales-svc-row { display: flex; gap: 8px; align-items: stretch; }
.sales-svc-row .sales-svc-input { flex: 1; min-width: 0; }
.sales-svc-row .btn { white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; }

/* The scan button borrows .btn-ghost, which is borderless -- give it an edge
   so it reads as a control rather than as text. */
.sales-svc-row .btn-ghost {
  border: 1px solid var(--border);
  background: var(--bg-0);
}
.sales-svc-row .btn-ghost:hover:not(:disabled) { border-color: var(--accent); color: var(--text-0); }
.sales-svc-row .btn-ghost:disabled { opacity: .55; cursor: not-allowed; }

.sales-svc-spin {
  width: 12px; height: 12px; border-width: 2px; margin: 0; flex: none;
}

.sales-suggest {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  font-size: 12px; color: var(--text-3);
}
.sales-suggest-lead { flex: none; }
/* display:flex above beats the UA [hidden] rule, so the strip rendered its
   "Also found:" lead with an empty list on every page load. Same guard
   .spinner-pill already has. */
.sales-suggest[hidden] { display: none; }
.sales-suggest #svcSuggestList { display: contents; }

.sales-suggest-pill {
  background: var(--bg-2); border: 1px dashed var(--border);
  border-radius: 20px; padding: 4px 11px;
  color: var(--text-1); font-family: var(--font); font-size: 12px;
  cursor: pointer;
}
.sales-suggest-pill:hover { border-style: solid; border-color: var(--accent); color: var(--text-0); }

.sales-svc-help { font-size: 11px; color: var(--text-3); }

/* ── Responsive: reps demo on laptops, but check ─── */
@media (max-width: 1100px) {
  .stat-row:has(> .stat-tile) { grid-template-columns: repeat(2, 1fr); }
  .sales-form-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  /* Stack the add/scan controls rather than squeezing the input to nothing. */
  .sales-svc-row { flex-wrap: wrap; }
  .sales-svc-row .sales-svc-input { flex: 1 1 100%; }
}
@media (max-width: 720px) {
  .stat-row:has(> .stat-tile) { grid-template-columns: 1fr 1fr; }
  .sales-form-grid { grid-template-columns: 1fr; }
  .cmp-row { grid-template-columns: 120px 1fr 40px; gap: 10px; }
  .sales-hero-number { font-size: 46px; }
  .sales-list-row { grid-template-columns: 1fr auto; row-gap: 8px; }
}
/* ── Searchable dropdowns ─────────────────────────── */
/* Drives both the upgraded <select>s and the JS-fed comboboxes.
   Structural rules use .ss-*, so they beat the classes the trigger
   inherits from the original select. Cosmetic fallbacks use :where()
   so they have zero specificity and .form-input / .client-switcher
   always win — that's what keeps each dropdown looking like the field
   it replaced. */

.ss-wrap { position: relative; display: block; }

/* Never display:none — a hidden required select makes the browser
   refuse to submit with "not focusable". pointer-events:none lets
   clicks fall through to the trigger underneath. */
.ss-native {
  position: absolute; top: 0; left: 0;
  width: 100%; height: 100%;
  margin: 0; padding: 0; border: 0;
  opacity: 0; pointer-events: none;
}

.ss-trigger {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; text-align: left; cursor: pointer;
  font-family: inherit; font-size: inherit;
}
:where(.ss-trigger) {
  width: 100%; padding: 10px 14px;
  background: var(--bg-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text-0);
  font-size: 14px;
}
.ss-trigger-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ss-caret { flex: 0 0 auto; opacity: .5; }
.ss-wrap.ss-open .ss-trigger,
.ss-trigger:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}

.ss-panel {
  display: none;
  position: fixed;
  /* Above the sidebar (100) and both page modals (1000, 9999),
     below the toast container so toasts stay readable. */
  z-index: 9998;
  flex-direction: column;
  min-width: 180px;
  background: var(--bg-0);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  font-family: var(--font);
}
.ss-panel.ss-open { display: flex; }

.ss-search {
  flex: 0 0 auto;
  margin: 0; border: 0; border-bottom: 1px solid var(--border);
  padding: 9px 12px;
  background: var(--bg-1); color: var(--text-0);
  font-family: inherit; font-size: 13px;
}
.ss-search::placeholder { color: var(--text-3); }
.ss-search:focus { outline: none; border-bottom-color: var(--accent); }

.ss-list { flex: 1 1 auto; overflow-y: auto; min-height: 0; }

.ss-group {
  padding: 6px 12px;
  background: var(--bg-1); border-bottom: 1px solid var(--border);
  color: var(--text-3); font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em;
  position: sticky; top: 0;
}

.dd-opt {
  padding: 8px 12px; font-size: 12px; cursor: pointer;
  color: var(--text-1); border-bottom: 1px solid var(--border);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dd-opt:last-child { border-bottom: none; }
.dd-opt:hover, .dd-opt.ss-active { background: var(--bg-2); color: var(--text-0); }
.dd-opt.ss-selected { color: var(--accent); font-weight: 600; }
.dd-opt.ss-disabled { color: var(--text-3); cursor: default; }
.dd-opt.ss-disabled:hover { background: none; }

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

/* Excluded keyword rows read as excluded without dimming their controls —
   a 45%-opacity checkbox looks exactly like a disabled one, which is what
   made the Intent filter seem to disable selection. */
tr.is-excluded td:not(.cb-cell) { opacity: .45; }

/* ── Sales: city field ────────────────────────────── */
.sales-field-hint {
  display: block; margin-top: 5px;
  font-size: 11px; color: var(--text-3); line-height: 1.45;
}
/* A warning border, not a dimmed control — the field stays fully usable and
   the server is what actually rejects. See the opacity rule at the bottom
   of this file. */
.field input.is-warn { border-color: var(--warning); }
.field input.is-warn + .sales-field-hint { color: var(--warning); }

/* ── Sales: live SERP panel ───────────────────────── */
.serp-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 12px;
  margin-bottom: 4px;
}
.serp-q {
  font-family: var(--font-mono); font-size: 13px; color: var(--text-0);
  background: var(--bg-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 3px 9px;
}
.serp-where { font-size: 12px; color: var(--text-3); }

.serp-verdict {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; border-radius: var(--radius);
  border: 1px solid; margin: 14px 0 18px; font-size: 14px;
}
.serp-verdict b { font-weight: 600; }
.serp-miss { background: var(--danger-bg, rgba(232,86,86,.1)); border-color: rgba(232,86,86,.3); color: var(--text-1); }
.serp-hit  { background: var(--success-bg, rgba(52,199,133,.12)); border-color: rgba(52,199,133,.3); color: var(--text-1); }

.serp-list { display: flex; flex-direction: column; gap: 1px; }
.serp-row {
  display: grid; grid-template-columns: 34px 1fr; gap: 12px;
  padding: 10px 12px; background: var(--bg-1);
  border-left: 2px solid transparent;
}
.serp-row:nth-child(odd) { background: var(--bg-2); }
/* The prospect's own row, when they're in there at all. */
.serp-row.is-them { border-left-color: var(--accent); background: var(--accent-bg); }
.serp-pos {
  font-family: var(--font-mono); font-size: 13px; font-weight: 600;
  color: var(--text-3); text-align: right; padding-top: 1px;
}
.serp-row.is-them .serp-pos { color: var(--accent); }
.serp-title {
  font-size: 13px; color: var(--text-0); line-height: 1.4;
  margin: 0 0 2px; overflow-wrap: anywhere;
}
.serp-domain {
  font-family: var(--font-mono); font-size: 11px; color: var(--text-3);
  overflow-wrap: anywhere;
}
.serp-you-tag {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 3px;
  background: var(--accent); color: #fff; font-size: 10px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase; vertical-align: 1px;
}

.serp-pack { margin-top: 20px; }
.serp-pack-title {
  font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-3); margin-bottom: 8px;
}
.serp-pack-list { display: flex; flex-wrap: wrap; gap: 8px; }
.serp-pack-item {
  display: flex; align-items: baseline; gap: 7px;
  padding: 7px 12px; background: var(--bg-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 12.5px; color: var(--text-1);
}
.serp-pack-item.is-them { border-color: var(--accent); background: var(--accent-bg); }
.serp-pack-rating { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); }

@media (max-width: 600px) {
  .serp-row { grid-template-columns: 26px 1fr; gap: 9px; padding: 9px 8px; }
}

/* ── Sales: location + intent keywords ───────────── */
.geo-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.geo-item {
  display: flex; flex-direction: column; gap: 3px;
  padding: 12px 14px; background: var(--bg-2);
  border: 1px solid var(--border); border-left: 2px solid var(--accent);
  border-radius: var(--radius-sm);
}
.geo-kw { font-size: 13px; color: var(--text-0); line-height: 1.35; overflow-wrap: anywhere; }
.geo-vol {
  font-family: var(--font-mono); font-size: 17px; font-weight: 600;
  color: var(--accent); line-height: 1.2;
}
.geo-vol small { font-size: 11px; font-weight: 400; color: var(--text-3); margin-left: 2px; }
.geo-cpc { font-size: 11px; color: var(--text-3); }

/* ── Sales: keyword gap ──────────────────────────── */
.gap-comps {
  display: grid; gap: 12px; margin-bottom: 22px;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.gap-comp {
  display: flex; flex-direction: column;
  background: var(--bg-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px 16px;
}
.gap-comp-head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 4px 10px;
  padding-bottom: 9px; margin-bottom: 9px;
  border-bottom: 1px solid var(--border);
}
.gap-comp-domain {
  font-size: 13px; font-weight: 600; color: var(--text-0);
  overflow-wrap: anywhere;
}
.gap-comp-count { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); }
.gap-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.gap-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font-size: 12.5px;
}
.gap-kw { color: var(--text-1); overflow-wrap: anywhere; min-width: 0; }
.gap-meta { display: flex; align-items: baseline; gap: 7px; flex: none; }
.gap-vol { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); }
.gap-sub {
  font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-3); margin: 0 0 10px;
}
.gap-who { font-size: 11px; color: var(--text-3); overflow-wrap: anywhere; }

/* ── Content idea clusters (IS step 5) ───────────── */
.cic-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px;
  margin: 0 0 12px;
}
.cic-title { font-size: 15px; font-weight: 600; color: var(--text-0); margin: 0; }
.cic-note { font-size: 12px; color: var(--text-3); }
.cic-grid {
  display: grid; gap: 12px; margin-bottom: 16px;
  grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
}
/* Cards in a grid row stretch to the tallest; without the flex column the
   "+N more" line floats mid-card with dead space under it. */
.cic-card {
  display: flex; flex-direction: column;
  background: var(--bg-1); border: 1px solid var(--border);
  border-left: 3px solid var(--success);
  border-radius: var(--radius); padding: 14px 16px;
}
.cic-card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 8px;
}
.cic-name {
  font-size: 13.5px; font-weight: 600; color: var(--text-0);
  margin: 0; line-height: 1.3; overflow-wrap: anywhere;
}
.cic-vol {
  font-family: var(--font-mono); font-size: 14px; font-weight: 600;
  color: var(--success); white-space: nowrap; flex: none;
}
.cic-vol small { font-size: 10px; font-weight: 400; color: var(--text-3); }
.cic-meta { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 11px; }
.cic-tag {
  font-size: 10px; padding: 2px 7px; border-radius: 3px;
  background: var(--bg-3); color: var(--text-2); white-space: nowrap;
}
.cic-intent { background: var(--accent-bg); color: var(--accent); }
.cic-count { color: var(--text-3); }
.cic-kws {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 5px;
}
.cic-kws li {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; font-size: 12px;
}
.cic-kw { color: var(--text-1); overflow-wrap: anywhere; min-width: 0; }
.cic-kw-vol {
  font-family: var(--font-mono); font-size: 11px; color: var(--text-3); flex: none;
}
.cic-more {
  margin: auto 0 0; padding-top: 9px;
  font-size: 11px; color: var(--text-3);
}
.cic-unclustered {
  font-size: 12px; color: var(--warning);
  margin: 0 0 14px; padding: 8px 12px;
  background: var(--warning-bg, rgba(229,168,32,.1));
  border-radius: var(--radius-sm);
}

/* ═══════════════════════════════════════════════════════
   Sales report — the seven-part presentation
   /sales/{id}/report. Shown on a shared screen with a prospect
   watching, so it's wider-spaced and larger-typed than the rest
   of the app, which is an operator tool.
   ═══════════════════════════════════════════════════════ */
.rp-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 32px; padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
}
.rp-eyebrow {
  font-size: 11px; font-weight: 700; color: var(--accent);
  text-transform: uppercase; letter-spacing: .14em;
}
.rp-head h1 { font-size: 30px; font-weight: 700; margin: 6px 0 4px; color: var(--text-0); }
.rp-head-sub { margin: 0; font-size: 14px; color: var(--text-2); }
.rp-head-sub a { color: var(--accent); }
.rp-head-sub .dot { margin: 0 8px; color: var(--text-3); }
.rp-head-actions { display: flex; gap: 8px; flex-shrink: 0; }

/* Rep-only. Never meant for the prospect — a screen-shared "AI unavailable"
   notice is the failure this class exists to hide.

   Hidden by default, on screen and in print, and revealed only while the rep
   holds the Notes toggle open. It used to be hidden in the @media print block
   alone, which got it exactly backwards: absent from the PDF nobody was
   worried about, and fully visible on the shared screen it was written to stay
   off. Default-hidden means the worst case is a rep missing a diagnostic
   rather than a prospect reading one. */
.rep-only { display: none; }
html.show-notes .rep-only { display: block; }

.rp-repnote {
  font-size: 12px; color: var(--text-3); margin-bottom: 20px;
  padding: 8px 12px; border-left: 2px solid var(--border-light);
  background: var(--bg-2); border-radius: var(--radius-sm);
}

.rp-section { position: relative; margin: 0 0 44px; padding-top: 8px; }
.rp-num {
  display: block; font-family: var(--font-mono); font-size: 11px;
  font-weight: 600; color: var(--accent); letter-spacing: .1em; margin-bottom: 6px;
}
.rp-title { font-size: 24px; font-weight: 700; color: var(--text-0); margin: 0 0 6px; }
.rp-sub { font-size: 13px; color: var(--text-2); margin: 0 0 16px; max-width: var(--measure); }
.rp-sub-h { font-size: 15px; font-weight: 600; color: var(--text-0); margin: 28px 0 6px; }
.rp-lede {
  font-size: 20px; font-weight: 600; color: var(--text-0);
  margin: 4px 0 10px; max-width: var(--measure); line-height: 1.35;
}
.rp-body { font-size: 15px; color: var(--text-1); margin: 0 0 18px; max-width: var(--measure); }
.rp-note { font-size: 13px; color: var(--text-3); margin: 12px 0 0; }
.rp-stats { margin-top: 20px; }
.rp-serp { margin-top: 24px; }
.rp-hero { margin-top: 4px; }

/* Metrics — what each number means, not more numbers. */
.rp-meaning { display: flex; flex-direction: column; gap: 2px; }
.rp-meaning-row {
  display: grid; grid-template-columns: 220px 1fr; gap: 18px;
  padding: 14px 0; border-bottom: 1px solid var(--border);
}
.rp-meaning-row:last-child { border-bottom: 0; }
.rp-meaning-k {
  font-family: var(--font-mono); font-size: 14px; font-weight: 600; color: var(--text-0);
}
.rp-meaning-v { font-size: 14px; color: var(--text-1); line-height: 1.55; }

.gap-comp-note { font-size: 12px; color: var(--text-2); margin: 6px 0 0; line-height: 1.5; }

/* Recommended strategy */
.rp-pillars { display: flex; flex-direction: column; gap: 12px; }
.rp-pillar {
  display: grid; grid-template-columns: 34px 1fr; gap: 14px; align-items: start;
  background: var(--bg-1); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius); padding: 16px 18px;
}
.rp-pillar-n {
  font-family: var(--font-mono); font-size: 18px; font-weight: 700; color: var(--accent);
}
.rp-pillar-t { font-size: 16px; font-weight: 600; color: var(--text-0); margin: 0 0 4px; }
.rp-pillar-w { font-size: 14px; color: var(--text-1); margin: 0 0 8px; line-height: 1.55; }
.rp-pillar-m { font-size: 13px; color: var(--text-2); margin: 0; }
.rp-pillar-m span {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--accent); margin-right: 6px;
}

/* Outlook */
.rp-horizons {
  display: grid; gap: 12px; margin-top: 18px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.rp-horizon {
  background: var(--bg-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px;
}
.rp-horizon-w {
  display: block; font-size: 11px; font-weight: 700; color: var(--accent);
  text-transform: uppercase; letter-spacing: .08em; margin-bottom: 6px;
}
.rp-horizon-e { font-size: 13px; color: var(--text-1); margin: 0; line-height: 1.55; }

.rp-foot {
  margin-top: 48px; padding-top: 18px; border-top: 1px solid var(--border);
  font-size: 12px; color: var(--text-3);
}
.rp-foot p { margin: 0; }

@media (max-width: 720px) {
  .rp-head { flex-direction: column; }
  .rp-head h1 { font-size: 24px; }
  .rp-title { font-size: 20px; }
  .rp-lede { font-size: 17px; }
  .rp-meaning-row { grid-template-columns: 1fr; gap: 4px; }
}

/* ── Motion ───────────────────────────────────────────
   Cory's brief allows randomised/dynamic values for PRESENTATION only and
   forbids altering real data. So the server renders every number as text and
   the JS animates toward the value it finds in the DOM — nothing here computes
   a figure, and with JS off or in print the numbers are already correct.
   The stagger is a deterministic index, not Math.random: a rep re-showing the
   same prospect should see the same thing, not a reshuffle. */
.rp-reveal { opacity: 1; }
/* The class goes on <html> from an inline head script, not on <body> from
   app.js: setting it after the body paints gives a visible flash of the
   content before it hides itself again. Nothing is hidden unless that script
   ran, so with JS off every element is simply visible. */
html.rp-animate .rp-reveal {
  opacity: 0; transform: translateY(10px);
  animation: rp-rise .5s cubic-bezier(.22,.61,.36,1) forwards;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes rp-rise { to { opacity: 1; transform: none; } }
html.rp-animate .cmp-bar i[data-bar] { width: 0 !important; }
html.rp-animate .cmp-bar i[data-bar].is-grown { width: var(--w) !important; }

@media (prefers-reduced-motion: reduce) {
  html.rp-animate .rp-reveal { opacity: 1; transform: none; animation: none; }
  html.rp-animate .cmp-bar i[data-bar] { width: var(--w) !important; }
  .cmp-bar i { transition: none; }
}

/* ── Stepped presentation ─────────────────────────────
   The rail itself lives in components/sales_step_nav.html with its own
   <style>, matching is_step_nav and ppq_step_nav. Only the pager and the
   whole-step empty state are here, because they're layout rather than
   component chrome. Greped before naming: .sales-pager and .rp-empty* were
   unused. */
.sales-pager {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin-top: 40px; padding-top: 20px;
  border-top: 1px solid var(--border);
}

/* A step whose lookups didn't come back. Deliberately quiet — this is read by
   a prospect, and "we didn't measure it" must not look like an error state or
   like a finding about their business. */
.rp-empty {
  padding: 28px; border: 1px dashed var(--border);
  border-radius: var(--radius); background: var(--bg-1);
  max-width: var(--measure);
}
.rp-empty-t { font-size: 15px; font-weight: 600; color: var(--text-1); margin: 0 0 6px; }
.rp-empty-b { font-size: 14px; color: var(--text-2); margin: 0; line-height: 1.55; }

/* ── Print / leave-behind PDF ─────────────────────────
   No PDF library in this app and none added. The report is already a stack of
   self-contained cards, so the browser does the job. Forced to the light
   palette because a dark background is unreadable on paper and wastes toner. */
@media print {
  :root {
    --bg-0: #fff; --bg-1: #fff; --bg-2: #f6f6f8; --bg-3: #eee;
    --border: #ccd; --border-light: #bbc;
    --text-0: #111; --text-1: #333; --text-2: #555; --text-3: #777;
    --accent: #b4551a; --accent-bg: #fbf3ee; --accent-border: #e0c3ad;
  }
  .sidebar, .top-bar, .rp-head-actions, .rep-only,
  .sales-steps, .sales-pager,
  .toast-container, .theme-toggle { display: none !important; }
  .main-content { margin-left: 0 !important; }
  .page-content { max-width: none !important; padding: 0 !important; }
  body { background: #fff !important; }
  .rp-section { break-inside: avoid; page-break-inside: avoid; margin-bottom: 28px; }
  .rp-pillar, .rp-horizon, .stat-tile, .cic-card, .geo-item, .gap-comp {
    break-inside: avoid; page-break-inside: avoid;
  }
  .rp-reveal { opacity: 1 !important; transform: none !important; animation: none !important; }
  /* --w is only ever set by the same script that adds .rp-animate, so this
     can't fall back to an empty value. */
  html.rp-animate .cmp-bar i[data-bar] { width: var(--w) !important; }
  a { text-decoration: none; color: inherit; }
}

/* ── Fixes to existing sales CSS ──────────────────────
   sales_snapshot.html used a .table-wrap class that was defined nowhere, so
   those four tables overflowed instead of scrolling — and because the table
   sits one level deep, the .card:has(> .data-table) fallback above didn't
   match either. Fixed in the template by using .table-scroll, which is what
   the rest of the app uses. .pill had only its three colour modifiers and no
   base, so the status chip on /sales rendered as bare coloured text. */
.pill {
  display: inline-flex; align-items: center; padding: 3px 9px;
  border-radius: 999px; font-size: 11px; font-weight: 600; letter-spacing: .3px;
}

/* ══════════════════════════════════════════════════════════════════════════
   SALES DESIGN LAYER  ("Daylight")                             2026-09-04
   ──────────────────────────────────────────────────────────────────────────
   Everything below is scoped to `body.app-sales`, which base.html emits only
   when IS_SALES. Three reasons it is built this way rather than by editing
   the tokens at the top of this file:

   1. `sales` has diverged from `main` by 1218 lines here with ZERO
      deletions. That is the only reason the catch-up merges auto-resolve.
      Editing :root would put a permanent conflict in the path of every
      future merge from main.

      The invariant is "touch nothing main also has" — lines 1-983 and
      1258-1343. Everything else is sales-added, so editing a line *inside*
      it is still an addition and the deletions column stays at 0. Inside
      the layer, edit freely; `git diff --numstat main -- <this file>` is
      the check.
   2. The six sales pips render production's own templates — there are nine
      `is_sales` gates in the whole template tree. There is no sales template
      tree to restyle, so the scope has to come from CSS.
   3. Prod and staging never load `app-sales`, so their render is provably
      untouched.

   The palette targets WCAG AA (4.5:1 text, 3:1 non-text) in BOTH themes.
   Every ratio in the comments was computed, not estimated; the checker lives
   in wiki/concepts/verification-practice.md.
   ══════════════════════════════════════════════════════════════════════════ */

/* Wrapped in @media screen so the @media print block near the end of this
   file keeps its own paper palette. `body.app-sales` (0,0,1,1) would
   otherwise outrank print's `:root` (0,0,1,0) and print a dark page. */
@media screen {

  /* ── Tokens: dark ──────────────────────────────────────────────────────
     The app boots `<html data-theme="dark">`, so this is the default a rep
     sees until they flip the toggle. Neutral rather than navy, so the page
     and the nav read as one surface — the split is what makes the current
     app look like two apps stitched together.
     Worst text pair: --text-3 on --bg-3, 4.59:1.                          */
  body.app-sales {
    --bg-0:  #101218;   /* page                                            */
    --bg-1:  #171a22;   /* cards, panels                                   */
    --bg-2:  #1d2029;   /* inputs, table headers                           */
    --bg-3:  #262a35;   /* hover fills                                     */
    --bg-hover: #21252f;

    --border:       #272b35;   /* decorative: card edges, row dividers     */
    --border-light: #6b7382;   /* CONTROL boundaries — 3.41:1 worst.       */

    --text-0: #f0f1f4;  /* 15.40:1 on --bg-1                               */
    --text-1: #c9ccd4;  /* 10.83:1                                         */
    --text-2: #9aa0ad;  /*  6.63:1                                         */
    --text-3: #8b92a1;  /*  5.57:1 on --bg-1, 4.59:1 on --bg-3             */

    --accent:        #e8863c;  /* 6.55:1 on --bg-1                         */
    --accent-hover:  #f29a56;
    --accent-bg:     rgba(232,134,60,.14);
    --accent-border: rgba(232,134,60,.34);

    --success: #4bd396;  --success-bg: rgba(75,211,150,.12);
    --warning: #f0b73a;  --warning-bg: rgba(240,183,58,.12);
    --danger:  #f47070;  --danger-bg:  rgba(244,112,112,.12);
    --info:    #6fa8ff;

    /* Ink that sits ON an accent or success FILL. White fails badly here —
       #fff on --accent is 2.66:1 and on --success 1.90:1 — so dark ink is
       the only correct choice in this theme. See the pip numerals below. */
    --on-fill: #16181f;
  }

  /* ── Tokens: light ─────────────────────────────────────────────────────
     The grounds are CATEO's own, not a warm paper of this layer's
     invention. The first pass shipped #faf9f7 / #f2f1ee / #eae8e3 — cream —
     and cream on an orange accent is the single commonest signature of
     generated design, which is exactly what this branch was asked to get
     rid of. So the four ground tokens and the decorative border are the
     values [data-theme="light"] already sets at :902, verbatim.

     What this layer still changes, and why it is not just deleted: the
     stock light theme's INK fails AA badly (--text-3 2.45-2.95:1,
     --accent 3.10:1, --warning 2.51:1). Grounds from stock, ink from here.
     Worst text pair now: --text-3 on --bg-3, 4.85:1.                      */
  [data-theme="light"] body.app-sales {
    --bg-0:  #eef0f4;   /* page   — stock :904                             */
    --bg-1:  #ffffff;   /* cards  — stock :905                             */
    --bg-2:  #f4f5f8;   /* inputs — stock :906                             */
    --bg-3:  #e8eaef;   /* hover  — stock :907                             */
    --bg-hover: #eceef3;

    --border:       #d4d8e1;   /* decorative — stock :909                  */
    --border-light: #7a8294;   /* CONTROL boundaries — 3.20:1 worst, on
                                  --bg-3. Stock's #bfc4d0 is 1.45:1 and
                                  identifies no input; this is the same
                                  split as dark. WCAG 1.4.11.             */

    --text-0: #1c1e26;  /* 14.57:1 on --bg-0, 16.63:1 on --bg-1            */
    --text-1: #3d4250;  /*  8.79:1 / 10.03:1                               */
    --text-2: #5c6172;  /*  5.40:1 /  6.16:1                               */
    --text-3: #5f6574;  /*  5.11:1 / 5.83:1, 4.85:1 on --bg-3              */

    --accent:        #a34c16;  /* 5.11:1 on --bg-0, 5.83:1 on --bg-1       */
    --accent-hover:  #843c11;  /* NOT the print block's #b4551a — that one  */
    --accent-bg:     rgba(163,76,22,.10);   /* fails on a tinted ground.   */
    --accent-border: rgba(163,76,22,.30);

    --success: #0f6d3f;  --success-bg: rgba(15,109,63,.10);
    --warning: #7d5b06;  --warning-bg: rgba(125,91,6,.10);
    --danger:  #b02b2b;  --danger-bg:  rgba(176,43,43,.09);
    --info:    #2f6ec8;

    --on-fill: #ffffff;  /* 5.83:1 on --accent, 6.40:1 on --success        */
  }

  /* The sidebar avatar takes `color: var(--accent)` from :196 against
     `--sb-bg-2` — the page accent on the nav ground, which is the exact
     mistake --sb-accent exists to prevent. 2.76:1 in light on every page of
     the box; it passes in dark only because the dark accent happens to be
     light enough. The rail numerals were given --sb-accent by the palette
     commit and this one was missed. 6.05:1 after, in both themes.         */
  body.app-sales .user-avatar { color: var(--sb-accent); }

  /* ── The nav, constant across both themes ──────────────────────────────
     Deliberately NOT re-declared in the light block: the rail is the same
     near-black in either theme, which is what makes it read as an anchor
     rather than as a leftover.

     This is not the current bug repeated. Today the sidebar is navy only
     because [data-theme="light"] never redefines these eight tokens, so it
     inherits dark-theme values by accident — and --sb-text-3 lands at
     2.30:1, the second-worst ratio in the app. Here every value is chosen
     against #16181f on purpose.

     Note --sb-text-3 is folded into --sb-text-2's value: there is no
     legitimate use for a third, dimmer step that cannot pass.              */
  body.app-sales {
    --sb-bg:     #16181f;
    --sb-bg-2:   #1e212a;
    --sb-border: #31353f;
    --sb-hover:  #23262f;
    --sb-text-0: #f2f2f4;  /* 15.86:1 */
    --sb-text-1: #c8cad2;  /* 10.84:1 */
    --sb-text-2: #9096a4;  /*  5.98:1 */
    --sb-text-3: #9096a4;  /*  5.98:1 — was #4a506a at 2.30:1 */

    /* The page accent is unusable on the rail: #ad5118 is 3.35:1 on
       near-black. The rail carries its own, at 6.67:1. One --accent cannot
       serve both grounds — that is the mistake this stylesheet makes today
       in the other direction. */
    --sb-accent: #e8863c;
  }

  /* ── Control boundaries ────────────────────────────────────────────────
     WCAG 1.4.11 wants 3:1 on anything that identifies a control's edge.
     --border is 1.25:1 on today's light page and identifies no input at
     all, so every text field in the app currently fails this. Controls take
     --border-light; decorative dividers keep --border.                     */
  body.app-sales .form-input,
  body.app-sales .form-select,
  body.app-sales .form-textarea,
  body.app-sales .ss-trigger,
  body.app-sales .btn-secondary,
  body.app-sales .btn-icon {
    border-color: var(--border-light);
  }

  /* ── Focus ─────────────────────────────────────────────────────────────
     The app has 12 `outline: none` declarations against a single
     :focus-visible, and replaces the outline with a 3px ring of --accent-bg
     — which in the current light theme is 6% opacity, i.e. invisible.
     A real ring, on top of whatever the base rule does.                    */
  body.app-sales :is(input, select, textarea, button, a, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* ── Fills that carry ink ──────────────────────────────────────────────
     The 6-pip rail's completed numeral is white on --success: 3.45:1 in the
     current light theme and 2.18:1 in dark, the worst ratio in the app. It
     sits on the component a prospect looks at most. These four rules are
     the whole fix; --on-fill is white in light and near-black in dark.

     Specificity note: the rail's own rules live in an inline <style> inside
     components/sales_step_nav.html, which the browser applies AFTER this
     linked sheet. (0,0,4,1) here beats their (0,0,3,0).                    */
  body.app-sales .sales-step.active .sales-step-num,
  body.app-sales .sales-step.done   .sales-step-num { color: var(--on-fill); }
  body.app-sales .sales-chip,
  body.app-sales .serp-you-tag                      { color: var(--on-fill); }

  /* ── The environment banner ────────────────────────────────────────────
     Was white on --info: 2.80:1 in dark. It also said "demo environment",
     which is not true — a rep is doing real research on a real prospect,
     often with that prospect watching. Reworded in base.html; recoloured
     onto the brand here instead of a blue that belongs to nothing.

     Constant across both themes, like the nav below it and for the same
     reason: it sits directly above the rail, and a warm cream strip across
     the top of a cool white page was the most visible piece of the paper
     this branch has now dropped. One value, one ground, one ratio.         */
  body.app-sales .sales-banner {
    background: #16181f;                   /* = --sb-bg */
    color: #e8863c;                        /* 6.67:1, = --sb-accent */
    border-bottom: 1px solid #31353f;
  }

}

/* ── Sales: the text layer ─────────────────────────────────────────────────
   Section headings, a type floor, and a badge system. Same scoping rule as
   the block above: nothing here is reachable without body.app-sales.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  /* Badge tints. Solid saturated pills with white text were three separate AA
     failures — #8b5cf6 at 4.23:1, #3b82f6 at 3.68:1, #6366f1 at 4.47:1, all
     carrying 9px text. Tinted ground plus coloured text fixes the contrast and
     drops the look that reads as generated. Every pair below is >= 5.93:1. */
  body.app-sales {
    --tag-neutral-bg:#262a35; --tag-neutral-fg:#c9ccd4;  /* 8.92:1 */
    --tag-accent-bg: #33261c; --tag-accent-fg: #f0a065;  /* 6.91:1 */
    --tag-ok-bg:     #17352a; --tag-ok-fg:     #6cdcaa;  /* 7.89:1 */
    --tag-warn-bg:   #332c17; --tag-warn-fg:   #f0c464;  /* 8.45:1 */
    --tag-info-bg:   #1c2839; --tag-info-fg:   #8ab8ff;  /* 7.35:1 */
    --tag-purple-bg: #26213a; --tag-purple-fg: #b39aee;  /* 6.45:1 */
  }
  [data-theme="light"] body.app-sales {
    --tag-neutral-bg:#e9ebf0; --tag-neutral-fg:#4a4f5e;  /* 6.85:1 */
    --tag-accent-bg: #f7e9df; --tag-accent-fg: #8f4313;  /* 5.93:1 */
    --tag-ok-bg:     #e2f0e8; --tag-ok-fg:     #0d5c35;  /* 6.87:1 */
    --tag-warn-bg:   #f5eddb; --tag-warn-fg:   #6b4e05;  /* 6.63:1 */
    --tag-info-bg:   #e4ecf8; --tag-info-fg:   #24528f;  /* 6.59:1 */
    --tag-purple-bg: #eee9f8; --tag-purple-fg: #5b3a9e;  /* 6.94:1 */
  }

  /* The base .badge in this stylesheet declares no background and no colour —
     only its three modifiers do. So `class="badge"` with nothing else renders
     as bare text, which is how the Related and Manual source badges have been
     shipping. Give the base a real appearance.

     !important is load-bearing here and it is the cost of not forking
     production's templates: these badges carry `style="font-size:9px;
     background:...;color:..."` inline, on the six pips that render prod's own
     is_keywords / is_strategy / is_discovery_body / is_clusters_body. An
     inline attribute beats any selector, so a scoped rule cannot reach them
     otherwise. The alternative was editing those templates, which would ship
     to 18 strategists on prod. */
  body.app-sales .badge {
    font-size: 11px !important;          /* was 9px on 19 badges */
    padding: 3px 9px;
    background: var(--tag-neutral-bg) !important;
    color: var(--tag-neutral-fg) !important;
    border: none;
  }

  /* Variants keyed on the value the template hardcodes inline. Ugly, and
     deliberately so — it is a marker that these belong in the template as
     semantic classes. If the badge colours are ever tokenised at source,
     delete this block. */
  body.app-sales .badge[style*="--accent"]  { background: var(--tag-accent-bg)  !important; color: var(--tag-accent-fg)  !important; }
  body.app-sales .badge[style*="--success"] { background: var(--tag-ok-bg)      !important; color: var(--tag-ok-fg)      !important; }
  body.app-sales .badge[style*="--warning"] { background: var(--tag-warn-bg)    !important; color: var(--tag-warn-fg)    !important; }
  body.app-sales .badge[style*="#3b82f6"],
  body.app-sales .badge.badge-info          { background: var(--tag-info-bg)    !important; color: var(--tag-info-fg)    !important; }
  body.app-sales .badge[style*="#7c3aed"],
  body.app-sales .badge[style*="#8b5cf6"]   { background: var(--tag-purple-bg)  !important; color: var(--tag-purple-fg)  !important; }

  /* The two keyword markers live in an inline <style> block rather than an
     attribute, so they need no !important — just a token and a readable size.
     Both were indigo/amber literals that belong to nothing in the palette. */
  body.app-sales .kw-deeper {
    font-size: 10px;                     /* was 8px, the smallest type in the app */
    background: var(--tag-info-bg); color: var(--tag-info-fg);
    border-color: var(--tag-info-fg);
  }
  body.app-sales .kw-negative {
    font-size: 10px;                     /* was 9px */
    background: var(--tag-warn-bg); color: var(--tag-warn-fg);
    border-color: var(--tag-warn-fg);
  }
  body.app-sales tr[data-dive]:not([data-dive="0"]) td:first-child {
    box-shadow: inset 3px 0 0 var(--tag-info-fg);
  }

  /* .merged-h's override lived here and is gone.
     Both merged pips are phase strips plus collapsing sections now
     (components/is_phase_strip.html), so nothing in the template tree carries
     that class and the rule matched nothing. Removed on the instruction this
     block's own comment left: a rule that matches nothing is worse than no
     rule, because it reads as already handled — which is how .kw-dive
     survived four days. Checked with grep across app/templates. */

  /* Page heading hierarchy. The title and its subtitle were 22px/14px with
     the subtitle at --text-2; on a wizard the step line is what orients you,
     so it gets weight rather than being a caption. */
  body.app-sales .page-title    { font-size: 26px; letter-spacing: -.015em; }
  body.app-sales .page-subtitle { font-size: 14px; font-weight: 500; color: var(--text-2); }
  body.app-sales .section-title { font-size: 17px; letter-spacing: -.01em; }

}

/* ── Sales: surfaces and row density ───────────────────────────────────────
   Everything here came out of rendering /market locally and looking at it,
   rather than reading the source. Same scoping rule as the blocks above.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  /* Column-group washes on the metrics table. These shipped as
     rgba(99,102,241,.06) and rgba(34,197,94,.06) — Tailwind's indigo-500 and
     green-500, in an app with no Tailwind. Against warm paper they read as a
     cool cast across the widest table on the page, which is what made the
     section look like it belonged to a different product.

     Two neutral washes instead, still distinguishable as domain-level vs
     page-level. Dark separates them by hue (warm / cool); light separates
     them by depth, both cool — a warm wash was the widest single band of
     cream on the box, on the widest table on the page. Every text colour
     that lands on them clears 4.87:1. */
  body.app-sales {
    --grp-a: #2a2119;   /* domain metrics */
    --grp-b: #1b2029;   /* page metrics   */
  }
  [data-theme="light"] body.app-sales {
    --grp-a: #e6ebf3;   /* deeper  — text-3 4.87:1, text-2 5.15:1          */
    --grp-b: #f4f5f8;   /* lighter — text-3 5.35:1, text-2 5.65:1          */
  }
  body.app-sales .m-table thead .m-group-domain { background: var(--grp-a); }
  body.app-sales .m-table thead .m-group-page   { background: var(--grp-b); }
  body.app-sales .m-table td.m-as               { background: var(--grp-a); }
  body.app-sales .m-table td.m-url-as           { background: var(--grp-b); }

  /* Row actions on the competitors table.

     Rendered, this is the page's real density problem: ~10 SERP rows per
     keyword-and-city bucket, two icon buttons per unblocked row. On a real
     prospect that is roughly 150 icon buttons standing at full strength, and
     because the ACTION cell is narrow they wrap onto two lines, so each pair
     also costs a row of height.

     Laid out side by side and held back until the row is touched. They stay
     in the tab order — focus-within reveals them — so this is a change to
     visual noise, not to what a keyboard can reach. pointer-events is what
     stops an invisible button being clickable. */
  body.app-sales .comp-row td:last-child {
    white-space: nowrap;
    text-align: right;
  }
  body.app-sales .comp-row .btn-icon {
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
  }
  body.app-sales .comp-row:hover .btn-icon,
  body.app-sales .comp-row:focus-within .btn-icon {
    opacity: 1;
    pointer-events: auto;
  }
  /* Touch and coarse pointers get no hover, so never hide them there. */
  @media (hover: none) {
    body.app-sales .comp-row .btn-icon { opacity: 1; pointer-events: auto; }
  }
  /* .btn-icon is defined twice — style.css:1164 and the template's own
     <style> — with different padding and colour, the template's winning
     because it loads later. Not resolving that here, just making the target
     big enough to hit: 5px padding on a 14px glyph is a 24px target. */
  body.app-sales .btn-icon {
    min-width: 26px;
    min-height: 26px;
    align-items: center;
    justify-content: center;
  }

}

/* ── Sales: the Keywords pip ───────────────────────────────────────────────
   From rendering pip 2 locally. Nothing on this page had been looked at —
   the three blocks above came out of /market, which shares almost none of
   its classes. Same scoping rule as always.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  /* The provider marker beside the source badge — D4S / SEM. It was the one
     8px element the text layer missed: 10 of them on a real prospect's page,
     with a 1px --border edge measuring 1.25:1 on the light ground, so the
     chip read as a grey smudge rather than a label. Same treatment as
     .kw-negative and .kw-deeper get above. */
  body.app-sales .kw-prov {
    font-size: 10px;                     /* was 8px */
    background: var(--tag-neutral-bg);
    color: var(--tag-neutral-fg);        /* 8.92:1 dark / 6.91:1 light */
    border-color: var(--border-light);
  }

  /* An excluded keyword row was dimmed with `opacity: .45` on every cell but
     the checkbox (style.css:1346, shared). That drops the keyword itself to
     2.32:1 and its "Negative: <term>" badge to 2.07:1 — the two worst text
     pairs left anywhere on the sales box, and on rows a rep deliberately
     scans to explain what got dropped and why.

     Opacity is the wrong tool for "unavailable" in this stylesheet's own
     convention. Dim with a token and mark the row with a ground instead, so
     the state survives at full contrast. The unticked checkbox is still the
     non-colour signal.

     The inline `style="color:var(--success)"` on the difficulty and rank
     cells still wins inside these rows, which is correct — a difficulty of
     12 is true whether or not the keyword is included. */
  body.app-sales tr.is-excluded td { background: var(--bg-2); }
  body.app-sales tr.is-excluded td:not(.cb-cell) {
    opacity: 1;
    color: var(--text-3);                /* 4.59:1 dark / 4.76:1 light */
  }

}

/* ── Sales: the primary button, and the type floor the layer couldn't reach ──
   From rendering pips 5 and 6. The first rule here is the worst ratio left on
   the box; the second is the only thing on these pages still under the floor.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  /* .btn-primary is `background: var(--accent); color: #fff` (style.css:345),
     with the same #fff repeated on :hover. In dark that is white on #e8863c —
     2.66:1, on the main call to action of every single pip: Continue to
     Competitors, Continue to Strategy, Research Keywords, Add.

     This is the completed-pip numeral all over again. --on-fill exists for
     exactly this and already carries the ink that works on an accent fill in
     either theme; the palette commit just never applied it past the rail.

       dark  rest 6.67:1   hover 8.05:1
       light rest 5.83:1   hover 7.97:1   (unchanged — --on-fill is #fff there)

     No !important needed: the shared rule hardcodes #fff at class specificity,
     so a scoped selector beats it. */
  body.app-sales .btn-primary,
  body.app-sales .btn-primary:hover { color: var(--on-fill); }

  /* The 11px floor from the text commit reaches everything that carries a
     class. It cannot reach `style="font-size:9px"`, which production's
     templates set on 19 elements across these pips — the Keywords / Volume /
     Avg KD labels under every cluster card (21 on a real strategy) and the
     service label under every target keyword.

     Keyed on the inline value, like the badge rules above, and ugly for the
     same reason: an inline attribute beats any selector. It deletes itself the
     day those sizes come from a type scale instead of an attribute. Harmless
     where it doubles up with the .badge rule — both land on 11px. */
  body.app-sales [style*="font-size:9px"] { font-size: 11px !important; }

}

/* ── Sales: the Setup pip ──────────────────────────────────────────────────
   From rendering pip 1. Two more instances of the white-on-accent-fill bug
   the palette commit fixed for the rail and the chips and then stopped — both
   in component <style> blocks rather than this file, which is why they were
   missed. Same token, same 2.66:1 -> 6.67:1 in dark.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  /* The dream-location chips (components/city_autocomplete.html:57). The x is
     rgba(255,255,255,.7) there, which is white ink AND opacity doing the work
     of a colour — it takes --on-fill at full strength instead. A control that
     removes a market the rep just typed should be legible, not faded. */
  body.app-sales .city-tag { color: var(--on-fill); }
  body.app-sales .city-tag-x,
  body.app-sales .city-tag-x:hover { color: var(--on-fill); }

  /* The GBP / City mode toggle on each office row
     (components/office_locations.html:62). 10px on a control the rep clicks to
     decide whether a location gets competitor discovery at all — the same
     11px floor the badges got, and readable ink on the active fill. */
  body.app-sales .office-mode-btn { font-size: 11px; }
  body.app-sales .office-mode-btn.active { color: var(--on-fill); }

  /* "Remove this office" renders as a raw OS button — grey bevel, black x,
     2px outset border, identical in both themes, so it's a white box on a
     dark page. Not a palette miss: .btn-icon-x has no rule in this file at
     all. It's defined in the <style> blocks of is_metrics_body.html and
     is_clusters_body.html, i.e. pips 3 and 4, and office_locations.html uses
     the class on pip 1 while only setting flex-shrink on it. A component
     inheriting its appearance from whichever page happens to include it.

     Same shape as the .kw-dive rule fixed earlier on this branch, inverted:
     there a rule pointed at a class nobody used, here a class points at a
     rule that isn't there. Values copied from the two page definitions so
     the button looks the same on every pip; those pages keep their own,
     which this outranks with identical values. Live on prod as well. */
  body.app-sales .btn-icon-x {
    background: none; border: none; cursor: pointer;
    color: var(--text-3); font-size: 16px; font-weight: 600;
    padding: 2px 6px; border-radius: 4px; line-height: 1;
  }
  body.app-sales .btn-icon-x:hover { background: var(--bg-2); color: var(--danger); }

}

/* ── Sales: the Content Ideas & Clusters pip ───────────────────────────────
   From rendering pip 4, which had never been looked at — the notes listed
   pips 1, 2, 5 and 6 as unrendered and said only /market had been reviewed,
   and pip 4 fell between those two statements. The layer targeted none of
   its classes before this block.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  /* The "Jump to cluster" chips carry two 8px tags, both of them the
     white-ink-on-a-fill bug again, both defined in is_clusters_body.html's
     own <style> so the badge rules never reached them:

       .toc-tag      white on --accent   2.66:1 dark
       .toc-tag-blog white on #3b82f6    3.68:1 in BOTH themes

     #3b82f6 is one of the three fills the text commit already named as an AA
     failure; it just also lives here. Both take the semantic tag scale — the
     same tints the badges got — at the 10px marker size the other markers on
     this branch use. */
  body.app-sales .toc-tag {
    font-size: 10px;                     /* was 8px */
    background: var(--tag-accent-bg);
    color: var(--tag-accent-fg);         /* 6.91:1 dark / 5.93:1 light */
  }
  body.app-sales .toc-tag-blog {
    background: var(--tag-info-bg);
    color: var(--tag-info-fg);           /* 7.35:1 dark / 6.59:1 light */
  }

  /* And the chip's own tint is rgba(232,118,46,.06) — the pre-redesign
     accent, hardcoded. --accent-bg is the same idea in the current palette.
     (is_baseline.html:32 has the same literal; that one is inline, so it
     can't be reached from here and is logged instead.) */
  body.app-sales .toc-primary { background: var(--accent-bg); }

  /* Every cluster header carries a Priority and a Primary-keyword select —
     22 of them on a real prospect — and both set `border:1px solid
     var(--border)` in an inline attribute. --border is the DECORATIVE token:
     1.20:1 light, 1.15:1 dark, so neither control has a visible edge. This
     is the split the palette commit made (--border for dividers,
     --border-light for control boundaries, WCAG 1.4.11) reaching the two
     controls it missed, which it missed because they are styled inline.

     Superseded by the buttons-and-controls block at the end of this file:
     these two were the first of twelve inline-bordered controls found, and
     one attribute-keyed rule now reaches all twelve. Four of the other ten
     have no class, so they could not have been reached this way. */

  /* City is a 90px column holding "San Antonio, TX", so it wrapped to two
     lines on every keyword row of every cluster — 66 rows on a real
     prospect, each paying a line of height for it. Same defect as the
     competitor row buttons on pip 3: the CSS is fine, the cell is narrow,
     and nothing in either file says so.

     nth-child(3) rather than a class because the cell has none, and the
     column order is fixed in the template for both tables (checkbox,
     Keyword, City, ...). Scoped to the two cards that hold them so it can't
     reach another pip's tables. */
  body.app-sales .cluster-card .data-table td:nth-child(3),
  body.app-sales #unclustered .data-table td:nth-child(3) { white-space: nowrap; }

}

/* ── Sales: buttons and controls ───────────────────────────────────────────
   The layer had never touched a button beyond .btn-primary's ink, so every
   control on the box was stock. Three things follow from the new grounds:

   - .btn-secondary fills with --bg-3, which on a white card is a grey slab
     competing with the primary button beside it. An outline reads as the
     same control and recedes properly.
   - .btn-ghost's --text-2 is the meta token; on a control it is the least
     legible thing in an action bar. --text-1 is +63% contrast for free.
   - twelve inputs and selects across five pips set `border:1px solid
     var(--border)` in an inline attribute -- the DECORATIVE token, 1.20:1
     light and 1.15:1 dark, so none of them has a visible edge. The palette
     commit's --border/--border-light split never reached them, and the
     2026-09-08 pass reached two of the twelve by class. Four of the twelve
     carry no class at all, so an attribute hook is not a shortcut here, it
     is the only thing that can reach them.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  /* One radius for controls, one for surfaces. --radius-sm is already 6px;
     stating it here means the layer owns the value rather than inheriting a
     number that could move. */
  body.app-sales .btn { border-radius: var(--radius-sm); }

  /* Outline, not a fill. Border is --border-light because this IS the edge
     that identifies the control -- WCAG 1.4.11, 3.20:1 worst. */
  body.app-sales .btn-secondary {
    background: transparent;
    color: var(--text-1);                /* 8.79:1 light / 10.83:1 dark */
    border-color: var(--border-light);
  }
  body.app-sales .btn-secondary:hover {
    background: var(--bg-2);
    border-color: var(--border-light);
    color: var(--text-0);
  }

  /* Ghost stays borderless -- 14 of them sit in the action bars on pip 2 and
     giving each an edge would be noise. It gets ink instead. */
  body.app-sales .btn-ghost { color: var(--text-1); }
  body.app-sales .btn-ghost:hover { background: var(--bg-3); color: var(--text-0); }

  /* The value the deleted :1040 duplicate carried, scoped. Two reachable
     users on this box: is_form.html:166 and sales_home.html:76. */
  body.app-sales .btn-lg { padding: 11px 22px; font-size: 14px; }

  /* .btn-icon and .btn-icon-x are the same component drawn two ways -- one
     defined in this file, one only in two page <style> blocks. Same box, so
     a row of them lines up and each is a 28px target. */
  body.app-sales .btn-icon,
  body.app-sales .btn-icon-x {
    min-width: 28px; min-height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--radius-sm);
  }

  /* The twelve inline-bordered controls. Keyed on the declaration, not on a
     layout value -- a selector like [style*="margin-bottom:5px"] is how
     .kw-dive happened. Element-qualified because 40 non-control elements
     repo-wide carry the same declaration and are decorative panels, not
     controls; this must not reach them.

     Supersedes the .priority-select/.pk-select pair from 2026-09-08: same
     fix, ten more controls, one rule. Still one !important, same trade as
     the badges -- an inline attribute beats any selector. */
  body.app-sales :is(input, select, textarea)[style*="solid var(--border)"] {
    border-color: var(--border-light) !important;
  }

  /* The prospect form's own controls take --border from :1022 and :1207 --
     same decorative-token mistake, but declared in a rule rather than an
     attribute, so no !important is needed. This is the first screen a rep
     opens and the only one they type into. */
  body.app-sales .sales-form input[type="text"],
  body.app-sales .sales-form input[type="password"],
  body.app-sales .sales-svc-row .btn-ghost { border-color: var(--border-light); }

  /* One more of the same, found rendering a populated competitors table:
     .add-url-input (is_competitors_body.html:400, one per keyword bucket)
     takes --border from the partial's own <style>. A rule, not an attribute,
     so no !important -- the layer just outranks it. */
  body.app-sales .add-url-input { border-color: var(--border-light); }

  /* And the opacity trap again, in the place the 2026-09-08 sweep didn't
     reach. `.blocked-row td { opacity: .7 }` (is_competitors_body.html:287)
     dims a blocked competitor's whole row, dropping its domain to 3.01:1
     light / 3.24:1 dark on top of the row's own amber tint. Identical shape
     to `tr.is-excluded { opacity: .45 }` on pip 2, which put a keyword at
     2.32:1 -- and the same fix: say the muted colour instead of dimming the
     result. The tint still marks the row.

     Prod has this one too (it is on main, unscoped, since 3a477903). */
  body.app-sales .blocked-row td { opacity: 1; color: var(--text-2); }

  /* And the THIRD instance, which this layer has been logging as unreachable
     since 2026-09-08: `opacity:.65` inline on a competitor row that isn't
     included (is_competitors_body.html:105).

     It went unmeasured because pip 3's finished page opened Discover, so the
     Competitors table was collapsed whenever a sweep ran. Opening the FIRST
     phase instead put it on screen by default and the sweep found it at once:
     the excluded row's URL at 2.79:1 light, its title 2.87:1, its domain link
     3.73:1, its position cell 2.87:1. Prod has it too.

     Opacity cannot be tuned out of this: --text-3 is 4.85:1 at full strength,
     so any multiplier low enough to read as de-emphasis lands under 4.5. That
     is the whole reason the project rule is "say the muted colour instead".
     So the row goes back to opacity 1 and the bg-2 tint marks it, exactly as
     tr.is-excluded does on pip 2 -- and the unticked checkbox already says
     "excluded" without dimming anything.

     Keyed on the presence of an inline opacity rather than the literal .65,
     and element-qualified to tr.comp-row so it cannot reach anything else. A
     value-keyed selector is how .kw-dive came to match nothing. Blocked rows
     don't take this attribute at all -- the template gates it on
     `not c.blocked` -- so this and the rule above never fight.

     The 18th !important, and unavoidable: an inline attribute beats any
     selector. It deletes itself when the template stops dimming rows. */
  body.app-sales tr.comp-row[style*="opacity"] { opacity: 1 !important; }
  body.app-sales tr.comp-row[style*="opacity"] > td { background: var(--bg-2); }

  /* Same root cause one more time: --border used where something has to be
     seen. The Discover matrix marks "this competitor doesn't rank here" with
     a dot at --border -- 1.23:1 dark, 1.43:1 light, which is not a quiet
     marker, it is an absent one. The YOU column in the same table marks the
     same fact with an em dash at --text-3 and is perfectly readable, so the
     table currently gives two answers to one question.

     --border-light rather than --text-3: it should be perceivable without
     competing with the real positions around it. Exactly one element in the
     repo carries this declaration, so the hook reaches what it says and
     nothing else.

     The 16th !important in this layer (13 badge colours, two type floors,
     the control-edge hook), and it was written without one first -- which
     changed nothing, because the colour is an inline attribute and an
     inline attribute beats any selector. Same trade as the badges. It
     deletes itself when the template says --border-light. */
  body.app-sales td > span[style*="color:var(--border)"] {
    color: var(--border-light) !important;
  }

}

/* ── Sales: cards ──────────────────────────────────────────────────────────
   Card surfaces on this box were entirely inherited -- the layer had no
   .card rule at all, so they were stock geometry wearing retinted tokens.

   Three changes, and the reasoning is the same one each time: a card here
   is a work surface holding a table, not an object to be picked up.

   1. 8px radius, not --radius-lg's 14px. 14px on a 1400px-wide card holding
      a 12-column table reads as a consumer app; it is also the same radius
      the 40px chips use, so nothing in the hierarchy is distinguished by it.
   2. No shadow in light. :938 gives every card --shadow-sm and lifts it to
      --shadow on hover. On a page of stacked full-width cards that is 20+
      overlapping drop shadows doing no work, and it is the "SaaS card kit"
      look directly. The grey page already separates a white card.
   3. No hover reaction on a card that does nothing when clicked. :277 moves
      every card's border to --border-light on hover, so passing the cursor
      down the page lights up eight panels in turn. Kept for the two cards
      that are genuinely selectable.
   ────────────────────────────────────────────────────────────────────────── */
@media screen {

  body.app-sales .card { border-radius: 8px; }
  body.app-sales .cic-card { border-radius: 8px; }

  /* (0,0,3,0) so it beats :938's [data-theme="light"] .card rather than
     relying on source order against an equal-specificity rule. */
  [data-theme="light"] body.app-sales .card,
  [data-theme="light"] body.app-sales .card:hover,
  [data-theme="light"] body.app-sales .stat-card { box-shadow: none; }

  /* .site-type-card is a radio in card clothing on the Setup step and
     .client-card opens a client, so both keep their hover. Everything else
     holds still. */
  body.app-sales .card:hover:not(.site-type-card):not(.client-card) {
    border-color: var(--border);
  }

  /* The one card that SHOULD lift: the sticky "n selected" bar on pip 4,
     which floats over the cluster list. Its shadow is inline and hardcoded
     rgba(0,0,0,.25) -- a dark-theme value, which on the white page is a grey
     smudge rather than a lift. Keyed on the property; it is the only inline
     box-shadow on any of the six pips that isn't a 1px table-header rule. */
  [data-theme="light"] body.app-sales .card[style*="box-shadow:0 6px"] {
    box-shadow: 0 6px 20px rgba(26,31,54,.14) !important;
  }

}

/* ══════════════════════════════════════════════════════════════════════════
   SALES: MEETINGS (Fathom)                                     2026-09-10
   ──────────────────────────────────────────────────────────────────────────
   Moved out on 2026-09-10. Every `.mtg-` rule now lives beside the markup that
   emits it, in components/meeting_list.html (meeting_assets) — the page shipped
   with eight classes that had no rule in here at all, which is what splitting
   markup and rules across two files buys you.
   The one exception stays below: the prospect list's `:has(> .mtg-link)` hook
   belongs to pages/sales_home.html, not to the meetings page.
   ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   SALES: MEETING REPORT DOCUMENT                               2026-09-10
   ──────────────────────────────────────────────────────────────────────────
   The follow-up a prospect reads and signs. It is a *document*, not a card
   deck like the retiring sales report — one measure, Letter proportions, and
   the same thing on screen as on paper.

   Screen rules are scoped and inside @media screen like the rest of the
   layer. The print block below is deliberately NOT scoped to body.app-sales:
   the layer's tokens don't exist in print (that's what the @media screen
   wrapper is for), so print states its ink in literals. It stacks on top of
   the existing print block, which already hides the sidebar and top bar and
   forces :root to a paper palette — this one only adds what that doesn't
   know about.
   ══════════════════════════════════════════════════════════════════════════ */
@media screen {

  body.app-sales .mrep-actions {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 20px;
  }
  body.app-sales .mrep-del:hover { color: var(--danger); border-color: var(--danger); }

  body.app-sales .mrep-doc {
    max-width: 8.5in; margin: 0 auto; padding: 56px 64px;
    background: var(--bg-1); border: 1px solid var(--border); border-radius: 8px;
    color: var(--text-1); font-size: 14.5px; line-height: 1.6;
  }
  body.app-sales .mrep-title {
    margin: 0; font-size: 26px; font-weight: 700; color: var(--text-0); line-height: 1.25;
  }
  body.app-sales .mrep-subtitle {
    margin: 4px 0 30px; font-size: 15px; font-style: italic; color: var(--text-2);
  }
  body.app-sales .mrep-doc h2 {
    margin: 30px 0 10px; font-size: 17px; font-weight: 700; color: var(--text-0);
  }
  body.app-sales .mrep-doc h3 {
    margin: 16px 0 6px; font-size: 14px; font-weight: 600; color: var(--text-1);
  }
  body.app-sales .mrep-intent { margin: 0; font-weight: 600; color: var(--text-0); }

  body.app-sales .mrep-table { width: 100%; border-collapse: collapse; }
  /* --border-light, not --border: this is a real boundary a reader uses to
     separate cells, so it answers to WCAG 1.4.11 rather than being decor. */
  body.app-sales .mrep-table th,
  body.app-sales .mrep-table td {
    padding: 10px 12px; text-align: left; vertical-align: top;
    border: 1px solid var(--border-light);
  }
  body.app-sales .mrep-table th {
    width: 22%; background: var(--bg-2); color: var(--text-0); font-weight: 600;
  }

  body.app-sales .mrep-doc ul { margin: 0; padding-left: 22px; }
  body.app-sales .mrep-doc li { margin: 5px 0; }
  body.app-sales .mrep-doc li > ul { margin-top: 4px; list-style: circle; }

  body.app-sales .mrep-foot {
    margin-top: 36px; padding-top: 12px; border-top: 1px solid var(--border);
    font-size: 12px; color: var(--text-3);
  }

  body.app-sales .mrep-flags {
    max-width: 8.5in; margin: 14px auto 0; padding: 10px 14px;
    background: var(--bg-2); border: 1px solid var(--border-light);
    border-radius: 8px; font-size: 13px; color: var(--text-1);
  }
  body.app-sales .mrep-notes-toggle { max-width: 8.5in; margin: 8px auto 0; }

  /* The editor (components/meeting_editor.html owns every .mred- rule) hides
     this bar while it's open. The bar is display:flex above, and an author
     display rule beats the UA's [hidden] — the same trap as .spinner-pill. */
  body.app-sales .mrep-actions[hidden] { display: none; }
  /* "Edited 2026-09-15" — sits in the actions bar, so print already hides it. */
  body.app-sales .mrep-edited {
    font-size: 12px; color: var(--text-2); padding: 3px 9px;
    border: 1px solid var(--border-light); border-radius: 999px;
  }

  @media (max-width: 768px) {
    body.app-sales .mrep-doc { padding: 28px 22px; }
  }

}

@media print {
  /* No @page rule existed anywhere in this file before. Letter, because the
     document this replaces was Letter and went out through Docusign. */
  @page { size: Letter; margin: 0.75in 0.8in; }

  .mrep-actions, .mrep-flags, .mrep-notes-toggle,
  .sales-banner, .sales-progress { display: none !important; }

  /* A rep who hits Ctrl+P while the editor is open must not get a blank sheet.
     The editor hides the document behind it on screen (and hides ITSELF in
     print), so print puts the document back. What comes out is the last SAVED
     version — the same thing the server-rendered PDF would return. */
  /* :not(.mred) — the editor's own root carries .mrep-doc too (it reuses the
     paper frame), and without this it would out-specify its own hide rule and
     print as an empty bordered box. */
  .mrep-doc[hidden]:not(.mred) { display: block !important; }
  /* .app-shell carries a 22px offset for the banner, which isn't printed. */
  body.has-staging-banner .app-shell { padding-top: 0 !important; }

  .mrep-doc {
    max-width: none; margin: 0; padding: 0; border: 0; border-radius: 0;
    background: #fff; color: #222; font-size: 11pt; line-height: 1.45;
  }
  .mrep-title { font-size: 20pt; color: #111; }
  .mrep-subtitle { margin-bottom: 18pt; color: #444; }
  .mrep-doc h2 {
    margin: 18pt 0 6pt; font-size: 13pt; color: #111;
    break-after: avoid; page-break-after: avoid;
  }
  .mrep-doc h3 { margin: 10pt 0 4pt; font-size: 11pt; break-after: avoid; page-break-after: avoid; }
  .mrep-sec, .mrep-group, .mrep-table tr { break-inside: avoid; page-break-inside: avoid; }
  .mrep-table th, .mrep-table td { padding: 6pt 8pt; border: 1px solid #999; }
  .mrep-table th { background: #f0f0f0; color: #111; }
  /* The reset at :79 is `*{margin:0;padding:0}`, so a ul has no padding-left
     and its markers render outside the content box — invisible. The rule that
     puts it back lives in @media screen, so on paper every bullet list was
     rendering as run-on lines with no markers at all. Print needs its own. */
  .mrep-doc ul { padding-left: 20pt; }
  .mrep-doc li { margin: 3pt 0; }
  .mrep-doc li > ul { margin-top: 2pt; list-style: circle; }
  .mrep-foot { color: #555; }
  /* A printed URL is noise; the text still reads. */
  .mrep-doc a { color: inherit; text-decoration: none; }
}

/* A prospect row grows a fifth cell when a meeting report exists. Keyed on
   :has() rather than editing .sales-list-row's own grid, so rows without one
   are untouched. :has() is already relied on at :889 and :1075. */
@media screen {
  body.app-sales .sales-list-row:has(> .mtg-link) {
    grid-template-columns: 1fr auto auto auto auto;
  }
}
