/* ========================================================
   DARK THEME (default)

   RAP-179 — `[data-scheme-preview="…"]` on every surface block lets the
   Appearance picker in Settings paint a real swatch from the real palette
   (a <span> carrying the attribute gets that scheme's variables) instead
   of a hand-copied hex list that would drift the first time a palette is
   tuned. Preview only: it never reaches <html>.
   ======================================================== */
:root,
:root[data-theme="dark"],
[data-scheme-preview="dark"] {
  --bg-0: #0a0b0f;
  --bg-1: #12141a;
  --bg-2: #1a1d26;
  --bg-3: #1e2230;
  --bg-hover: #232736;
  --border: #262a36;
  --border-strong: #323749;
  --text-primary: #e8eaed;
  --text-secondary: #9aa0ac;
  --text-muted: #5a6170;
  --accent: #b22222;
  --accent-hover: #c62929;
  --accent-soft: rgba(178, 34, 34, 0.16);
  --accent-strong: rgba(178, 34, 34, 0.32);
  --danger: #ef4444;
  --danger-soft: rgba(239, 68, 68, 0.12);
  --success: #10b981;
  --shadow-lg: 0 20px 60px -20px rgba(0, 0, 0, 0.6);
  --shadow-md: 0 8px 20px -8px rgba(0, 0, 0, 0.5);
  --shadow-sm: 0 2px 8px -2px rgba(0, 0, 0, 0.4);
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;

  /* Contextual tokens that need to flip per theme */
  --nav-bg: rgba(18, 20, 26, 0.85);
  --avatar-text: #fca5a5;
  --brand-gradient: linear-gradient(135deg, #b22222 0%, #7f1d1d 100%);
  --page-gradient-1: rgba(178, 34, 34, 0.09);
  --page-gradient-2: rgba(178, 34, 34, 0.05);

  /* Lead status palette — color per SPEC §3 (stable across themes) */
  --st-coral:   #f87171;
  --st-coral2:  #fb923c;  /* J.2.7.6 intro_sent — coral→amber gradient bridge */
  --st-amber:   #f59e0b;
  --st-amber2:  #fbbf24;
  --st-blue:    #3b82f6;
  --st-indigo:  #6366f1;
  --st-teal:    #14b8a6;
  --st-green:   #34d399;
  --st-green2:  #10b981;
  --st-green3:  #059669;
  --st-gray:    #6b7280;
  --st-purple:  #a855f7;

  /* K.E.1.1 — iMessage blue. Used by any send-via-iMessage CTA so
     the visual signal matches Apple's bubble color rather than the
     red brand accent (which reads as primary action / destructive). */
  --color-imessage: #007AFF;
  --color-imessage-hover: #0071eb;
}

/* ========================================================
   LIGHT THEME
   ======================================================== */
:root[data-theme="light"],
[data-scheme-preview="light"] {
  --bg-0: #f6f7f9;
  --bg-1: #ffffff;
  --bg-2: #f3f4f6;
  --bg-3: #e9ecef;
  --bg-hover: #eef0f4;
  --border: #e5e7eb;
  --border-strong: #cbd5e1;
  --text-primary: #111827;
  --text-secondary: #374151;
  --text-muted: #6b7280;
  --accent: #b22222;
  --accent-hover: #991b1b;
  --accent-soft: rgba(178, 34, 34, 0.08);
  --accent-strong: rgba(178, 34, 34, 0.22);
  --danger: #dc2626;
  --danger-soft: rgba(220, 38, 38, 0.08);
  --success: #047857;
  --shadow-lg: 0 18px 50px -24px rgba(0, 0, 0, 0.2);
  --shadow-md: 0 8px 20px -10px rgba(0, 0, 0, 0.1);
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);

  --nav-bg: rgba(255, 255, 255, 0.88);
  --avatar-text: #991b1b;
  --page-gradient-1: rgba(178, 34, 34, 0.04);
  --page-gradient-2: rgba(178, 34, 34, 0.02);

  --color-imessage: #007AFF;
  --color-imessage-hover: #006fe6;
}

/* ========================================================
   SEMANTIC LAYER — tokens that must flip with the theme's
   POLARITY (dark surfaces vs. light surfaces) rather than with
   its individual palette.

   RAP-179: before this existed, every one of these lived twice —
   once as a literal in the base rule (the dark value) and once in a
   `[data-theme="light"] .selector` override (the light value). That
   shape only ever supported exactly two themes: a third scheme on a
   light background would silently inherit all the DARK literals and
   render unreadable chips, alerts and banners.

   The values below are a mechanical lift of those two sets — no
   colour was changed. Add a new scheme by adding its selector to
   whichever of the two lists matches its background polarity; only
   override an individual token when that scheme genuinely needs it.

   Keep the selector lists in sync with SCHEMES in public/js/theme.js
   (scripts/rap179_schemes_smoke.js asserts it).
   ======================================================== */

/* ── dark-polarity schemes ─────────────────────────────── */
/* The bare `:root` covers every dark-polarity scheme: their own blocks
   only restate the SURFACE palette, so they keep inheriting this set.
   Light-polarity schemes must name themselves in the list below. */
:root,
:root[data-theme="dark"] {
  --logo-filter: invert(1) hue-rotate(180deg);
  --icon-sun-display: inline;
  --icon-moon-display: none;
  --overlay-badge-bg: rgba(255, 255, 255, 0.06);
  --overlay-note-bg: rgba(255, 255, 255, 0.025);
  --overlay-tablehead-bg: rgba(255, 255, 255, 0.02);
  --overlay-pill-bg: rgba(255, 255, 255, 0.06);
  --overlay-rowhover-bg: rgba(255, 255, 255, 0.04);
  --overlay-select-bg: rgba(255, 255, 255, 0.04);
  /* Text printed ON an accent fill (primary buttons, nav count badges).
     White in both shipped themes because both accents are a dark red; a
     scheme that brightens its accent for legibility on its own background
     has to flip this, or the primary button gets LESS readable than the
     theme it was meant to improve on. */
  --on-accent: white;
  --danger-text: #fca5a5;
  --danger-text-hover: #fecaca;
  --danger-line: rgba(239, 68, 68, 0.28);
  --badge-coral-bg: rgba(248,113,113,.12);
  --badge-coral-fg: #fca5a5;
  --badge-coral-bd: rgba(248,113,113,.25);
  --badge-coral2-bg: rgba(251,146,60,.12);
  --badge-coral2-fg: #fdba74;
  --badge-coral2-bd: rgba(251,146,60,.25);
  --badge-amber-bg: rgba(245,158,11,.12);
  --badge-amber-fg: #fcd34d;
  --badge-amber-bd: rgba(245,158,11,.25);
  --badge-amber2-bg: rgba(251,191,36,.12);
  --badge-amber2-fg: #fde68a;
  --badge-amber2-bd: rgba(251,191,36,.25);
  --badge-blue-bg: rgba(59,130,246,.14);
  --badge-blue-fg: #93c5fd;
  --badge-blue-bd: rgba(59,130,246,.28);
  --badge-indigo-bg: rgba(99,102,241,.14);
  --badge-indigo-fg: #a5b4fc;
  --badge-indigo-bd: rgba(99,102,241,.28);
  --badge-teal-bg: rgba(20,184,166,.14);
  --badge-teal-fg: #5eead4;
  --badge-teal-bd: rgba(20,184,166,.28);
  --badge-green-bg: rgba(52,211,153,.14);
  --badge-green-fg: #6ee7b7;
  --badge-green-bd: rgba(52,211,153,.28);
  --badge-green2-bg: rgba(16,185,129,.14);
  --badge-green2-fg: #6ee7b7;
  --badge-green2-bd: rgba(16,185,129,.28);
  --badge-green3-bg: rgba(5,150,105,.18);
  --badge-green3-fg: #a7f3d0;
  --badge-green3-bd: rgba(5,150,105,.28);
  --badge-gray-bg: rgba(107,114,128,.14);
  --badge-gray-fg: #d1d5db;
  --badge-gray-bd: rgba(107,114,128,.3);
  --badge-purple-bg: rgba(168,85,247,.14);
  --badge-purple-fg: #d8b4fe;
  --badge-purple-bd: rgba(168,85,247,.28);
  --alert-error-fg: #fca5a5;
  --alert-error-bd: rgba(239,68,68,.25);
  --alert-success-fg: #6ee7b7;
  --alert-warning-fg: #fcd34d;
  --toast-success-fg: #6ee7b7;
  --toast-warning-fg: #fcd34d;
  --mc-ok-fg: #6ee7b7;
  --mc-fail-fg: #fca5a5;
  --task-overdue-bg: rgba(239,68,68,.04);
  --danger-zone-bg: rgba(239, 68, 68, 0.03);
  --appt-teal-bg: rgba(20,184,166,.08);
  --appt-teal-bd: rgba(20,184,166,.35);
  --appt-indigo-bg: rgba(99,102,241,.08);
  --appt-indigo-bd: rgba(99,102,241,.35);
  --appt-gray-bg: rgba(107,114,128,.08);
  --appt-gray-bd: rgba(107,114,128,.35);
  --client-success-bg: rgba(16, 185, 129, 0.08);
  --client-success-bd: rgba(16, 185, 129, 0.3);
  --client-success-fg: inherit;
  --status-dot-ring: 0 0 0 2px rgba(0,0,0,0.3);
  --appt-marker-ring: 0 0 0 1px rgba(0,0,0,0.3);
  --msg-pill-bg: rgba(0,0,0,0.06);
  --msg-pill-hover-bg: rgba(0, 0, 0, 0.1);
  --decision-gray-bg: rgba(148, 163, 184, 0.14);
  --decision-gray-bd: rgba(148, 163, 184, 0.55);
  --decision-gray-fg: rgb(148, 163, 184);
  --decision-leased-bg: rgba(71, 85, 105, 0.20);
  --decision-leased-bd: rgba(71, 85, 105, 0.85);
  --decision-leased-fg: rgb(148, 163, 184);
  --notif-bg: #d97706;
  --notif-fg: #fff;
  --notif-btn-bg: rgba(255, 255, 255, 0.18);
  --notif-btn-bd: rgba(255, 255, 255, 0.25);
  --notif-item-line: rgba(255, 255, 255, 0.18);
  --update-prompt-bg: #1b1e27;
  --update-prompt-fg: #e8eaf0;
  --update-prompt-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  --temp-cold-opacity: 0.45;
  --temp-warm-bg: rgba(245, 158, 11, 0.16);
  --temp-warm-bd: rgba(245, 158, 11, 0.55);
  --temp-hot-bg: rgba(239, 68, 68, 0.18);
  --temp-hot-bd: rgba(239, 68, 68, 0.6);
  --temp-chip-hot-bg: rgba(239, 68, 68, 0.16);
  --temp-chip-hot-fg: #fca5a5;
  --temp-chip-hot-bd: rgba(239, 68, 68, 0.6);
  --temp-chip-warm-bg: rgba(245, 158, 11, 0.16);
  --temp-chip-warm-fg: #fcd34d;
  --temp-chip-warm-bd: rgba(245, 158, 11, 0.6);
  --pending-bg: rgba(59, 130, 246, 0.1);
  --pending-bd: rgba(59, 130, 246, 0.3);
  --pending-fg: var(--text-primary);
  --pending-hover-bg: rgba(59, 130, 246, 0.16);
  --rel-row-bg: rgba(255, 255, 255, 0.02);
  --rel-row-bd: var(--border);
  --linkage-bg: rgba(99, 102, 241, 0.12);
  --linkage-fg: var(--text-primary);
  --linkage-bd: rgba(99, 102, 241, 0.32);
  --rel-result-bg: rgba(255, 255, 255, 0.03);
  --rel-result-bd: var(--border);
  --rel-result-hover-bg: rgba(255, 255, 255, 0.06);
  --rel-picked-bg: rgba(99, 102, 241, 0.08);
  --coll-warn-bg: rgba(245, 158, 11, 0.08);
  --coll-warn-bd: rgba(245, 158, 11, 0.32);
  --coll-intra-bg: rgba(220, 38, 38, 0.12);
  --coll-intra-bd: rgba(220, 38, 38, 0.55);
  --coll-intra-fg: rgb(239, 68, 68);
  --coll-row-line: rgba(255, 255, 255, 0.06);
  --coll-picker-bg: rgba(255, 255, 255, 0.03);
}

/* ── light-polarity schemes ────────────────────────────── */

/* Two families below intentionally carry their DARK values here, because
   that is what the Light theme actually rendered before RAP-179 and this
   commit changes no pixel of it:
     • --overlay-*  — white-on-white tints that Light has always drawn as
       effectively invisible; no [data-theme="light"] override existed.
     • --badge-coral2-* — the one status hue the July light-contrast pass
       missed. Its pastel #fdba74 washes out on white.
   Both are gaps in Light, not deliberate choices; new light-polarity
   schemes override them with values that actually work. Fixing them for
   Light itself is a separate call for Mauricio, not a drive-by. */
:root[data-theme="light"],
:root[data-theme="sepia"],
:root[data-theme="contrast-light"] {
  --logo-filter: none;
  --icon-sun-display: none;
  --icon-moon-display: inline;
  --overlay-badge-bg: rgba(255, 255, 255, 0.06);
  --overlay-note-bg: rgba(255, 255, 255, 0.025);
  --overlay-tablehead-bg: rgba(255, 255, 255, 0.02);
  --overlay-pill-bg: rgba(255, 255, 255, 0.06);
  --overlay-rowhover-bg: rgba(255, 255, 255, 0.04);
  --overlay-select-bg: rgba(255, 255, 255, 0.04);
  --on-accent: white;
  --danger-text: #b91c1c;
  --danger-text-hover: #991b1b;
  --danger-line: rgba(220, 38, 38, 0.28);
  --badge-coral-bg: rgba(220, 38, 38, 0.08);
  --badge-coral-fg: #b91c1c;
  --badge-coral-bd: rgba(220, 38, 38, 0.28);
  --badge-coral2-bg: rgba(251,146,60,.12);
  --badge-coral2-fg: #fdba74;
  --badge-coral2-bd: rgba(251,146,60,.25);
  --badge-amber-bg: rgba(245, 158, 11, 0.1);
  --badge-amber-fg: #b45309;
  --badge-amber-bd: rgba(245, 158, 11, 0.3);
  --badge-amber2-bg: rgba(251, 191, 36, 0.12);
  --badge-amber2-fg: #b45309;
  --badge-amber2-bd: rgba(251, 191, 36, 0.3);
  --badge-blue-bg: rgba(59, 130, 246, 0.1);
  --badge-blue-fg: #1d4ed8;
  --badge-blue-bd: rgba(59, 130, 246, 0.3);
  --badge-indigo-bg: rgba(99, 102, 241, 0.1);
  --badge-indigo-fg: #4338ca;
  --badge-indigo-bd: rgba(99, 102, 241, 0.3);
  --badge-teal-bg: rgba(20, 184, 166, 0.1);
  --badge-teal-fg: #0f766e;
  --badge-teal-bd: rgba(20, 184, 166, 0.3);
  --badge-green-bg: rgba(16, 185, 129, 0.1);
  --badge-green-fg: #047857;
  --badge-green-bd: rgba(16, 185, 129, 0.3);
  --badge-green2-bg: rgba(16, 185, 129, 0.1);
  --badge-green2-fg: #047857;
  --badge-green2-bd: rgba(16, 185, 129, 0.3);
  --badge-green3-bg: rgba(5, 150, 105, 0.12);
  --badge-green3-fg: #065f46;
  --badge-green3-bd: rgba(5, 150, 105, 0.32);
  --badge-gray-bg: rgba(107, 114, 128, 0.1);
  --badge-gray-fg: #374151;
  --badge-gray-bd: rgba(107, 114, 128, 0.3);
  --badge-purple-bg: rgba(168, 85, 247, 0.1);
  --badge-purple-fg: #7e22ce;
  --badge-purple-bd: rgba(168, 85, 247, 0.3);
  --alert-error-fg: #b91c1c;
  --alert-error-bd: rgba(220, 38, 38, 0.28);
  --alert-success-fg: #047857;
  --alert-warning-fg: #92400e;
  --toast-success-fg: #047857;
  --toast-warning-fg: #b45309;
  --mc-ok-fg: #047857;
  --mc-fail-fg: #b91c1c;
  --task-overdue-bg: rgba(220, 38, 38, 0.03);
  --danger-zone-bg: rgba(220, 38, 38, 0.02);
  --appt-teal-bg: rgba(20,184,166,.08);
  --appt-teal-bd: rgba(20,184,166,.35);
  --appt-indigo-bg: rgba(99,102,241,.08);
  --appt-indigo-bd: rgba(99,102,241,.35);
  --appt-gray-bg: rgba(107,114,128,.08);
  --appt-gray-bd: rgba(107,114,128,.35);
  --client-success-bg: rgba(16, 185, 129, 0.08);
  --client-success-bd: rgba(5, 150, 105, 0.3);
  --client-success-fg: #065f46;
  --status-dot-ring: 0 0 0 1px rgba(0, 0, 0, 0.12);
  --appt-marker-ring: 0 0 0 1px rgba(0,0,0,0.3);
  --msg-pill-bg: var(--bg-3);
  --msg-pill-hover-bg: var(--bg-hover);
  --decision-gray-bg: rgba(100, 116, 139, 0.10);
  --decision-gray-bd: rgba(71, 85, 105, 0.45);
  --decision-gray-fg: rgb(71, 85, 105);
  --decision-leased-bg: rgba(71, 85, 105, 0.12);
  --decision-leased-bd: rgba(51, 65, 85, 0.65);
  --decision-leased-fg: rgb(51, 65, 85);
  --notif-bg: #f59e0b;
  --notif-fg: #1f2937;
  --notif-btn-bg: rgba(0, 0, 0, 0.08);
  --notif-btn-bd: rgba(0, 0, 0, 0.15);
  --notif-item-line: rgba(0, 0, 0, 0.1);
  --update-prompt-bg: #fff;
  --update-prompt-fg: #1f2937;
  --update-prompt-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  --temp-cold-opacity: 0.55;
  --temp-warm-bg: rgba(245, 158, 11, 0.14);
  --temp-warm-bd: rgba(217, 119, 6, 0.5);
  --temp-hot-bg: rgba(239, 68, 68, 0.12);
  --temp-hot-bd: rgba(220, 38, 38, 0.5);
  --temp-chip-hot-bg: rgba(239, 68, 68, 0.1);
  --temp-chip-hot-fg: #b91c1c;
  --temp-chip-hot-bd: rgba(220, 38, 38, 0.5);
  --temp-chip-warm-bg: rgba(245, 158, 11, 0.12);
  --temp-chip-warm-fg: #b45309;
  --temp-chip-warm-bd: rgba(217, 119, 6, 0.5);
  --pending-bg: rgba(59, 130, 246, 0.08);
  --pending-bd: rgba(59, 130, 246, 0.3);
  --pending-fg: #1d4ed8;
  --pending-hover-bg: rgba(59, 130, 246, 0.14);
  --rel-row-bg: rgba(0, 0, 0, 0.015);
  --rel-row-bd: rgba(0, 0, 0, 0.08);
  --linkage-bg: rgba(99, 102, 241, 0.08);
  --linkage-fg: #4338ca;
  --linkage-bd: rgba(99, 102, 241, 0.32);
  --rel-result-bg: rgba(0, 0, 0, 0.02);
  --rel-result-bd: rgba(0, 0, 0, 0.08);
  --rel-result-hover-bg: rgba(0, 0, 0, 0.05);
  --rel-picked-bg: rgba(99, 102, 241, 0.06);
  --coll-warn-bg: rgba(245, 158, 11, 0.07);
  --coll-warn-bd: rgba(245, 158, 11, 0.32);
  --coll-intra-bg: rgba(220, 38, 38, 0.08);
  --coll-intra-bd: rgba(220, 38, 38, 0.55);
  --coll-intra-fg: rgb(185, 28, 28);
  --coll-row-line: rgba(0, 0, 0, 0.06);
  --coll-picker-bg: rgba(0, 0, 0, 0.03);
}

/* ========================================================
   RAP-179 — ADDITIONAL SCHEMES

   Each block below restates only the SURFACE palette (backgrounds,
   text, borders, accent, shadows, page gradients). Everything else —
   radii, the status-dot hues, the iMessage blue, and the whole semantic
   layer above — is inherited, so a scheme is a short, readable list
   rather than a fork of the stylesheet.

   A scheme's POLARITY is declared by which of the two semantic selector
   lists above it appears in, NOT by anything in its own block. Dark
   schemes appear in neither (the bare `:root` already covers them);
   light schemes must be listed explicitly. Getting that wrong is the one
   way to make a scheme unreadable, so the smoke test asserts every
   light-polarity scheme in theme.js is present in that list.

   Dark and Light are deliberately absent from this section: they are
   defined above, unchanged, and nothing here may restyle them.
   ======================================================== */

/* ── MIDNIGHT — blue-tinted dark ────────────────────────── */
:root[data-theme="midnight"],
[data-scheme-preview="midnight"] {
  --bg-0: #070b16;
  --bg-1: #0e1524;
  --bg-2: #162033;
  --bg-3: #1b2740;
  --bg-hover: #22304d;
  --border: #24314a;
  --border-strong: #35476a;
  --text-primary: #e7ecf7;
  --text-secondary: #a3b0c6;
  --text-muted: #74829c;
  /* A touch brighter than Dark's #b22222: navy is darker than near-black
     for a DARK red, so the stock accent drops below Dark's own link
     contrast. Same hue, same brand. */
  --accent: #bd2828;
  --accent-hover: #c93030;
  --accent-soft: rgba(189, 40, 40, 0.18);
  --accent-strong: rgba(189, 40, 40, 0.34);
  --danger: #ef4444;
  --danger-soft: rgba(239, 68, 68, 0.14);
  --success: #10b981;
  --shadow-lg: 0 20px 60px -20px rgba(0, 0, 0, 0.7);
  --shadow-md: 0 8px 20px -8px rgba(0, 0, 0, 0.55);
  --shadow-sm: 0 2px 8px -2px rgba(0, 0, 0, 0.45);

  --nav-bg: rgba(14, 21, 36, 0.85);
  --avatar-text: #fca5a5;
  --page-gradient-1: rgba(59, 130, 246, 0.10);
  --page-gradient-2: rgba(178, 34, 34, 0.07);
}

/* ── GRAPHITE — neutral slate dark ──────────────────────── */
:root[data-theme="graphite"],
[data-scheme-preview="graphite"] {
  --bg-0: #16181b;
  --bg-1: #1e2124;
  --bg-2: #26292e;
  --bg-3: #2d3137;
  --bg-hover: #353a41;
  --border: #34383f;
  --border-strong: #4a4f58;
  --text-primary: #e9eaec;
  --text-secondary: #adb2b9;
  --text-muted: #7e848c;
  /* Graphite's surfaces are the lightest of the dark schemes, so the brand
     red has to come up with them to stay as legible as it is on Dark. */
  --accent: #cf3030;
  /* Hover DARKENS here rather than brightening. Going lighter is the usual
     dark-theme move, but the accent is also a button FILL: #de4040 put white
     button text at 4.27:1, i.e. the hover state was less readable than the
     resting one. */
  --accent-hover: #c62929;
  --accent-soft: rgba(207, 48, 48, 0.16);
  --accent-strong: rgba(207, 48, 48, 0.32);
  --danger: #ef4444;
  --danger-soft: rgba(239, 68, 68, 0.12);
  --success: #10b981;
  --coll-intra-fg: #f87171;
  --temp-cold-opacity: 0.52;
  --shadow-lg: 0 20px 60px -20px rgba(0, 0, 0, 0.55);
  --shadow-md: 0 8px 20px -8px rgba(0, 0, 0, 0.45);
  --shadow-sm: 0 2px 8px -2px rgba(0, 0, 0, 0.35);

  --nav-bg: rgba(30, 33, 36, 0.86);
  --avatar-text: #fca5a5;
  --page-gradient-1: rgba(178, 34, 34, 0.07);
  --page-gradient-2: rgba(255, 255, 255, 0.03);
}

/* ── HIGH CONTRAST (DARK) ───────────────────────────────── */
/* The brand red is deliberately brightened here and nowhere else:
   #b22222 on #000 is ~3.4:1, which fails the very thing this scheme
   exists for. Borders are opaque mid-greys rather than the near-invisible
   hairlines the other dark schemes use. */
:root[data-theme="contrast-dark"],
[data-scheme-preview="contrast-dark"] {
  --bg-0: #000000;
  --bg-1: #0b0b0b;
  --bg-2: #151515;
  --bg-3: #1e1e1e;
  --bg-hover: #2b2b2b;
  --border: #5a5a5a;
  --border-strong: #8a8a8a;
  --text-primary: #ffffff;
  --text-secondary: #e4e4e4;
  --text-muted: #b8b8b8;
  --accent: #ff5252;
  --accent-hover: #ff7b7b;
  --accent-soft: rgba(255, 82, 82, 0.20);
  --accent-strong: rgba(255, 82, 82, 0.48);
  --danger: #ff6161;
  --danger-soft: rgba(255, 97, 97, 0.18);
  --success: #3ddc97;
  --shadow-lg: 0 20px 60px -20px rgba(0, 0, 0, 0.9);
  --shadow-md: 0 8px 20px -8px rgba(0, 0, 0, 0.8);
  --shadow-sm: 0 2px 8px -2px rgba(0, 0, 0, 0.7);

  --nav-bg: rgba(0, 0, 0, 0.94);
  --avatar-text: #ff9d9d;
  --brand-gradient: linear-gradient(135deg, #ff5252 0%, #8f0000 100%);
  --page-gradient-1: transparent;
  --page-gradient-2: transparent;

  /* Semantic lifts: the stock dark chip text sits on ~12% tints that all
     but vanish on true black, and the pastels are the weakest link. */
  --overlay-badge-bg: rgba(255, 255, 255, 0.12);
  --overlay-pill-bg: rgba(255, 255, 255, 0.12);
  --overlay-rowhover-bg: rgba(255, 255, 255, 0.10);
  --overlay-note-bg: rgba(255, 255, 255, 0.06);
  --overlay-tablehead-bg: rgba(255, 255, 255, 0.06);
  --overlay-select-bg: rgba(255, 255, 255, 0.09);
  --danger-text: #ff9d9d;
  --danger-text-hover: #ffc9c9;
  --danger-line: rgba(255, 97, 97, 0.65);
  --badge-coral-bg: rgba(248, 113, 113, 0.22);  --badge-coral-fg: #ffbcbc;  --badge-coral-bd: rgba(248, 113, 113, 0.7);
  --badge-coral2-bg: rgba(251, 146, 60, 0.22);  --badge-coral2-fg: #ffd0a3; --badge-coral2-bd: rgba(251, 146, 60, 0.7);
  --badge-amber-bg: rgba(245, 158, 11, 0.22);   --badge-amber-fg: #ffe08a;  --badge-amber-bd: rgba(245, 158, 11, 0.7);
  --badge-amber2-bg: rgba(251, 191, 36, 0.22);  --badge-amber2-fg: #ffeaa8; --badge-amber2-bd: rgba(251, 191, 36, 0.7);
  --badge-blue-bg: rgba(59, 130, 246, 0.24);    --badge-blue-fg: #b6d4ff;   --badge-blue-bd: rgba(59, 130, 246, 0.75);
  --badge-indigo-bg: rgba(99, 102, 241, 0.24);  --badge-indigo-fg: #c3c8ff; --badge-indigo-bd: rgba(99, 102, 241, 0.75);
  --badge-teal-bg: rgba(20, 184, 166, 0.24);    --badge-teal-fg: #8ff5e5;   --badge-teal-bd: rgba(20, 184, 166, 0.75);
  --badge-green-bg: rgba(52, 211, 153, 0.24);   --badge-green-fg: #9df3cd;  --badge-green-bd: rgba(52, 211, 153, 0.75);
  --badge-green2-bg: rgba(16, 185, 129, 0.24);  --badge-green2-fg: #9df3cd; --badge-green2-bd: rgba(16, 185, 129, 0.75);
  --badge-green3-bg: rgba(5, 150, 105, 0.28);   --badge-green3-fg: #bcf7dd; --badge-green3-bd: rgba(5, 150, 105, 0.75);
  --badge-gray-bg: rgba(148, 163, 184, 0.22);   --badge-gray-fg: #e2e6eb;   --badge-gray-bd: rgba(148, 163, 184, 0.7);
  --badge-purple-bg: rgba(168, 85, 247, 0.24);  --badge-purple-fg: #e3c6ff; --badge-purple-bd: rgba(168, 85, 247, 0.75);
  --alert-error-fg: #ffbcbc;
  --alert-error-bd: rgba(255, 97, 97, 0.6);
  --alert-success-fg: #9df3cd;
  --alert-warning-fg: #ffe08a;
  --toast-success-fg: #9df3cd;
  --toast-warning-fg: #ffe08a;
  --mc-ok-fg: #9df3cd;
  --mc-fail-fg: #ffbcbc;
  --temp-chip-hot-fg: #ffbcbc;
  --temp-chip-warm-fg: #ffe08a;
  /* The temperature toggle's tint is the state signal (the glyph is a
     colour emoji and carries none) — on true black a 16% wash disappears. */
  --temp-warm-bg: rgba(245, 158, 11, 0.28);
  --temp-warm-bd: rgba(245, 158, 11, 0.8);
  --temp-hot-bg: rgba(239, 68, 68, 0.30);
  --temp-hot-bd: rgba(239, 68, 68, 0.85);
  --temp-cold-opacity: 0.7;
  --decision-gray-fg: #cbd5e1;
  --decision-leased-fg: #cbd5e1;
  --decision-gray-bd: rgba(148, 163, 184, 0.75);
  --decision-leased-bd: rgba(148, 163, 184, 0.6);
  --coll-intra-fg: #ff8a8a;
  --coll-row-line: rgba(255, 255, 255, 0.22);
  --notif-item-line: rgba(255, 255, 255, 0.35);
  --update-prompt-bg: #0b0b0b;
  --update-prompt-fg: #ffffff;
  --linkage-fg: #c3c8ff;
  --pending-fg: #b6d4ff;
  --pending-bd: rgba(59, 130, 246, 0.7);
  /* #ff5252 is bright enough to read as a LINK on black, which makes it
     too bright to carry white BUTTON text — near-black on it instead. */
  --on-accent: #1a0000;
  --temp-cold-opacity: 0.7;
}

/* ── SEPIA — warm paper light ───────────────────────────── */
:root[data-theme="sepia"],
[data-scheme-preview="sepia"] {
  --bg-0: #f2ebdf;
  --bg-1: #fbf6ee;
  --bg-2: #f0e8d9;
  --bg-3: #e7dcc8;
  --bg-hover: #ebe2d1;
  --border: #ded1b9;
  --border-strong: #c3b394;
  --text-primary: #2c2418;
  /* Cream is darker than white, so anything drawn at reduced opacity (the
     disabled pager buttons, the dimmed cold glyph) loses ground against it.
     Both are pulled down a step to land where Light does. */
  --text-secondary: #423827;
  /* Table headers and hints run on this; #7a6c55 was Light's ratio minus
     0.2 once the surface stopped being pure white. */
  --text-muted: #6b5f4a;
  --accent: #9a1d1d;
  --accent-hover: #7d1414;
  --accent-soft: rgba(154, 29, 29, 0.09);
  --accent-strong: rgba(154, 29, 29, 0.26);
  --danger: #b3261e;
  --danger-soft: rgba(179, 38, 30, 0.09);
  --success: #256b47;
  --shadow-lg: 0 18px 50px -24px rgba(74, 58, 30, 0.32);
  --shadow-md: 0 8px 20px -10px rgba(74, 58, 30, 0.18);
  --shadow-sm: 0 1px 3px rgba(74, 58, 30, 0.14);

  --nav-bg: rgba(251, 246, 238, 0.9);
  --avatar-text: #7d1414;
  --brand-gradient: linear-gradient(135deg, #9a1d1d 0%, #5f1010 100%);
  --page-gradient-1: rgba(161, 31, 31, 0.05);
  --page-gradient-2: rgba(160, 120, 40, 0.07);

  /* The two families the Light theme never fixed (see the note above the
     light-polarity block): white-on-white overlays, and coral2. */
  --overlay-badge-bg: rgba(74, 58, 30, 0.07);
  --overlay-pill-bg: rgba(74, 58, 30, 0.07);
  --overlay-note-bg: rgba(74, 58, 30, 0.04);
  --overlay-tablehead-bg: rgba(74, 58, 30, 0.05);
  --overlay-rowhover-bg: rgba(74, 58, 30, 0.05);
  --overlay-select-bg: rgba(255, 252, 246, 0.9);
  --badge-coral2-bg: rgba(194, 65, 12, 0.10);
  --badge-coral2-fg: #9a3412;
  --badge-coral2-bd: rgba(194, 65, 12, 0.32);
  --appt-marker-ring: 0 0 0 1px rgba(74, 58, 30, 0.22);

  /* The shared light amber (#b45309) is tuned against pure white; cream
     costs it ~0.3 and drops the warning family under 4.5:1. */
  --alert-success-fg: #03694c;
  --toast-success-fg: #03694c;
  --mc-ok-fg: #03694c;
  --client-success-fg: #024e39;
  --alert-warning-fg: #8a3d05;
  --toast-warning-fg: #9a4708;
  --temp-chip-warm-fg: #9a4708;
  --badge-amber-fg: #9a4708;
  --badge-amber2-fg: #9a4708;

  /* Calendar status dots: the stock pastels are already weak on white and
     weaker on cream. Deepened to the same hues at paper-friendly weight —
     this is the one scheme that moves them, and it moves them UP. */
  --st-coral: #e04545;
  --st-coral2: #e2670f;
  --st-amber: #c07800;
  --st-amber2: #b08000;
  --st-teal: #0d9488;
  --st-green: #12a06f;
  --st-green2: #0e9b6d;
  --temp-cold-opacity: 0.64;
}

/* ── HIGH CONTRAST (LIGHT) ──────────────────────────────── */
/* Built for reading a phone in direct sun: opaque borders, black text,
   and status hues darkened to their 700/800 range so a chip is legible
   rather than merely tinted. */
:root[data-theme="contrast-light"],
[data-scheme-preview="contrast-light"] {
  --bg-0: #ffffff;
  --bg-1: #ffffff;
  --bg-2: #f1f1f1;
  --bg-3: #e4e4e4;
  --bg-hover: #d9d9d9;
  --border: #6b6b6b;
  --border-strong: #2b2b2b;
  --text-primary: #000000;
  --text-secondary: #1c1c1c;
  --text-muted: #4a4a4a;
  --accent: #8f0000;
  --accent-hover: #5e0000;
  --accent-soft: rgba(143, 0, 0, 0.09);
  --accent-strong: rgba(143, 0, 0, 0.38);
  --danger: #a10000;
  --danger-soft: rgba(161, 0, 0, 0.09);
  --success: #00553a;
  --shadow-lg: 0 18px 50px -24px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 8px 20px -10px rgba(0, 0, 0, 0.3);
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25);

  --nav-bg: rgba(255, 255, 255, 0.96);
  --avatar-text: #8f0000;
  --brand-gradient: linear-gradient(135deg, #8f0000 0%, #3d0000 100%);
  --page-gradient-1: transparent;
  --page-gradient-2: transparent;

  /* Status dots and calendar markers are shapes, not text — but the
     pastel end of the stock palette drops under 3:1 on white, which is
     exactly the audience this scheme is for. */
  --st-coral: #c81e1e;
  --st-coral2: #c2410c;
  --st-amber: #a35a00;
  --st-amber2: #8a5a00;
  --st-blue: #1749b3;
  --st-indigo: #3730a3;
  --st-teal: #0f6c62;
  --st-green: #04684a;
  --st-green2: #04684a;
  --st-green3: #045c40;
  --st-gray: #4b5563;
  --st-purple: #7222b8;

  --overlay-badge-bg: rgba(0, 0, 0, 0.09);
  --overlay-pill-bg: rgba(0, 0, 0, 0.09);
  --overlay-note-bg: rgba(0, 0, 0, 0.05);
  --overlay-tablehead-bg: rgba(0, 0, 0, 0.06);
  --overlay-rowhover-bg: rgba(0, 0, 0, 0.07);
  --overlay-select-bg: #ffffff;
  --danger-text: #a10000;
  --danger-text-hover: #6d0000;
  --danger-line: rgba(161, 0, 0, 0.65);
  --badge-coral-bg: rgba(200, 30, 30, 0.11);   --badge-coral-fg: #8f0f0f;  --badge-coral-bd: rgba(200, 30, 30, 0.7);
  --badge-coral2-bg: rgba(194, 65, 12, 0.11);  --badge-coral2-fg: #8a2c08; --badge-coral2-bd: rgba(194, 65, 12, 0.7);
  --badge-amber-bg: rgba(163, 90, 0, 0.12);    --badge-amber-fg: #7a4300;  --badge-amber-bd: rgba(163, 90, 0, 0.7);
  --badge-amber2-bg: rgba(138, 90, 0, 0.12);   --badge-amber2-fg: #6b4500; --badge-amber2-bd: rgba(138, 90, 0, 0.7);
  --badge-blue-bg: rgba(23, 73, 179, 0.11);    --badge-blue-fg: #16357f;   --badge-blue-bd: rgba(23, 73, 179, 0.7);
  --badge-indigo-bg: rgba(55, 48, 163, 0.11);  --badge-indigo-fg: #2e2882; --badge-indigo-bd: rgba(55, 48, 163, 0.7);
  --badge-teal-bg: rgba(15, 108, 98, 0.12);    --badge-teal-fg: #0b5049;   --badge-teal-bd: rgba(15, 108, 98, 0.7);
  --badge-green-bg: rgba(4, 104, 74, 0.12);    --badge-green-fg: #03503a;  --badge-green-bd: rgba(4, 104, 74, 0.7);
  --badge-green2-bg: rgba(4, 104, 74, 0.12);   --badge-green2-fg: #03503a; --badge-green2-bd: rgba(4, 104, 74, 0.7);
  --badge-green3-bg: rgba(4, 92, 64, 0.14);    --badge-green3-fg: #02402d; --badge-green3-bd: rgba(4, 92, 64, 0.7);
  --badge-gray-bg: rgba(75, 85, 99, 0.12);     --badge-gray-fg: #23282f;   --badge-gray-bd: rgba(75, 85, 99, 0.7);
  --badge-purple-bg: rgba(114, 34, 184, 0.11); --badge-purple-fg: #5b1a94; --badge-purple-bd: rgba(114, 34, 184, 0.7);
  --alert-error-fg: #8f0f0f;
  --alert-error-bd: rgba(161, 0, 0, 0.6);
  --alert-success-fg: #03503a;
  --alert-warning-fg: #6b3d00;
  --toast-success-fg: #03503a;
  --toast-warning-fg: #7a4300;
  --mc-ok-fg: #03503a;
  --mc-fail-fg: #8f0f0f;
  --temp-chip-hot-fg: #8f0f0f;
  --temp-chip-warm-fg: #7a4300;
  --temp-hot-bd: rgba(161, 0, 0, 0.75);
  --temp-warm-bd: rgba(163, 90, 0, 0.75);
  --temp-cold-opacity: 0.75;
  --decision-gray-fg: #23282f;
  --decision-gray-bd: rgba(51, 65, 85, 0.7);
  --decision-leased-fg: #1c2430;
  --decision-leased-bd: rgba(30, 41, 59, 0.8);
  --coll-intra-fg: #8f0f0f;
  --coll-row-line: rgba(0, 0, 0, 0.18);
  --status-dot-ring: 0 0 0 1px rgba(0, 0, 0, 0.45);
  --appt-marker-ring: 0 0 0 1px rgba(0, 0, 0, 0.45);
  --notif-bg: #8a5a00;
  --notif-fg: #ffffff;
  --notif-btn-bg: rgba(0, 0, 0, 0.22);
  --notif-btn-bd: rgba(255, 255, 255, 0.7);
  --notif-item-line: rgba(255, 255, 255, 0.45);
  --linkage-fg: #2e2882;
  --pending-fg: #16357f;
  --pending-bd: rgba(23, 73, 179, 0.7);
  --client-success-bd: rgba(4, 104, 74, 0.6);
  --client-success-fg: #03503a;
}

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

html, body {
  height: 100%;
}

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg-0);
  color: var(--text-primary);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#app { min-height: 100%; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }

::selection { background: var(--accent-soft); color: var(--text-primary); }

/* =====================================================
   LOGIN SCREEN
   ===================================================== */

.login-wrap {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 1.5rem;
  background:
    radial-gradient(ellipse 900px 600px at 50% -10%, var(--page-gradient-1), transparent 70%),
    radial-gradient(ellipse 600px 400px at 85% 110%, var(--page-gradient-2), transparent 70%),
    var(--bg-0);
}
.login-card {
  width: 100%; max-width: 420px;
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 2.5rem 2rem;
  box-shadow: var(--shadow-lg);
}
.brand { text-align: center; margin-bottom: 1.75rem; }
.brand-logo {
  display: flex;
  justify-content: center;
  margin-bottom: 0.25rem;
}
.brand-subtitle {
  font-size: 0.875rem;
  color: var(--text-muted);
  letter-spacing: 0.01em;
}

/* Logo image (shared across login / client / nav). The PNG has a
   transparent background, but its black elements (R's, roofline,
   wordmark) vanish on dark surfaces. Dark mode inverts luminance via
   invert(1) and rotates hue 180° so the red door stays red —
   net effect: black → white, white → black, red ≈ red. */
.rr-logo {
  display: block;
  max-width: 100%;
  height: auto;
  filter: var(--logo-filter);
}
.rr-logo-full {
  width: 100%;
  max-width: 280px;
}
.rr-logo-nav {
  height: 42px;
  width: 96px;
  object-fit: cover;
  object-position: center 38%;
}

form.stack > * + * { margin-top: 1.125rem; }
.footnote {
  margin-top: 1.5rem; text-align: center;
  font-size: 0.8125rem; color: var(--text-muted);
}

/* =====================================================
   FIELDS (shared between login + forms)
   ===================================================== */

.field { display: block; }
.field label {
  display: block;
  font-size: 0.8125rem; font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: 0.5rem; letter-spacing: 0.01em;
}
.field .req { color: var(--danger); }
/* K.G.6.E — inline per-field validation error (e.g. phone must be
   10 digits). Sits directly below the input. Hidden until validation
   on blur or submit flags an issue. */
.field-error {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.75rem;
  color: var(--danger, #ef4444);
}
.field-error[hidden] { display: none; }
.field input,
.field select,
.field textarea {
  width: 100%;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.65rem 0.8125rem;
  color: var(--text-primary);
  font-size: 0.9375rem; font-family: inherit;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.field textarea { resize: vertical; min-height: 80px; }
.field input:hover:not(:focus),
.field select:hover:not(:focus),
.field textarea:hover:not(:focus) {
  border-color: var(--border-strong);
}
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-3);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--text-muted); }

.field select {
  appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'><path d='M1 1L5 5L9 1' stroke='%239aa0ac' stroke-width='1.5' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  padding-right: 2rem;
}

/* --------- Password input with eye toggle --------- */

.password-input-wrap {
  position: relative;
}
.password-input-wrap input {
  /* leave room on the right for the toggle button */
  padding-right: 2.5rem !important;
}
.password-toggle {
  position: absolute;
  right: 6px; top: 50%;
  transform: translateY(-50%);
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--text-muted);
  cursor: pointer;
  transition: color .12s ease, background .12s ease;
  padding: 0;
  font-family: inherit;
}
.password-toggle:hover {
  color: var(--text-primary);
  background: var(--bg-3);
}
.password-toggle:focus-visible {
  outline: none;
  color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.eye-icon { width: 18px; height: 18px; display: block; }
.password-toggle[aria-pressed="true"]  .eye-show { display: none; }
.password-toggle[aria-pressed="false"] .eye-hide { display: none; }

.password-mismatch {
  margin-top: -0.25rem;
  font-size: 0.8125rem;
  color: var(--danger-text);
  padding: 0.125rem 0;
}
.field.inline {
  display: flex; flex-direction: column; gap: 0.375rem;
}
.field.inline label { margin-bottom: 0; }
.inline-label {
  display: block;
  font-size: 0.8125rem; font-weight: 500;
  color: var(--text-secondary); margin-bottom: 0.375rem;
}

/* =====================================================
   BUTTONS
   ===================================================== */

button, .primary, .ghost {
  font-family: inherit;
}

.primary {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--on-accent);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  padding: 0.65rem 1rem;
  font-size: 0.9375rem; font-weight: 500;
  cursor: pointer;
  transition: background .15s ease, transform .05s ease, box-shadow .15s ease;
  box-shadow: var(--shadow-sm);
  text-decoration: none;
}
button.primary { width: 100%; padding: 0.8125rem; }
form .primary { width: 100%; padding: 0.8125rem; }
.primary:hover:not(:disabled) { background: var(--accent-hover); color: var(--on-accent); }
.primary:active:not(:disabled) { transform: translateY(1px); }
.primary:disabled { opacity: 0.55; cursor: not-allowed; }
a.primary { width: auto; }

.ghost {
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.55rem 0.9rem;
  font-size: 0.875rem; font-weight: 500;
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
  text-decoration: none;
}
.ghost:hover:not(:disabled) {
  background: var(--bg-2); color: var(--text-primary);
  border-color: var(--border-strong);
}
.ghost:disabled { opacity: .5; cursor: not-allowed; }

.ghost.small, .primary.small {
  padding: 0.375rem 0.75rem;
  font-size: 0.8125rem;
  border-radius: var(--radius-sm);
}

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  background: transparent; color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 1.125rem; line-height: 1;
  cursor: pointer;
}
.icon-btn:hover { background: var(--bg-2); color: var(--text-primary); }

/* =====================================================
   ALERTS
   ===================================================== */

.alert {
  padding: 0.75rem 0.875rem;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  border: 1px solid transparent;
}
.alert.error {
  background: var(--danger-soft);
  border-color: var(--alert-error-bd);
  color: var(--alert-error-fg);
}
.alert.success {
  background: rgba(16, 185, 129, 0.1);
  border-color: rgba(16, 185, 129, 0.3);
  color: var(--alert-success-fg);
}
.alert.info {
  background: var(--accent-soft);
  border-color: var(--accent-strong);
  color: var(--text-primary);
}
/* RAP-43 — low-inventory advisory (success-with-warning, amber): distinct
   from .error (failure red) and .info (accent). */
.alert.warning {
  background: rgba(232, 161, 60, 0.12);
  border-color: rgba(232, 161, 60, 0.35);
  color: var(--alert-warning-fg);
}

/* =====================================================
   APP SHELL (nav + main)
   ===================================================== */

.nav {
  position: sticky; top: 0; z-index: 20;
  background: var(--nav-bg);
  backdrop-filter: saturate(140%) blur(8px);
  -webkit-backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--border);
}
.nav-inner {
  max-width: 1280px; margin: 0 auto;
  display: flex; align-items: center; gap: 1.5rem;
  padding: 0.625rem 1.5rem;
}
.nav-brand {
  display: inline-flex; align-items: center;
  color: var(--text-primary);
  text-decoration: none;
  padding: 4px;
  border-radius: var(--radius-sm);
}
.nav-brand:hover { color: var(--text-primary); }
.nav-brand:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.nav-links {
  display: flex; align-items: center; gap: 0.125rem;
  flex: 1;
}
.nav-link {
  padding: 0.5rem 0.875rem;
  font-size: 0.875rem; font-weight: 500;
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: background .15s ease, color .15s ease;
}
.nav-link:hover {
  color: var(--text-primary);
  background: var(--bg-2);
}
.nav-link.is-active {
  color: var(--text-primary);
  background: var(--bg-2);
}

/* J.2.2.4 — aggregate-unread badge on the Messages tab. Inline-block
   to flow next to the label, accent background. K.N C — counts are
   exact (no '10+' cap); the pill widens with the number, whitespace
   nowrap so a 3-digit count never wraps or clips at phone width.
   Hidden via [hidden] when zero. */
.nav-link-badge {
  white-space: nowrap;
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0.05rem 0.4rem;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.4;
  color: #fff;
  background: var(--accent);
  border-radius: 999px;
  vertical-align: 1px;
  min-width: 1.2rem;
  text-align: center;
}
.nav-link-badge[hidden] { display: none; }

.nav-user {
  display: flex; align-items: center; gap: 0.5rem;
}
.nav-avatar {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--avatar-text);
  font-size: 0.8125rem; font-weight: 600;
  letter-spacing: 0.02em;
}

.nav-theme-toggle {
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
  padding: 0;
}
.nav-theme-toggle:hover {
  background: var(--bg-2);
  color: var(--text-primary);
  border-color: var(--border);
}
.nav-theme-toggle:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.theme-icon { width: 16px; height: 16px; }
/* Dark-polarity schemes show the sun (tap → light); light-polarity ones show
   the moon. Driven by tokens so a new scheme joins by polarity, not by name. */
.theme-icon-sun  { display: var(--icon-sun-display); }
.theme-icon-moon { display: var(--icon-moon-display); }

.nav-signout {
  background: transparent; color: var(--text-muted);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 0.375rem 0.625rem;
  font-size: 0.8125rem; font-family: inherit;
  cursor: pointer;
}
.nav-signout:hover {
  color: var(--text-primary);
  background: var(--bg-2);
  border-color: var(--border);
}

.app-main {
  max-width: 1280px;
  margin: 0 auto;
  padding: 1.75rem 1.5rem 3rem;
}
.app-main.narrow { max-width: 780px; }

/* =====================================================
   PAGE HEADER + LAYOUT BLOCKS
   ===================================================== */

.page-header {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; margin-bottom: 1.5rem;
  flex-wrap: wrap;
}
.page-title {
  font-size: 1.5rem; font-weight: 600;
  letter-spacing: -0.02em;
}
.page-subtitle {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  margin-top: 0.25rem;
}
.page-actions {
  display: flex; align-items: center; gap: 0.625rem;
}

.breadcrumbs {
  display: flex; align-items: center; gap: 0.5rem;
  font-size: 0.8125rem; color: var(--text-muted);
  margin-bottom: 0.875rem;
}
.breadcrumbs a { color: var(--text-secondary); }
.breadcrumbs a:hover { color: var(--text-primary); }
.breadcrumbs span { color: var(--text-muted); }

/* Phase J: lead-detail nav row — "← Back to leads · Prev | Next" */
.lead-detail-nav { gap: 0.625rem; }
.lead-detail-nav .nav-back { font-weight: 500; }
.lead-detail-nav .nav-pn { color: var(--text-secondary); }
.lead-detail-nav .nav-pn:hover { color: var(--text-primary); }
.lead-detail-nav .nav-sep { color: var(--text-muted); user-select: none; }

/* K.K.1.B — "Return to creating new lead" breadcrumb that appears on
   the lead detail page when the user clicked "View existing lead" from
   the new-lead collision panel. */
.pending-lead-banner {
  display: inline-flex;
  align-items: center;
  margin: 0 0 0.75rem;
  padding: 0.5rem 0.875rem;
  background: var(--pending-bg);
  border: 1px solid var(--pending-bd);
  color: var(--pending-fg);
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.pending-lead-banner:hover {
  background: var(--pending-hover-bg);
  border-color: rgba(59, 130, 246, 0.5);
}
.card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  margin-bottom: 1.25rem;
  overflow: hidden;
}
.card-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
}
.card-header h2, .card-header h3 {
  font-size: 1rem; font-weight: 600; letter-spacing: -0.01em;
}
.placeholder-card-inline {
  padding: 1.25rem;
  text-align: center;
  background: transparent;
  border: 1px dashed var(--border);
  box-shadow: none;
}

/* =====================================================
   EMPTY / LOADING
   ===================================================== */

.empty-state {
  padding: 3rem 1.5rem; text-align: center;
  color: var(--text-secondary);
}
.empty-state h2 {
  font-size: 1.125rem; font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 0.5rem;
}
.empty-state p { margin-bottom: 0.5rem; }
.empty-state.small { padding: 1.5rem; }

.loading {
  padding: 2rem; text-align: center;
  color: var(--text-muted); font-size: 0.875rem;
}

/* =====================================================
   SEGMENTED CONTROL (Calendar / List toggle)
   ===================================================== */

.segmented {
  display: inline-flex;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 3px;
}
.segmented-btn {
  background: transparent;
  border: none;
  padding: 0.4rem 0.875rem;
  font-size: 0.8125rem; font-weight: 500;
  color: var(--text-secondary);
  border-radius: calc(var(--radius-md) - 3px);
  cursor: pointer; font-family: inherit;
  transition: color .15s ease, background .15s ease;
}
.segmented-btn.is-active {
  background: var(--bg-1);
  color: var(--text-primary);
  box-shadow: var(--shadow-sm);
}

/* =====================================================
   CALENDAR
   ===================================================== */

.calendar-card { padding-bottom: 0.5rem; }
.calendar-header {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
}
.calendar-title {
  font-size: 1.0625rem; font-weight: 600;
  letter-spacing: -0.01em;
  flex: 1; text-align: center;
}
.calendar-header .icon-btn { flex-shrink: 0; }
.calendar-header .small { margin-left: 0.5rem; }

.calendar-legend {
  display: flex; flex-wrap: wrap;
  gap: 0.25rem 1.25rem;
  padding: 0.625rem 1.25rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.8125rem;
  color: var(--text-secondary);
}
.legend-item {
  display: inline-flex; align-items: center;
  gap: 0.5rem;
}

.calendar-grid { padding: 0.75rem 0.75rem 1rem; }
.cal-weekdays, .cal-days {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.cal-weekdays {
  padding-bottom: 0.5rem;
}
.cal-weekday {
  font-size: 0.75rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted);
  text-align: center; padding: 0.375rem 0;
}
.cal-day {
  aspect-ratio: 1 / 0.95;
  min-height: 76px;
  padding: 0.4rem 0.5rem 0.3rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  display: flex; flex-direction: column;
  background: var(--bg-1);
}
.cal-day.is-empty {
  background: transparent;
  border-color: transparent;
}
.cal-day.is-filled {
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}
.cal-day.is-filled:hover { border-color: var(--border-strong); background: var(--bg-2); }
.cal-day.is-today { border-color: var(--accent); }
.cal-day.is-today .cal-date { color: var(--accent); font-weight: 600; }
.cal-day.is-selected {
  background: var(--bg-3);
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft) inset;
}
.cal-date {
  font-size: 0.8125rem; font-weight: 500;
  color: var(--text-secondary);
}
.cal-dots {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: auto; padding-top: 0.5rem;
  align-items: center;
}
.dot-more {
  font-size: 0.6875rem; color: var(--text-muted); margin-left: 0.125rem;
}

/* =====================================================
   STATUS DOTS & BADGES
   ===================================================== */

.status-dot {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 999px;
  box-shadow: var(--status-dot-ring);
}
.dot-coral   { background: var(--st-coral); }
.dot-coral2  { background: var(--st-coral2); }
.dot-amber   { background: var(--st-amber); }
.dot-amber2  { background: var(--st-amber2); }
.dot-blue    { background: var(--st-blue); }
.dot-indigo  { background: var(--st-indigo); }
.dot-teal    { background: var(--st-teal); }
.dot-green   { background: var(--st-green); }
.dot-green2  { background: var(--st-green2); }
.dot-green3  { background: var(--st-green3); }
.dot-gray    { background: var(--st-gray); }
.dot-purple  { background: var(--st-purple); }

.badge {
  display: inline-flex; align-items: center;
  padding: 0.2rem 0.55rem;
  font-size: 0.75rem; font-weight: 500;
  letter-spacing: 0.01em;
  border-radius: 999px;
  background: var(--overlay-badge-bg);
  color: var(--text-secondary);
  border: 1px solid transparent;
  white-space: nowrap;
}
.badge-coral   { background: var(--badge-coral-bg); color: var(--badge-coral-fg); border-color: var(--badge-coral-bd); }
.badge-coral2  { background: var(--badge-coral2-bg); color: var(--badge-coral2-fg); border-color: var(--badge-coral2-bd); }
.badge-amber   { background: var(--badge-amber-bg); color: var(--badge-amber-fg); border-color: var(--badge-amber-bd); }
.badge-amber2  { background: var(--badge-amber2-bg); color: var(--badge-amber2-fg); border-color: var(--badge-amber2-bd); }
.badge-blue    { background: var(--badge-blue-bg); color: var(--badge-blue-fg); border-color: var(--badge-blue-bd); }
.badge-indigo  { background: var(--badge-indigo-bg); color: var(--badge-indigo-fg); border-color: var(--badge-indigo-bd); }
.badge-teal    { background: var(--badge-teal-bg); color: var(--badge-teal-fg); border-color: var(--badge-teal-bd); }
.badge-green   { background: var(--badge-green-bg); color: var(--badge-green-fg); border-color: var(--badge-green-bd); }
.badge-green2  { background: var(--badge-green2-bg); color: var(--badge-green2-fg); border-color: var(--badge-green2-bd); }
.badge-green3  { background: var(--badge-green3-bg); color: var(--badge-green3-fg); border-color: var(--badge-green3-bd); }
.badge-gray    { background: var(--badge-gray-bg); color: var(--badge-gray-fg); border-color: var(--badge-gray-bd); }
.badge-purple  { background: var(--badge-purple-bg); color: var(--badge-purple-fg); border-color: var(--badge-purple-bd); }

.chip {
  display: inline-flex; align-items: center;
  padding: 0.15rem 0.55rem;
  font-size: 0.75rem; font-weight: 500;
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  color: var(--text-secondary);
  border: 1px solid var(--border);
}

/* =====================================================
   LEAD ROW / DAY PANEL
   ===================================================== */

.lead-list {
  list-style: none;
  padding: 0.25rem;
}
.lead-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background .12s ease;
}
.lead-row:hover { background: var(--bg-2); }
.lead-row + .lead-row { border-top: 1px solid var(--border); }
.lead-name { font-weight: 500; }

/* RAP-213 — the name of a Lost lead, on every internal surface.

   var(--danger-text) is the text-safe red, NOT var(--danger) (a fill colour)
   and emphatically not var(--accent), which measures 2.95–3.49:1 as text on
   the three dark schemes. --danger-text is already defined for both
   polarities plus its own override on each high-contrast scheme, so all seven
   schemes are covered with no new token. Measured against every surface a
   lead name actually sits on (card --bg-1, hover/table --bg-2, active
   conversation row --bg-3), the worst pair is Sepia on --bg-3 at 4.77:1 —
   above the 4.5:1 AA floor; test:rap213 re-measures all 21 pairs so a future
   palette edit cannot quietly drop one below it.

   Colour only — no weight or decoration change. The name still has to read as
   the same name in a list scanned by shape, and the sr-only "(Lost)" in
   leadNameHtml() carries the meaning for anyone who can't see the hue. */
.lead-name-lost { color: var(--danger-text); }

.match-context {
  font-size: 0.75rem;
  font-style: italic;
  color: var(--text-muted);
  margin-top: 2px;
}

/* =====================================================
   DATA TABLE
   ===================================================== */

.table-wrap {
  overflow-x: auto;
}
.data-table {
  width: 100%; border-collapse: collapse;
  font-size: 0.875rem;
}
.data-table thead th {
  text-align: left;
  font-size: 0.75rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted);
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--border);
  background: var(--bg-2);
}
.data-table tbody td {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
  color: var(--text-primary);
  vertical-align: middle;
}
.data-table tbody tr {
  cursor: pointer;
  transition: background .12s ease;
}
.data-table tbody tr:hover { background: var(--bg-2); }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table .nowrap { white-space: nowrap; }

.pagination {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.875rem 1.25rem;
  border-top: 1px solid var(--border);
  font-size: 0.8125rem;
  gap: 0.75rem;
}
.pagination-controls {
  display: flex; align-items: center; gap: 0.5rem;
}
.page-indicator {
  color: var(--text-secondary);
  font-weight: 500;
  padding: 0 0.375rem;
}

/* =====================================================
   FILTER BAR
   ===================================================== */

/* Phase J: lifted into the page header so it sits above the Calendar/List
   toggle and is visible in both views. Flex-wrap so it accommodates the
   variable filter set (search + status + source + assigned [admin+] +
   date_from + date_to + page-size [list-only]) on any viewport. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  padding: 1rem 1.25rem;
  align-items: end;
}
.filter-bar .field.inline { flex: 0 0 auto; }
.filter-bar .filter-search { flex: 1 1 220px; min-width: 180px; }
.filter-bar input[type="date"],
.filter-bar select { min-width: 9rem; }

/* K.O follow-up — ONE row at desktop in BOTH views. List view's extra
   controls (Date from/to, Rows) used to wrap the bar to a second row,
   so the layout visibly jumped when switching Calendar ↔ List. Above
   the K.N mobile-collapse breakpoint the bar no longer wraps: the date
   inputs and the Rows select are compacted to fixed widths and the
   wide selects shrink instead of forcing a wrap. Mobile (≤820px)
   keeps the collapsed-filters behavior untouched. */
@media (min-width: 821px) {
  .filter-bar { flex-wrap: nowrap; gap: 0.625rem; }
  /* shrinkable, but never below the control's own min-width (the
     automatic flex minimum) — a wrapper squeezed below its control
     would let the control protrude and overflow the bar */
  .filter-bar .field.inline { flex: 0 1 auto; }
  .filter-bar .field.inline select {
    width: 100%;
    min-width: 5.5rem;
  }
  /* Date inputs render ~2px taller than selects; pin every control to
     one height so the bar is pixel-identical in both views. */
  .filter-bar .field.inline select,
  .filter-bar input[type="date"] {
    height: 42px;
  }
  .filter-bar input[type="date"] {
    width: 100%;
    min-width: 6.75rem;
    max-width: 8.5rem;
  }
  .filter-bar #flt-page-size {
    width: 4.75rem;
    min-width: 4.75rem;
  }
  .filter-bar .filter-search { flex: 1 1 200px; min-width: 118px; }
}

/* =====================================================
   LEAD DETAIL
   ===================================================== */

.lead-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1.5rem; margin-bottom: 1.5rem;
  flex-wrap: wrap;
}
.lead-header-main { flex: 1; min-width: 240px; }
.lead-header-actions {
  min-width: 240px;
  display: flex; flex-direction: column; gap: 0.375rem;
}
/* RAP-43 (prod Bug 2): the status line under the CRMLS auto-email button
   must never inflate the actions column. A long message gave the column a
   page-wide intrinsic (max-content) width; .lead-header wraps, so the
   column landed on its own full-width row and align-items:stretch turned
   every action button into a page-wide bar. width:0 removes the text from
   the intrinsic-width calculation; min-width:100% re-stretches the line to
   the column's resolved width at layout time so the text wraps inside it. */
#crmls-auto-email-msg { width: 0; min-width: 100%; }
/* RAP-73 — skipped-city callout on the CRMLS auto-email summary line. */
.crmls-skipped-cities { color: var(--warning, #e0a93e); }
/* RAP-87 — location-retry note on the same line. Deliberately NOT warning
   coloured: the search that got set up is correct, this only explains why
   the Matrix search reads differently from the lead's city list. */
.crmls-geocode-retry { color: var(--text-muted, #6b7280); }
.lead-meta {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 0.625rem;
  margin-top: 0.625rem;
  color: var(--text-secondary);
  font-size: 0.9375rem;
}
.lead-meta.small { font-size: 0.8125rem; }
.lead-meta strong { color: var(--text-primary); font-weight: 500; }

/* RAP-198 — the header phone dials / messages.

   THE RESTING COLOUR IS NOT var(--accent), AND THAT IS THE WHOLE POINT.
   Letting these inherit the base `a { color: var(--accent) }` was the first
   attempt and it is what the brief's "consistent with the CRM's link styling"
   literally asks for, but measuring the rendered page killed it: the accent
   red against --bg-0 lands at 2.95:1 in Dark, 3.27 Midnight, 3.49 Graphite —
   under the 4.5:1 floor, and a real regression, because this number renders
   at 7.49:1 today. Accent works in this app for button FILLS and for
   occasional inline links inside prose; a standing piece of contact
   information that Mauricio reads at a glance is neither. (Same shape as the
   RAP-179 --on-accent finding: the token table looks fine, the rendered page
   is what tells you.)

   So the affordance is an UNDERLINE and the colour goes UP, not down:
   --text-primary is more legible than the --text-secondary this line
   inherited before. Accent is still here — it is the hover/focus state, so
   the pointer story matches the rest of the app while the resting state
   stays readable. Everything is a var(), so all seven schemes are correct by
   construction rather than by a per-theme sweep (the RAP-179 lesson: the
   override is the thing that rots).
   The on-screen string is untouched; only the wrapping element changed. */
.lead-phone { display: inline-flex; align-items: center; gap: 0.375rem; }
.lead-phone-link {
  /* Keeps a formatted number from breaking across lines at 375px, so the tap
     target stays one rectangle instead of two fragments. */
  white-space: nowrap;
  color: var(--text-primary);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--text-muted);
  text-underline-offset: 0.2em;
}
.lead-phone-sms { display: inline-flex; align-items: center; justify-content: center; color: var(--text-secondary); }
.lead-phone-sms svg { width: 1rem; height: 1rem; display: block; }
.lead-phone-link:hover, .lead-phone-link:focus-visible,
.lead-phone-sms:hover, .lead-phone-sms:focus-visible {
  color: var(--accent);
  text-decoration-color: currentColor;
}

/* Thumb targets, on touch devices only — desktop geometry is untouched.
   Each control grows to Apple's 44x44 minimum and then hands the extra space
   straight back with an equal negative margin, so the header row keeps the
   exact height and rhythm it had before RAP-198. Measured: .lead-header is
   381.66px at 375px wide both with and without this change.
   The 16px icon needs a box, not padding, to reach 44 in BOTH axes — padding
   alone left it 24x38, which looks fine and misses under a thumb. */
@media (pointer: coarse) {
  .lead-phone-link {
    padding-block: 0.6875rem;
    margin-block: -0.6875rem;
  }
  .lead-phone-sms {
    width: 2.75rem; height: 2.75rem;
    margin-block: -0.75rem;
    /* Asymmetric on purpose. A symmetric -0.625rem gave the row its original
       width but slid the icon's box 4px OVER the number's, and the icon wins
       those pixels (later in the DOM) — so a thumb on the last digit opened
       Messages instead of dialling. The left margin now cancels the flex gap
       and nothing more, putting the icon's box exactly where the number's
       ends; the rest of the give-back is taken on the right, where the
       neighbour is plain text with no tap target to steal. */
    margin-left: -0.375rem;
    margin-right: -0.875rem;
  }
}

.status-msg { font-size: 0.8125rem; color: var(--text-muted); min-height: 1rem; }
.status-msg.ok { color: var(--success); }
.status-msg.error { color: var(--danger-text); }

.kv-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.25rem 1.5rem;
  padding: 1rem 1.25rem;
}
.kv {
  display: flex; flex-direction: column;
  gap: 0.25rem;
  padding: 0.625rem 0;
  border-bottom: 1px solid var(--border);
}
.kv:last-child, .kv:nth-last-child(2):not(:nth-child(odd)) { border-bottom: none; }
.kv-wide { grid-column: 1 / -1; }
.kv dt {
  font-size: 0.75rem;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); font-weight: 500;
}
.kv dd {
  font-size: 0.9375rem;
  color: var(--text-primary);
  word-break: break-word;
}

.checklist {
  padding: 0.5rem 0;
}
.checklist-item {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.625rem 1.25rem;
  color: var(--text-secondary);
  width: 100%;
  background: transparent;
  border: none;
  border-radius: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.checklist-item:hover:not(:disabled) {
  background: var(--bg-2);
  color: var(--text-primary);
}
.checklist-item:hover:not(:disabled):not(.is-done) .checklist-check {
  border-color: var(--accent);
}
.checklist-item:focus-visible {
  outline: none;
  background: var(--bg-2);
  box-shadow: inset 2px 0 0 var(--accent);
}
.checklist-item:disabled { cursor: wait; opacity: 0.65; }
.checklist-item.is-done { color: var(--text-primary); }
.checklist-check {
  width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 6px;
  font-size: 0.75rem; font-weight: 700;
  color: transparent;
  transition: background .15s ease, border-color .15s ease;
}
.checklist-item.is-done .checklist-check {
  background: var(--success);
  border-color: var(--success);
  color: white;
}
.checklist-label { flex: 1; }

/* =====================================================
   FORM GRID (Add Lead)
   ===================================================== */

.form-grid {
  padding: 1.25rem;
  display: flex; flex-direction: column; gap: 1.25rem;
}
.form-grid .fs {
  border: none;
  display: flex; flex-direction: column; gap: 0.875rem;
}
.form-grid legend {
  font-size: 0.75rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 0.5rem;
}
.grid-2 {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 0.875rem;
}
.grid-3 {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 0.875rem;
}
.form-actions {
  display: flex; justify-content: flex-end;
  gap: 0.625rem;
}
.form-actions .primary { width: auto; padding: 0.65rem 1.25rem; }

/* =====================================================
   INLINE FORMS (Add Note / Add Task / Add Appointment)
   ===================================================== */

.inline-form {
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 0.75rem;
}
.inline-form textarea,
.inline-form input,
.inline-form select {
  width: 100%;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.55rem 0.8125rem;
  color: var(--text-primary);
  font-size: 0.9375rem; font-family: inherit;
}
.inline-form textarea {
  resize: vertical;
  min-height: 72px;
}
.inline-form textarea:focus,
.inline-form input:focus,
.inline-form select:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-3);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.inline-form-actions {
  display: flex; justify-content: flex-end;
  gap: 0.5rem;
}
.inline-form .primary,
.inline-form-actions .primary {
  width: auto;
}

/* Subhead separator (e.g. Upcoming / Past, Leads / Appointments) */
.subhead {
  padding: 0.875rem 1.25rem 0.375rem;
  font-size: 0.6875rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-muted);
}
.subhead-muted { opacity: 0.75; }

/* =====================================================
   NOTES
   ===================================================== */

.notes-list {
  list-style: none;
  padding: 0.25rem 0;
}
.note {
  padding: 0.875rem 1.25rem;
  border-bottom: 1px solid var(--border);
}
.note:last-child { border-bottom: none; }
.note-meta {
  display: flex; align-items: center; gap: 0.5rem;
  margin-bottom: 0.375rem;
}
.note-author {
  font-weight: 500;
  font-size: 0.875rem;
  color: var(--text-primary);
}
.note-meta .icon-btn.note-delete {
  margin-left: auto;
  opacity: 0;
  transition: opacity .15s ease;
}
.note:hover .note-meta .icon-btn.note-delete { opacity: 1; }
.note-content {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  line-height: 1.55;
  white-space: pre-wrap;
  word-wrap: break-word;
}

/* K.H.A.3 — System-authored notes (notes.source IS NOT NULL). Muted background
   stripe + italic author so the eye can pick them out from manual notes. */
.note.note-system {
  background: var(--overlay-note-bg);
  border-left: 3px solid var(--accent, #6b8eff);
  padding-left: calc(1.25rem - 3px);
}
.note.note-system .note-author {
  font-style: italic;
  color: var(--text-secondary);
  font-weight: 600;
}

/* =====================================================
   TASKS
   ===================================================== */

.task-list {
  list-style: none;
  padding: 0.25rem 0;
}
.task {
  display: flex; align-items: flex-start; gap: 0.75rem;
  padding: 0.75rem 1.25rem;
  border-bottom: 1px solid var(--border);
  transition: background .12s ease;
}
.task:last-child { border-bottom: none; }
.task-toggle {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 6px;
  background: transparent;
  color: transparent;
  font-size: 0.8125rem; font-weight: 700;
  cursor: pointer; font-family: inherit;
  transition: background .15s ease, border-color .15s ease;
  margin-top: 2px;
}
.task-toggle:hover { border-color: var(--accent); }
.task.is-done .task-toggle {
  background: var(--success);
  border-color: var(--success);
  color: white;
}
.task-main { flex: 1; min-width: 0; }
.task-title {
  font-size: 0.9375rem;
  color: var(--text-primary);
  font-weight: 500;
  word-wrap: break-word;
}
.task.is-done .task-title {
  text-decoration: line-through;
  color: var(--text-muted);
}
.task-desc {
  margin-top: 0.2rem;
  word-wrap: break-word;
}
.task-meta {
  margin-top: 0.3rem;
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  align-items: center;
}
.overdue-badge {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  font-size: 0.6875rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  border-radius: 999px;
  background: var(--danger-soft);
  color: var(--danger-text);
  border: 1px solid var(--danger-line);
}
.task.is-overdue {
  background: var(--task-overdue-bg);
}
/* RAP-94 — Reschedule + delete now share a right-hand action group, so the
   `margin-left:auto` that used to push the lone delete button right lives on
   the container. The delete button keeps its reveal-on-hover behavior
   (unchanged); Reschedule is always visible, because it is a primary action
   on a phone, where :hover never fires. */
.task-actions {
  display: flex; align-items: center; gap: 0.25rem;
  margin-left: auto;
  flex-shrink: 0;
  margin-top: 2px;
}
.task .task-delete {
  opacity: 0;
  transition: opacity .15s ease;
  flex-shrink: 0;
}
.task:hover .task-delete { opacity: 1; }
.task-edit { margin-top: 0.6rem; }

/* RAP-94 — reminder settings card checkbox. The existing .checkbox-inline and
   .checkbox-row rules are both scoped to other components, so this is a local
   rule rather than a change to theirs. */
label.reminders-check {
  display: inline-flex; align-items: center; gap: 0.5rem;
  cursor: pointer;
  color: var(--text-primary);
}
label.reminders-check input[type="checkbox"] {
  width: 16px; height: 16px;
  accent-color: var(--accent, #6366f1);
  cursor: pointer;
}

/* RAP-194 — phone-notification settings card.
   Every colour here is a token, never a literal: this card has to render on
   all seven RAP-179 schemes, and a hardcoded dark value would be invisible
   on the light-polarity ones. */
.push-types { margin-top: 0.75rem; }
.push-types-title {
  font-size: 0.95rem; font-weight: 600; margin: 0 0 0.25rem;
  color: var(--text-primary);
}
.push-type-group { margin-top: 0.9rem; }
.push-type-group-title {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-muted);
  margin: 0 0 0.4rem;
}
label.push-type-row {
  display: flex; align-items: flex-start; gap: 0.55rem;
  /* Generous vertical padding: this is a list of small checkboxes that gets
     used on a phone, where a tight row is a mis-tap. */
  padding: 0.45rem 0;
  cursor: pointer;
  color: var(--text-primary);
  line-height: 1.35;
}
label.push-type-row input[type="checkbox"] {
  width: 16px; height: 16px;
  margin-top: 0.15rem;
  accent-color: var(--accent, #6366f1);
  cursor: pointer;
  flex-shrink: 0;
}
.push-ios-steps {
  margin: 0.5rem 0 0.5rem 1.1rem;
  padding: 0;
  line-height: 1.55;
}
.push-ios-steps li { margin-bottom: 0.2rem; }

/* =====================================================
   APPOINTMENTS
   ===================================================== */

.appt-list {
  list-style: none;
  padding: 0.25rem 0;
}
.appt-list.is-past { opacity: 0.65; }
.appt {
  display: flex; align-items: center; gap: 1rem;
  padding: 0.75rem 1.25rem;
  border-bottom: 1px solid var(--border);
}
.appt:last-child { border-bottom: none; }
.appt-date {
  width: 52px; flex-shrink: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 0.35rem 0;
  border-radius: var(--radius-md);
  background: var(--bg-2);
  border: 1px solid var(--border);
  line-height: 1;
}
.appt-date.type-teal   { border-color: var(--appt-teal-bd);  background: var(--appt-teal-bg); }
.appt-date.type-indigo { border-color: var(--appt-indigo-bd);  background: var(--appt-indigo-bg); }
.appt-date.type-gray   { border-color: var(--appt-gray-bd); background: var(--appt-gray-bg); }
.appt-month {
  font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 2px;
}
.appt-day {
  font-size: 1.125rem; font-weight: 600;
  color: var(--text-primary);
}
.appt-main { flex: 1; min-width: 0; }
.appt-title {
  display: flex; align-items: center; gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 0.9375rem;
  color: var(--text-primary);
  font-weight: 500;
}
.appt-meta { margin-top: 0.25rem; }
.appt-desc {
  margin-top: 0.3rem;
  color: var(--text-secondary);
  word-wrap: break-word;
}

/* RAP-94 — Reschedule / delete controls on an appointment row. The row is
   align-items:center, which reads fine for a one-line row but pins the
   buttons to the vertical middle of an expanded reschedule form; align them
   to the top so they stay next to the title they belong to. */
.appt { align-items: flex-start; }
.appt-actions {
  display: flex; align-items: center; gap: 0.25rem;
  flex-shrink: 0;
}
.appt-edit { margin-top: 0.6rem; }

/* Compact variant used inside the day-panel on the calendar view. */
.appt-list-compact .lead-row {
  padding: 0.6rem 1rem;
}

/* =====================================================
   APPOINTMENT MARKERS (calendar day cells)
   ===================================================== */

.cal-appts {
  display: flex; flex-wrap: wrap; gap: 3px;
  padding-top: 3px;
  align-items: center;
}
.appt-marker {
  display: inline-block;
  width: 10px; height: 5px;
  border-radius: 2px;
  box-shadow: var(--appt-marker-ring);
}
.mk-teal   { background: var(--st-teal); }
.mk-indigo { background: var(--st-indigo); }
.mk-gray   { background: var(--st-gray); }

/* =====================================================
   DASHBOARD
   ===================================================== */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 0.875rem;
  margin-bottom: 1.25rem;
}
.stat-card {
  display: block;
  padding: 1.125rem 1.25rem;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--text-primary);
  box-shadow: var(--shadow-sm);
  transition: border-color .15s ease, transform .05s ease, background .15s ease;
}
.stat-card:hover {
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.stat-card:active { transform: translateY(1px); }
.stat-card.is-accent { border-color: var(--accent-strong); background: var(--accent-soft); }
.stat-card.is-alert  { border-color: rgba(239,68,68,.3);   background: rgba(239,68,68,.05); }
.stat-label {
  font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.5rem;
}
.stat-value {
  font-size: 1.875rem; font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--text-primary);
}
.stat-card.is-alert .stat-value { color: var(--danger-text); }
.stat-hint {
  margin-top: 0.25rem;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.pipeline-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1px;
  background: var(--border);
}
.pipeline-cell {
  display: flex; align-items: center; gap: 0.625rem;
  padding: 0.9rem 1.25rem;
  background: var(--bg-1);
  text-decoration: none;
  color: var(--text-primary);
  font-size: 0.9375rem;
  transition: background .15s ease;
}
.pipeline-cell:hover {
  background: var(--bg-2);
  color: var(--text-primary);
}
.pipeline-cell.is-empty { color: var(--text-muted); }
.pipeline-dot { flex-shrink: 0; }
.pipeline-label { flex: 1; }
.pipeline-count {
  font-weight: 600;
  color: var(--text-secondary);
  min-width: 1.5rem;
  text-align: right;
}
.pipeline-cell:hover .pipeline-count { color: var(--text-primary); }

/* =====================================================
   MODAL
   ===================================================== */

.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  display: flex; align-items: center; justify-content: center;
  padding: 1.5rem;
  animation: modalFade 0.15s ease;
}
@keyframes modalFade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.modal {
  width: 100%; max-width: 480px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 1.5rem;
  animation: modalIn 0.18s cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: none; }
}
.modal-title {
  font-size: 1.125rem; font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 0.75rem;
}
.modal-body {
  color: var(--text-secondary);
  margin-bottom: 1rem;
  font-size: 0.9375rem;
  line-height: 1.55;
}
.modal-body p + p { margin-top: 0.625rem; }
.modal-body p strong { color: var(--text-primary); font-weight: 500; }
.modal-body .field { margin-bottom: 0; }
.modal-body form.stack > * + * { margin-top: 0.875rem; }
.modal-error { margin-bottom: 0.75rem; }
.modal-actions {
  display: flex; justify-content: flex-end;
  gap: 0.5rem;
}
.modal-actions .primary,
.modal-actions .ghost { width: auto; padding: 0.55rem 1rem; }

/* =====================================================
   DANGER BUTTON VARIANTS
   ===================================================== */

.primary.is-danger {
  background: var(--danger);
}
.primary.is-danger:hover:not(:disabled) {
  background: #dc2626;
}

.ghost.is-danger {
  color: var(--danger-text);
  border-color: rgba(239, 68, 68, 0.35);
}
.ghost.is-danger:hover:not(:disabled) {
  background: var(--danger-soft);
  color: var(--danger-text-hover);
  border-color: rgba(239, 68, 68, 0.55);
}

/* =====================================================
   INLINE EDIT SLOTS
   ===================================================== */

.inline-edit-slot {
  display: inline-block;
  width: 100%;
  min-width: 0;
}
.inline-edit-slot.is-editing {
  display: block;
}

.inline-edit {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.25rem 0.5rem;
  margin: -0.25rem -0.5rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  max-width: 100%;
  transition: background .12s ease, border-color .12s ease;
}
.inline-edit:hover {
  background: var(--bg-2);
  border-color: var(--border);
}
.inline-edit:focus-visible {
  outline: none;
  background: var(--bg-2);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.inline-edit-value {
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}
.inline-edit-value a { color: inherit; }
.inline-edit-placeholder {
  color: var(--text-muted);
  font-style: italic;
  /* Italic glyphs slant past their character box; the parent's
     overflow:hidden (for ellipsis truncation) clips the trailing char
     without this breathing room. */
  padding-inline-end: 0.15rem;
}
.inline-edit-pencil {
  font-size: 0.75rem;
  color: var(--text-muted);
  opacity: 0;
  transition: opacity .12s ease;
  flex-shrink: 0;
}
.inline-edit:hover .inline-edit-pencil,
.inline-edit:focus-visible .inline-edit-pencil { opacity: 1; }

.inline-edit-form {
  display: flex; flex-direction: column;
  gap: 0.5rem;
  padding: 0.25rem 0;
}
.inline-edit-control {
  width: 100%;
  background: var(--bg-2);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 0.45rem 0.625rem;
  color: var(--text-primary);
  font: inherit;
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.inline-edit-control:focus { outline: none; }
textarea.inline-edit-control { resize: vertical; min-height: 72px; }
select.inline-edit-control {
  appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'><path d='M1 1L5 5L9 1' stroke='%239aa0ac' stroke-width='1.5' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.625rem center;
  padding-right: 1.75rem;
}
.inline-edit-error { margin: 0; }
.inline-edit-actions {
  display: flex;
  gap: 0.375rem;
}
/* Override the form-wide "button.primary fills the row" rule set by the
   login form — inline edits need Save + Cancel side-by-side. Save takes
   the remaining space; Cancel sizes to its text so "Cancel" never wraps. */
.inline-edit-actions .primary {
  width: auto;
  flex: 1;
}
.inline-edit-actions .ghost {
  flex-shrink: 0;
  white-space: nowrap;
}

/* =====================================================
   DANGER ZONE (lead detail)
   ===================================================== */

.danger-zone {
  border-color: rgba(239, 68, 68, 0.25);
  background: var(--danger-zone-bg);
}
.danger-zone .card-header {
  border-bottom-color: rgba(239, 68, 68, 0.2);
}
.danger-zone-body {
  padding: 1rem 1.25rem;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
}
.danger-zone-copy { flex: 1; min-width: 220px; }
.danger-zone-title {
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: 0.25rem;
}
/* K.L — second row in the danger zone gets a top divider */
.danger-zone .danger-zone-body + .danger-zone-body {
  border-top: 1px solid rgba(239, 68, 68, 0.15);
}

/* =====================================================
   ABSORBED-LEAD BANNER (Phase K.L.D.2)
   ===================================================== */
.absorbed-lead-banner {
  margin: 0.75rem 0;
  padding: 0.875rem 1rem;
}
.absorbed-lead-banner-body {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
}
.absorbed-lead-banner-body strong { color: var(--text-primary); }
.absorbed-lead-banner-body .button {
  flex-shrink: 0;
  text-decoration: none;
  padding: 0.5rem 0.875rem;
  border-radius: var(--radius-sm);
}

/* =====================================================
   MERGE LEAD MODAL (Phase K.L)
   ===================================================== */

.modal.modal-wide {
  max-width: 640px;
  width: 100%;
}

.merge-search-input {
  width: 100%;
  padding: 0.6rem 0.75rem;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 0.9375rem;
  font-family: inherit;
  margin: 0.5rem 0 0.75rem;
}
.merge-search-input:focus {
  outline: none;
  border-color: var(--accent, #6366f1);
}
.merge-results {
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-2);
}
.merge-results .muted { padding: 0.75rem; }
.merge-results .alert.error { margin: 0.5rem; }
.merge-candidate-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--border);
}
.merge-candidate-row:last-child { border-bottom: none; }
.merge-candidate-main { flex: 1; min-width: 0; }
.merge-candidate-name {
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: 0.125rem;
}
.merge-candidate-meta {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  font-size: 0.75rem;
}

.merge-preview-pair {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.merge-preview-side {
  padding: 0.75rem;
  background: var(--bg-2);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}
.merge-preview-absorbed {
  border-color: rgba(239, 68, 68, 0.25);
  background: rgba(239, 68, 68, 0.04);
}
.merge-side-name {
  font-weight: 500;
  margin: 0.25rem 0;
  color: var(--text-primary);
}
.merge-preview-arrow {
  font-size: 1.25rem;
  color: var(--text-secondary);
}
.merge-section-h {
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  margin: 1rem 0 0.5rem;
}
.merge-counts {
  list-style: none;
  padding: 0;
  margin: 0 0 0.5rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 0.25rem 1rem;
}
.merge-counts li { font-size: 0.875rem; }
.merge-conflicts {
  margin: 0.5rem 0;
  padding-left: 1.25rem;
  font-size: 0.875rem;
}
.merge-conflicts code {
  font-family: ui-monospace, Menlo, Monaco, monospace;
  font-size: 0.8125rem;
  background: var(--bg-2);
  padding: 0.0625rem 0.25rem;
  border-radius: 3px;
}
.merge-inheritances { margin-top: 0.5rem; }
.merge-undo-note {
  margin-top: 1rem;
  padding: 0.625rem 0.75rem;
  background: rgba(245, 158, 11, 0.08);
  border: 1px solid rgba(245, 158, 11, 0.25);
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
}

/* =====================================================
   MERGE UNDO TOAST (Phase K.L.C.6)
   ===================================================== */
.merge-undo-toast {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.625rem 1rem;
  display: flex; align-items: center; gap: 0.75rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
  z-index: 10000;
  max-width: 90vw;
  font-size: 0.875rem;
}
.merge-undo-counter {
  background: var(--bg-2);
  padding: 0.125rem 0.5rem;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  color: var(--text-secondary);
}
.merge-undo-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-primary);
  padding: 0.25rem 0.625rem;
  border-radius: 999px;
  cursor: pointer;
  font: inherit;
}
.merge-undo-btn:hover { background: var(--bg-2); }
.merge-undo-close {
  background: transparent;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 1.125rem;
  line-height: 1;
  padding: 0 0.25rem;
}
.merge-undo-err {
  color: var(--danger, #ef4444);
  font-size: 0.75rem;
  margin-left: 0.5rem;
}

/* =====================================================
   TEAM PAGE
   ===================================================== */

.team-name {
  display: flex; align-items: center; gap: 0.75rem;
}
.team-actions {
  display: flex; align-items: center; gap: 0.375rem;
  justify-content: flex-end;
}
.role-select {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.35rem 1.75rem 0.35rem 0.625rem;
  color: var(--text-primary);
  font-size: 0.8125rem;
  font-family: inherit;
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'><path d='M1 1L5 5L9 1' stroke='%239aa0ac' stroke-width='1.5' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.5rem center;
  transition: border-color .15s ease;
}
.role-select:hover:not(:disabled) { border-color: var(--border-strong); }
.role-select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.role-select:disabled { opacity: 0.6; cursor: wait; }

/* K.F.7.C.4 — permission toggle (checkbox styled as switch) */
.perm-toggle {
  display: inline-flex; align-items: center;
  cursor: pointer;
}
/* RBAC (2026-07) — per-admin team-member picker (Team detail drawer). */
.team-member-list { display: flex; flex-direction: column; gap: 0.25rem; }
.team-member-option {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.35rem 0.5rem; border-radius: 8px; cursor: pointer;
}
.team-member-option:hover { background: var(--bg-2); }
.team-member-option input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.perm-toggle input[type="checkbox"] {
  appearance: none;
  width: 36px; height: 20px;
  background: var(--bg-3, var(--bg-2));
  border: 1px solid var(--border);
  border-radius: 999px;
  position: relative;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
  margin: 0;
}
.perm-toggle input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 1px; left: 1px;
  width: 16px; height: 16px;
  background: var(--text-secondary, #9aa0ac);
  border-radius: 50%;
  transition: transform .15s ease, background-color .15s ease;
}
.perm-toggle input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
.perm-toggle input[type="checkbox"]:checked::after {
  transform: translateX(16px);
  background: #fff;
}
.perm-toggle input[type="checkbox"]:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.perm-toggle input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* =====================================================
   TEAM — slim table rows + detail drawer (2026-07)
   ===================================================== */

/* The base .data-table is width:100%, which stretched these four short
   columns across the whole card and inflated the inter-column gaps. Size the
   team table to its content and left-align it, so leftover width collects on
   the right instead of being distributed between columns. .table-wrap keeps
   overflow-x:auto, so narrow viewports still scroll. */
.data-table.team-table { width: auto; }
/* ...but the table lives in a block .card + .table-wrap, both of which still
   filled the container — leaving a half-empty box with a hard right edge and a
   stranded header underline. Make the team card (and its wrap) hug the table:
   fit-content shrinks the box to the table's width; max-width keeps it from
   sprawling on very long names and clamps to 100% so narrow viewports still
   scroll the table inside the wrap. Scoped to #team-body so the shared .card
   component is untouched elsewhere. */
#team-body > .card { width: fit-content; max-width: min(100%, 60rem); }
#team-body > .card > .table-wrap { width: fit-content; max-width: 100%; }
/* Tighten the column gaps (gap = right-padding of a cell + left-padding of the
   next). 0.75rem each ≈ 24px between columns — cohesive but not cramped. */
.data-table.team-table thead th,
.data-table.team-table tbody td {
  padding-left: 0.75rem;
  padding-right: 0.75rem;
}
/* Keep the first column breathing against the card edge, and let the trailing
   chevron hug Last login rather than floating far right. */
.data-table.team-table thead th:first-child,
.data-table.team-table tbody td:first-child { padding-left: 1rem; }
.data-table.team-table thead th:last-child,
.data-table.team-table tbody td:last-child { padding-left: 0.4rem; }

/* Clickable rows: hover highlight already comes from .data-table tbody tr:hover.
   Add the pointer + a visible chevron + keyboard focus ring. */
.data-table.team-table tbody tr.is-clickable { cursor: pointer; }
.data-table.team-table tbody tr.is-clickable:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent-soft);
}
/* RAP-222 — a deactivated row is dimmed but NOT hidden or struck through:
   it is still clickable (that is how access gets restored) and its assigned-
   leads count is still the number that matters. Opacity only, so every
   colour token keeps whatever contrast its scheme gave it — the RAP-179 rule
   about not hardcoding a colour that has to work in seven palettes. The row
   returns to full strength on hover so it never reads as disabled. */
.data-table.team-table tbody tr.is-deactivated { opacity: 0.62; }
.data-table.team-table tbody tr.is-deactivated:hover,
.data-table.team-table tbody tr.is-deactivated:focus-visible { opacity: 1; }

.team-filter-bar {
  display: flex;
  justify-content: flex-end;
  padding: 0.75rem 1rem 0;
}
.team-filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.875rem;
  color: var(--text-secondary);
  cursor: pointer;
}
.team-filter-toggle input { cursor: pointer; }

.team-row-chevron {
  color: var(--text-muted);
  font-size: 1.25rem;
  text-align: right;
  width: 1.5rem;
  transition: color .15s ease, transform .15s ease;
}
.data-table.team-table tbody tr:hover .team-row-chevron {
  color: var(--text-secondary);
  transform: translateX(2px);
}

.drawer-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  display: flex; justify-content: flex-end;
  animation: modalFade 0.15s ease;
}
.drawer {
  width: 100%; max-width: 420px; height: 100%;
  background: var(--bg-1);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
  animation: drawerIn 0.2s cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes drawerIn {
  from { transform: translateX(28px); opacity: 0.5; }
  to   { transform: none; opacity: 1; }
}
.drawer-header {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 1.25rem 1.25rem 1rem;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.drawer-avatar { width: 40px; height: 40px; font-size: 0.9375rem; flex-shrink: 0; }
.drawer-head-text { flex: 1; min-width: 0; }
.drawer-name { font-size: 1.0625rem; font-weight: 500; margin: 0; color: var(--text-primary); }
.drawer-email { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drawer-close {
  background: none; border: none; cursor: pointer;
  color: var(--text-secondary); font-size: 1.5rem; line-height: 1;
  width: 32px; height: 32px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  transition: background-color .15s ease, color .15s ease;
  flex-shrink: 0;
}
.drawer-close:hover { background: var(--bg-hover); color: var(--text-primary); }
.drawer-close:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }

.drawer-content {
  flex: 1; overflow-y: auto;
  padding: 1.25rem;
  display: flex; flex-direction: column; gap: 1.5rem;
}
.drawer-section h3 {
  font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); margin: 0 0 0.75rem; font-weight: 600;
}
.drawer-perm-list { display: flex; flex-direction: column; }
.drawer-perm-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 0.625rem 0;
}
.drawer-perm-row + .drawer-perm-row { border-top: 1px solid var(--border); }
.drawer-perm-label { font-size: 0.875rem; color: var(--text-primary); }
.drawer-meta {
  display: grid; grid-template-columns: auto 1fr; gap: 0.5rem 1rem;
  font-size: 0.8125rem; margin: 0;
}
.drawer-meta dt { color: var(--text-muted); }
.drawer-meta dd { margin: 0; color: var(--text-secondary); text-align: right; }
.drawer-actions {
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--border);
  display: flex; gap: 0.5rem; flex-shrink: 0;
}
.drawer-actions button { flex: 1; }
.team-save-status { margin-top: 0.5rem; min-height: 1rem; }

@media (max-width: 480px) {
  .drawer { max-width: 100%; }
}

/* =====================================================
   PROPERTIES (lead detail)
   ===================================================== */

.property-list {
  padding: 0.75rem;
  display: flex; flex-direction: column;
  gap: 0.75rem;
}
.property-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-2);
  padding: 1rem 1.125rem;
}
.property-card-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; margin-bottom: 0.5rem;
}
.property-address { flex: 1; min-width: 0; }
.property-title {
  font-size: 0.9375rem; font-weight: 500;
  color: var(--text-primary);
  word-wrap: break-word;
}
.property-card-actions {
  display: flex; align-items: center; gap: 0.5rem;
  flex-shrink: 0;
}

.property-status-wrap {
  display: inline-flex;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: background .12s ease;
}
.property-status-select {
  background: transparent;
  border: none;
  font: inherit;
  font-size: 0.75rem; font-weight: 500;
  padding: 0.2rem 1.75rem 0.2rem 0.625rem;
  border-radius: 999px;
  color: inherit;
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'><path d='M1 1L5 5L9 1' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' opacity='0.6'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.5rem center;
}
.property-status-select:focus { outline: none; }
.property-status-select:focus-visible { box-shadow: 0 0 0 3px var(--accent-soft); }

.property-status-gray  { background: rgba(107,114,128,.14); color: #d1d5db; border-color: rgba(107,114,128,.3); }
.property-status-amber { background: rgba(245,158,11,.12); color: #fcd34d; border-color: rgba(245,158,11,.25); }
.property-status-blue  { background: rgba(59,130,246,.14); color: #93c5fd; border-color: rgba(59,130,246,.28); }
.property-status-green { background: rgba(52,211,153,.14); color: #6ee7b7; border-color: rgba(52,211,153,.28); }
.property-status-coral { background: rgba(248,113,113,.14); color: #fca5a5; border-color: rgba(248,113,113,.28); }

.property-card-body {
  display: flex; flex-direction: column; gap: 0.375rem;
}
.property-link a { font-size: 0.875rem; }
/* RAP-128 — the `.property-notes` rule that used to sit here is gone.
   It styled a lead-detail property card that no longer exists, and the only
   element left in the app matching the bare `.property-notes` selector is the
   property page's Notes CARD (propertyDetail.js:927, `class="card property-notes"`).
   Its `white-space: pre-wrap` was therefore rendering the template's own newlines
   between the card header and the notes grid as a ~42px band of empty space. That
   was survivable while the card held three fields; with the card down to a single
   Admin notes field it read as an orphaned empty section, which is exactly what
   this change was supposed to remove. The card's typography comes from `.card`,
   `.property-notes-label-*` and the textarea rules in properties.css — none of it
   depended on this block. */

/* =====================================================
   DOCUMENTS (lead detail)
   ===================================================== */

.documents-contact-block {
  border-top: 1px solid var(--border);
}
.documents-contact-block:first-child {
  border-top: none;
}
.documents-contact-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 1.25rem 0;
  flex-wrap: wrap;
}
.documents-contact-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
}

.documents-toolbar {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.5rem 1.25rem 0.75rem;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.documents-contact-block:last-child .documents-toolbar + .documents-grid {
  border-bottom: none;
}
.documents-grid {
  padding: 0.75rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 0.5rem;
}
.doc-slot {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-2);
  color: var(--text-primary);
  transition: border-color .12s ease, background .12s ease;
  min-width: 0;
  overflow: hidden;
}
.doc-slot.is-empty { opacity: 0.75; }
.doc-slot.is-uploaded:hover { border-color: var(--accent); }

/* Main click target — view the document inline. On empty slots this
   element is a non-interactive <div> with the same layout. */
.doc-slot-view {
  flex: 1;
  min-width: 0;
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.75rem 0.875rem;
  background: transparent;
  border: none;
  color: inherit;
  font: inherit;
  text-align: left;
}
.doc-slot.is-uploaded .doc-slot-view {
  cursor: pointer;
  transition: background .12s ease;
}
.doc-slot.is-uploaded .doc-slot-view:hover { background: var(--bg-3); }
.doc-slot.is-uploaded .doc-slot-view:focus-visible {
  outline: none;
  background: var(--bg-3);
  box-shadow: inset 2px 0 0 var(--accent);
}

/* Download icon button on the right side of uploaded slots. */
.doc-slot-download {
  flex-shrink: 0;
  width: 44px;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: none;
  border-left: 1px solid var(--border);
  color: var(--text-muted);
  cursor: pointer;
  transition: color .12s ease, background .12s ease;
}
.doc-slot-download:hover {
  color: var(--text-primary);
  background: var(--bg-3);
}
.doc-slot-download:focus-visible {
  outline: none;
  color: var(--accent);
  background: var(--bg-3);
  box-shadow: inset 2px 0 0 var(--accent);
}
.doc-slot-download svg { width: 18px; height: 18px; }

/* bugfix-doc-delete-capability — trash button, same shell as download
   button but with a destructive (red) hover state to signal the action. */
.doc-slot-delete {
  flex-shrink: 0;
  width: 44px;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: none;
  border-left: 1px solid var(--border);
  color: var(--text-muted);
  cursor: pointer;
  transition: color .12s ease, background .12s ease;
}
.doc-slot-delete:hover {
  color: var(--danger, #ef4444);
  background: var(--bg-3);
}
.doc-slot-delete:focus-visible {
  outline: none;
  color: var(--danger, #ef4444);
  background: var(--bg-3);
  box-shadow: inset 2px 0 0 var(--danger, #ef4444);
}
.doc-slot-delete svg { width: 18px; height: 18px; }

/* Phase Employment-Types.3 — custom-slot controls. */
/* "Custom" chip next to a custom slot's label. */
.doc-slot-chip {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.4rem;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 999px;
  vertical-align: middle;
}
/* RAP-235 remediation — a slot that is no longer required but still holds an
   upload. Rendered so it reads as a record rather than a to-do: the chip is
   muted instead of accent-coloured, and the tile is dimmed by OPACITY only, so
   all seven RAP-179 schemes keep their own contrast (the RAP-222 rule for
   deactivated users, applied to the same problem). */
.doc-slot-chip-retired {
  color: var(--text-secondary);
  background: var(--bg-3);
}
.doc-slot.is-retired { opacity: 0.72; }

/* Remove (×) button on custom slot tiles — removes the slot definition
   (gated server-side when a doc is uploaded). Same right-rail shell as the
   download/delete buttons, narrower. */
.doc-slot-remove {
  flex-shrink: 0;
  width: 34px;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: none;
  border-left: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: color .12s ease, background .12s ease;
}
.doc-slot-remove:hover {
  color: var(--danger, #ef4444);
  background: var(--bg-3);
}
.doc-slot-remove:focus-visible {
  outline: none;
  color: var(--danger, #ef4444);
  background: var(--bg-3);
  box-shadow: inset 2px 0 0 var(--danger, #ef4444);
}
/* "+ Add custom slot" tile — a dashed, muted variant of .doc-slot that
   reads as an action rather than a document. */
.doc-slot.doc-slot-add {
  cursor: pointer;
  border-style: dashed;
  background: transparent;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 0.875rem;
  color: var(--text-muted);
  text-align: left;
  font: inherit;
  opacity: 1;
}
.doc-slot.doc-slot-add:hover {
  border-color: var(--accent);
  color: var(--text-primary);
  background: var(--bg-2);
}
.doc-slot.doc-slot-add:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.doc-slot-add-plus {
  width: 28px; height: 28px;
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px dashed var(--border-strong, var(--border));
  border-radius: 999px;
  font-size: 1.1rem;
  line-height: 1;
}

.doc-slot-check {
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--success);
  color: white;
  border-radius: 999px;
  font-weight: 700;
  font-size: 0.9375rem;
  flex-shrink: 0;
}
.doc-slot-check-empty {
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1.5px dashed var(--border-strong);
  color: var(--text-muted);
  border-radius: 999px;
  flex-shrink: 0;
}
.doc-slot-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.doc-slot-label {
  font-size: 0.9375rem;
  font-weight: 500;
}
.doc-slot-meta {
  margin-top: 0.15rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* =====================================================
   PUBLIC CLIENT UPLOAD PAGE
   ===================================================== */

.client-wrap {
  min-height: 100vh;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 2.5rem 1.25rem;
  background:
    radial-gradient(ellipse 900px 600px at 50% -10%, var(--page-gradient-1), transparent 70%),
    radial-gradient(ellipse 600px 400px at 85% 110%, var(--page-gradient-2), transparent 70%),
    var(--bg-0);
}
.client-card {
  width: 100%; max-width: 560px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 2rem;
  box-shadow: var(--shadow-lg);
}
.client-brand {
  display: flex; justify-content: center;
  margin-bottom: 1.75rem;
  color: var(--text-primary);
}
.client-brand .rr-logo {
  width: 100%;
  max-width: 280px;
  height: auto;
}
/* RAP-261 item 1 — the uploader's way back to the hub. Rendered only when
   the page was opened from there. --text-secondary rather than --accent:
   RAP-179 measured var(--accent) as text at 2.95-3.49:1 on the dark schemes,
   under the 4.5:1 AA floor a link needs. */
.client-back-link {
  display: inline-block;
  margin-bottom: 1rem;
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.9375rem;
  font-weight: 600;
}
.client-back-link:hover { color: var(--text-primary); text-decoration: underline; }

.client-greeting {
  font-size: 1.375rem; font-weight: 600;
  letter-spacing: -0.02em;
  margin-bottom: 0.5rem;
}
.client-intro p { color: var(--text-secondary); }
.client-intro { margin-bottom: 1.5rem; }

.client-progress {
  margin-top: 1rem;
  display: flex; align-items: center; gap: 0.75rem;
}
.client-progress-bar {
  flex: 1;
  height: 6px;
  background: var(--bg-2);
  border-radius: 999px;
  overflow: hidden;
}
.client-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent-hover) 100%);
  transition: width .3s ease;
}

.client-success {
  display: flex; align-items: center; gap: 0.875rem;
  padding: 1rem 1.25rem; margin-bottom: 1.25rem;
  background: var(--client-success-bg);
  border: 1px solid var(--client-success-bd);
  border-radius: var(--radius-md);
}
.client-success-mark {
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--success); color: white;
  border-radius: 999px; font-weight: 700;
  flex-shrink: 0;
}
.client-success h3 { font-size: 1rem; font-weight: 600; margin-bottom: 0.125rem; color: var(--client-success-fg); }
.client-success p { font-size: 0.875rem; margin: 0; }

.client-list {
  list-style: none; padding: 0;
  display: flex; flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}
.client-item {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-2);
  transition: border-color .12s ease, background .12s ease;
}
.client-item:hover:not(.is-busy) { border-color: var(--border-strong); }
.client-item.is-done { border-color: rgba(16, 185, 129, 0.35); background: rgba(16, 185, 129, 0.04); }
.client-item.is-busy { opacity: 0.75; }

.client-item-label {
  display: flex; align-items: center; gap: 0.875rem;
  padding: 0.875rem 1rem;
  cursor: pointer;
}
.client-item.is-busy .client-item-label { cursor: wait; }
.client-item-label input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}
.client-item-check {
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--border-strong);
  border-radius: 999px;
  color: white; font-weight: 700; font-size: 0.9375rem;
  flex-shrink: 0;
  transition: background .15s ease, border-color .15s ease;
}
.client-item.is-done .client-item-check {
  background: var(--success);
  border-color: var(--success);
}
.client-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.client-item-title { font-weight: 500; color: var(--text-primary); }
.client-item-meta { font-size: 0.8125rem; color: var(--text-secondary); margin-top: 0.125rem; }
.client-item-action { flex-shrink: 0; }
.client-item-upload, .client-item-replace {
  display: inline-flex; align-items: center;
  padding: 0.375rem 0.75rem;
  border-radius: var(--radius-sm);
  font-size: 0.8125rem; font-weight: 500;
  background: var(--accent-soft);
  color: var(--avatar-text);
  border: 1px solid var(--accent-strong);
}
.client-item.is-done .client-item-replace {
  background: transparent;
  color: var(--text-secondary);
  border-color: var(--border);
}
/* 2026-07 lock-on-upload — slot past its 45s replace window. */
.client-item.is-locked .client-item-label { cursor: default; }
.client-item-locked { font-size: 1rem; opacity: 0.7; }
.client-replace-countdown { font-variant-numeric: tabular-nums; }

.client-spinner {
  display: inline-block;
  width: 16px; height: 16px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 999px;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.client-footnote { margin-top: 0.5rem; text-align: center; }

.client-error {
  text-align: center;
  padding: 1.5rem 0;
}
.client-error h2 { font-size: 1.125rem; margin-bottom: 0.5rem; }
.client-error p { color: var(--text-secondary); margin-bottom: 0.5rem; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}

/* =====================================================
   LEAD DETAIL TABS
   ===================================================== */

#lead-header-area { margin-bottom: 1.25rem; }

.tab-bar {
  display: flex;
  gap: 0.25rem;
  padding: 0.375rem;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 1.25rem;
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.tab-bar::-webkit-scrollbar { height: 6px; }
.tab-bar::-webkit-scrollbar-track { background: transparent; }
.tab-bar::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 3px;
}

.tab-btn {
  flex-shrink: 0;
  padding: 0.5rem 0.9rem;
  background: transparent;
  border: none;
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  transition: background .15s ease, color .15s ease;
}
.tab-btn:hover {
  color: var(--text-primary);
  background: var(--bg-2);
}
.tab-btn.is-active {
  color: var(--text-primary);
  background: var(--bg-3);
  box-shadow: var(--shadow-sm);
}
.tab-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.tab-panel {
  /* Hidden panels still occupy no space since we use the [hidden] attr. */
}

/* =====================================================
   ATTACHMENTS (lead detail)
   ===================================================== */

.att-dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.375rem;
  margin: 0.75rem 1.25rem 1rem;
  padding: 1.25rem 1rem;
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-2);
  color: var(--text-secondary);
  text-align: center;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease, color .12s ease;
}
.att-dropzone:hover:not(.is-busy),
.att-dropzone:focus-visible {
  border-color: var(--accent);
  background: var(--bg-3);
  color: var(--text-primary);
  outline: none;
}
.att-dropzone.is-dragover {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--text-primary);
}
.att-dropzone.is-busy {
  cursor: wait;
  color: var(--text-primary);
}
.att-dropzone u { text-decoration: underline; color: var(--text-primary); }
.att-dropzone-icon {
  font-size: 1.375rem;
  color: var(--accent);
  line-height: 1;
  margin-bottom: 0.125rem;
}
.att-error { margin: 0 1.25rem 1rem; }

.att-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.75rem;
  padding: 0.75rem 1.25rem 1.25rem;
}
.att-card {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  transition: border-color .12s ease, transform .05s ease;
}
.att-card:hover { border-color: var(--border-strong); }
.att-card:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.att-card:active { transform: translateY(1px); }

.att-thumb {
  width: 100%;
  height: 150px;
  background: var(--bg-3);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  color: var(--text-muted);
}
.att-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.att-thumb-pdf {
  background: linear-gradient(135deg, #1a1d26 0%, #232736 100%);
  color: var(--accent);
}
.att-thumb-loading { font-size: 1.125rem; opacity: 0.6; }

.att-card-body {
  padding: 0.625rem 0.75rem 0.75rem;
  display: flex; flex-direction: column; gap: 0.25rem;
  flex: 1;
}
.att-card-filename {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.att-card-meta {
  display: flex; align-items: center; gap: 0.5rem;
  flex-wrap: wrap;
}
.att-card-caption {
  margin-top: 0.125rem;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-clamp: 2;
}
.att-card-footer { margin-top: auto; padding-top: 0.25rem; }

/* ── Kebab menu ──────────────────────────────────────────────── */
.att-kebab-wrap {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 10;
}

.att-kebab {
  opacity: 0;
  font-size: 1.1rem;
  line-height: 1;
  padding: 2px 7px;
  background: var(--bg-3) !important;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  transition: opacity .12s ease;
}

.att-card:hover .att-kebab,
.att-card:focus-within .att-kebab { opacity: 1; }

.att-dropdown {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  background: var(--bg-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  min-width: 150px;
  z-index: 50;
  padding: 0.25rem 0;
}

.att-dropdown-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.5rem 0.875rem;
  font-size: 0.875rem;
  background: none;
  border: none;
  color: var(--text-primary);
  cursor: pointer;
  transition: background .1s;
}
.att-dropdown-item:hover { background: var(--bg-2); }
.att-dropdown-danger     { color: var(--danger); }

/* =====================================================
   ADD LEAD: SCREENSHOT UPLOAD
   ===================================================== */

.new-lead-tabs {
  padding: 0.875rem 1.25rem;
  border-bottom: 1px solid var(--border);
}

.screenshot-panel {
  padding: 1.25rem;
}

.screenshot-dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.5rem;
  min-height: 220px;
  padding: 2rem 1.5rem;
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--bg-2);
  color: var(--text-secondary);
  text-align: center;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, color .15s ease;
}
.screenshot-dropzone:hover,
.screenshot-dropzone:focus-visible {
  border-color: var(--accent);
  background: var(--bg-3);
  color: var(--text-primary);
  outline: none;
}
.screenshot-dropzone.is-dragover {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--text-primary);
}
.screenshot-icon {
  color: var(--accent);
  margin-bottom: 0.25rem;
}
.screenshot-dropzone-title {
  font-size: 1rem;
  font-weight: 500;
  color: var(--text-primary);
}

.screenshot-processing {
  display: flex; align-items: center; gap: 0.875rem;
  min-height: 220px;
  padding: 2rem 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-2);
  justify-content: center;
}
.screenshot-processing .client-spinner {
  width: 24px; height: 24px;
  border-width: 3px;
  flex-shrink: 0;
}
.screenshot-processing-title {
  font-size: 1rem;
  font-weight: 500;
  color: var(--text-primary);
}

.prefill-banner {
  display: flex; align-items: flex-start; gap: 0.75rem;
  margin: 1rem 1.25rem 0;
  padding: 0.75rem 1rem;
  background: var(--accent-soft);
  border: 1px solid var(--accent-strong);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: 0.875rem;
  line-height: 1.5;
}
.prefill-banner-icon {
  font-size: 1.125rem;
  line-height: 1;
  flex-shrink: 0;
}
.prefill-banner-body strong { color: var(--text-primary); font-weight: 500; }

/* =====================================================
   K.K.D — Related leads card on lead detail
   ===================================================== */

.relationships-list {
  display: flex; flex-direction: column; gap: 0.5rem;
}
.relationship-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem;
  padding: 0.625rem 0.75rem;
  background: var(--rel-row-bg);
  border: 1px solid var(--rel-row-bd);
  border-radius: var(--radius-sm);
}
.relationship-row-main {
  display: flex; flex-direction: column; gap: 0.125rem;
  flex: 1 1 auto; min-width: 0;
}
.relationship-row-head {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
}
.relationship-row-head strong { color: var(--text-primary); font-weight: 500; }
.relationship-row-actions {
  display: flex; gap: 0.25rem; flex-shrink: 0;
}
.linkage-badge {
  display: inline-flex; align-items: center; gap: 0.2rem;
  font-size: 0.75rem;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  background: var(--linkage-bg);
  color: var(--linkage-fg);
  border: 1px solid var(--linkage-bd);
  margin-left: 0.35rem;
}
.add-rel-modal { display: flex; flex-direction: column; gap: 0.75rem; }
.add-rel-results {
  display: flex; flex-direction: column; gap: 0.35rem;
  max-height: 240px; overflow-y: auto;
}
.add-rel-result {
  display: flex; flex-direction: column; gap: 0.1rem;
  text-align: left;
  padding: 0.5rem 0.7rem;
  background: var(--rel-result-bg);
  border: 1px solid var(--rel-result-bd);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text-primary);
}
.add-rel-result:hover { background: var(--rel-result-hover-bg); }
.add-rel-picked {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 0.7rem;
  background: var(--rel-picked-bg);
  border: 1px solid rgba(99, 102, 241, 0.32);
  border-radius: var(--radius-sm);
}
.add-rel-picked-row {
  display: flex; flex-direction: column; gap: 0.1rem;
}

/* =====================================================
   K.K.C — collision warning panel on lead creation form
   ===================================================== */

.collision-panel {
  margin: 1rem 0 0;
  padding: 0.875rem 1rem;
  border-radius: var(--radius-md);
  border: 1px solid;
  font-size: 0.875rem;
  line-height: 1.45;
  display: flex; flex-direction: column; gap: 0.625rem;
}
.collision-panel-loading {
  background: rgba(107, 114, 128, 0.08);
  border-color: rgba(107, 114, 128, 0.28);
  color: var(--text-muted);
  font-style: italic;
}
.collision-panel-exact {
  background: var(--coll-warn-bg);
  border-color: var(--coll-warn-bd);
  color: var(--text-primary);
}
.collision-panel-fuzzy {
  background: var(--coll-warn-bg);
  border-color: var(--coll-warn-bd);
  color: var(--text-primary);
}
/* K.K.2.C — intra-agent hard-block. Saturated red (stronger than the
   K.K.1 amber used for cross-agent) so the visual weight matches the
   actual "you cannot create this" semantic. */
.collision-panel-intra {
  background: var(--coll-intra-bg);
  border-color: var(--coll-intra-bd);
  color: var(--text-primary);
}
.collision-panel-header {
  font-weight: 600;
  color: var(--text-primary);
  display: flex; align-items: center; gap: 0.5rem;
}
.collision-panel-header-fuzzy { color: var(--text-primary); }
.collision-panel-header-intra {
  color: var(--coll-intra-fg);
}
.collision-panel-icon { font-size: 1rem; line-height: 1; }

.collision-rows {
  display: flex; flex-direction: column; gap: 0.625rem;
}
.collision-row {
  padding-top: 0.5rem;
  border-top: 1px solid var(--coll-row-line);
  display: flex; flex-direction: column; gap: 0.5rem;
}
.collision-row:first-of-type {
  border-top: 0;
  padding-top: 0;
}
.collision-row-head {
  display: flex; flex-direction: column; gap: 0.2rem;
}
.collision-row-name strong { color: var(--text-primary); font-weight: 600; }
.collision-row-name .muted { font-weight: 400; }
.collision-row-meta { line-height: 1.4; }

.collision-actions {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  margin-top: 0.25rem;
}
.collision-actions .ghost.small,
.collision-picker-actions .ghost.small,
.collision-picker-actions .primary.small {
  font-size: 0.8125rem;
  padding: 0.35rem 0.7rem;
}

.collision-picker {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 0.6rem 0.75rem;
  background: var(--coll-picker-bg);
  border-radius: var(--radius-sm);
}
.collision-picker select {
  width: 100%;
  padding: 0.4rem 0.6rem;
  background: var(--overlay-select-bg);
  color: var(--text-primary);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.12));
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
}
.collision-picker-actions {
  display: flex; gap: 0.5rem; justify-content: flex-end;
}

/* =====================================================
   UTILITIES
   ===================================================== */

.muted { color: var(--text-muted); }
.small { font-size: 0.8125rem; }

/* =====================================================
   RESPONSIVE
   ===================================================== */

@media (max-width: 820px) {
  .nav-inner { gap: 0.75rem; padding: 0.75rem 1rem; }
  .nav-brand-text { display: none; }
  .nav-link { padding: 0.4rem 0.625rem; font-size: 0.8125rem; }
  .app-main { padding: 1.25rem 1rem 2.5rem; }
  .page-header { align-items: flex-start; }
  .filter-bar { flex-direction: column; align-items: stretch; }
  .filter-bar .field.inline,
  .filter-bar .filter-search { flex: 1 1 auto; min-width: 0; }
  .kv-grid { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .lead-header-actions { min-width: 0; width: 100%; }
  .property-card-head { flex-direction: column; align-items: stretch; gap: 0.5rem; }
  .property-card-actions { align-self: flex-end; }
}

@media (max-width: 560px) {
  .grid-3 { grid-template-columns: 1fr; }
  .cal-day { min-height: 62px; padding: 0.3rem; }
  .cal-date { font-size: 0.75rem; }
  .status-dot { width: 7px; height: 7px; }
  .cal-dots { gap: 3px; padding-top: 0.3rem; }
  .cal-appts { gap: 2px; padding-top: 2px; }
  /* K.N A visual-pass catch — scope to the header (as the 820px rule
     already is): unscoped, this also hid the DRAWER's Sign out button
     at ≤560px, leaving the drawer footer with no visible Sign out. */
  .nav .nav-signout { display: none; }
  .page-title { font-size: 1.25rem; }
  .appt { gap: 0.75rem; }
  .appt-date { width: 44px; }
}

@media (max-width: 480px) {
  .login-card { padding: 2rem 1.5rem; }
  .brand h1 { font-size: 1.25rem; }
}

/* =====================================================
   MODAL — WIDE VARIANT (Send Intro Text, future larger modals)
   ===================================================== */

.modal.modal-wide { max-width: 640px; }

/* =====================================================
   RAP-104 — "Remind me" custom lead time (hours + minutes)
   ===================================================== */

.reminder-custom {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 6px;
}

.reminder-custom[hidden] { display: none; }

.reminder-custom-inputs {
  display: flex;
  gap: 8px;
}

/* Each unit is [number input][unit label] side by side, so "2 hours
   15 minutes" reads left-to-right the way it's spoken. flex:1 keeps the two
   boxes equal at any width instead of letting the hours box dominate. */
.reminder-custom-unit {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 0;
  min-width: 0;
}

.reminder-custom-unit input {
  /* min-width:0 so flex can actually shrink these on a 375px screen —
     number inputs carry an intrinsic width that otherwise overflows the row. */
  min-width: 0;
  width: 100%;
  /* 16px prevents iOS Safari from zooming the page on focus, which on a phone
     reads as the layout jumping when you tap the field. */
  font-size: 16px;
}

.reminder-custom-unit span { white-space: nowrap; }

.reminder-custom .help.is-error { color: var(--danger, #f87171); }

/* =====================================================
   TOAST (inline helper; shared .toast base lives in call-recorder.css)
   ===================================================== */

.toast.toast-success {
  border-color: rgba(16, 185, 129, 0.4);
  color: var(--toast-success-fg);
}
/* RAP-104 — "your early reminder was skipped" is not a success and not an
   error: the save worked, one of the two reminders just couldn't be armed.
   Amber so it reads as something to notice rather than something that broke. */
.toast.toast-warning {
  border-color: rgba(245, 158, 11, 0.45);
  color: var(--toast-warning-fg);
}

/* =====================================================
   SETTINGS — MISSION CONTROL CARD (SPEC §6.1)
   ===================================================== */

.mc-banner {
  background: var(--accent-soft);
  border-color: var(--accent-strong);
  color: var(--text-primary);
  margin-bottom: 1rem;
  font-size: 0.9rem;
}
.mc-banner strong { font-weight: 600; }

/* Phase E — duplicate-phone banner reuses .mc-banner colors but adds
   a row layout so the Dismiss button sits to the right of the text. */
.duplicate-banner {
  display: flex;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
}
.duplicate-banner-text { flex: 1 1 320px; }

/* K.H — Intake-overwrite banner (expandable details) */
.intake-overwrite-banner .intake-overwrite-text {
  flex: 1 1 100%;
  min-width: 0;
}
.intake-overwrite-header {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  flex-wrap: wrap;
}
.intake-overwrite-header > span { flex: 1 1 280px; }
.intake-overwrite-details {
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}
.intake-overwrite-details[hidden] { display: none; }
.intake-overwrite-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 0.5rem;
}
.intake-overwrite-table th,
.intake-overwrite-table td {
  padding: 0.375rem 0.5rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  font-size: 0.875rem;
  vertical-align: top;
}
.intake-overwrite-table th {
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--overlay-tablehead-bg);
}
.intake-overwrite-table tr.phone-ignored-row td {
  font-style: italic;
}
.intake-overwrite-footer { margin: 0.5rem 0 0; }

.mc-secret-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}
.mc-secret-input { flex: 1 1 220px; min-width: 0; }

.mc-template-hint code {
  background: var(--bg-3);
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 0.8em;
}

.mc-actions { justify-content: flex-end; gap: 0.625rem; }

.mc-test-line { margin-top: 0.25rem; }
.mc-test-ok { color: var(--mc-ok-fg); }
.mc-test-failed { color: var(--mc-fail-fg); }

/* =====================================================
   SEND INTRO TEXT MODAL (SPEC §6.3)
   ===================================================== */

.sit-modal .modal-body { color: var(--text-primary); }

.sit-header {
  display: flex; justify-content: space-between; align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}
.sit-header .modal-title { margin-bottom: 0; }

.sit-close {
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 1.5rem;
  line-height: 1;
  width: 2rem; height: 2rem;
  padding: 0;
  cursor: pointer;
  border-radius: var(--radius-sm, 6px);
}
.sit-close:hover { color: var(--text-primary); background: var(--bg-3); }

.sit-loading {
  display: flex; flex-direction: column; align-items: center;
  gap: 0.75rem;
  padding: 2rem 1rem;
}

.sit-to {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.75rem;
  background: var(--bg-2);
  border-radius: var(--radius-md);
  margin-bottom: 0.875rem;
  font-size: 0.9rem;
}
.sit-to strong { font-weight: 600; }

.sit-field { margin-bottom: 0.875rem; display: block; }
.sit-label {
  display: block;
  font-size: 0.75rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 0.375rem;
}

.sit-textarea {
  width: 100%;
  min-height: 120px;
  padding: 0.6rem 0.75rem;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font: inherit;
  line-height: 1.5;
  resize: vertical;
}
.sit-textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-3);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.sit-attachment {
  display: flex; flex-direction: column; gap: 0.5rem;
}
.sit-thumb {
  width: 180px;
  max-width: 100%;
  min-height: 120px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.sit-thumb img {
  display: block;
  max-width: 100%;
  max-height: 240px;
  object-fit: contain;
}

.sit-filename {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all;
}

.sit-attachment-meta {
  display: flex; flex-direction: column; gap: 0.25rem;
}

.sit-download-link {
  color: var(--accent);
  text-decoration: none;
}
.sit-download-link:hover { text-decoration: underline; }

.sit-copy-hint {
  margin-top: 0.25rem;
  font-style: italic;
}

.mc-openclaw-hint {
  margin-top: -0.25rem;
  font-style: italic;
}

.sit-error { margin-bottom: 0.75rem; }

/* =====================================================
   MESSAGES — two-pane shell (Phase J.2.1, refined J.2.2.1)
   Layout strategy (J.2.2.1): when the messages view is on
   screen we promote body + #app to flex columns via the
   explicit .messages-view route class,
   and the shell takes flex:1 of the remaining space. This
   replaces the old `height: calc(100vh - 9rem)` calc that
   was off by ~50px and pushed the composer past the
   viewport on shorter desktop windows. Side-effect-free for
   other views — app.js adds the class only for /messages and
   /messages/:id, then removes it on the next route.
   ===================================================== */

body.messages-view {
  /* Lock scrolling to inside the panes. Without this, page can
     scroll vertically AND each pane can scroll, fighting each other. */
  overflow: hidden;
}

body.messages-view,
body.messages-view #app {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

body.messages-view #app {
  flex: 1 1 auto;
  min-height: 0;
}

/* J.2.2.3 — when the messages view is mounted, drop the nav's
   1280px max-width so the avatar + signout button sit at the same
   right edge as the messages shell. Without this, the nav-inner
   stays centered at 1280px on wide screens while the shell extends
   to viewport edge — visually the avatar floats in a column the
   panes can't enter.

   J.2.2.4 — scoped to viewports >820px (matches the existing
   desktop/mobile breakpoint elsewhere in this file). On mobile,
   the nav uses its standard layout — without the cap, narrow
   screens didn't have enough horizontal room for all tabs +
   avatar without the avatar bleeding past the right edge or the
   leftmost tab clipping past the left edge. */
@media (min-width: 821px) {
  body.messages-view .nav-inner {
    max-width: none;
  }
}

.app-main.messages-app-main {
  /* Inbox uses the full viewport width — overrides the default
     1280px max-width + 0-auto margins on .app-main. The margin
     reset is critical: max-width:none removes the cap but
     margin: 0 auto can STILL center in a flex container's
     remaining space, which is what was visually narrowing the
     shell on wide desktops. Belt + suspenders width:100% guards
     the case where some browser quirk de-prioritises stretch. */
  max-width: none;
  margin: 0;
  width: 100%;
  padding: 1rem 1.25rem 1rem;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  align-self: stretch;
}

.messages-shell {
  display: grid;
  /* CSS variable is set inline by messagesInbox.js, restored from
     localStorage on each mount. The 4px middle column is the resize
     handle. */
  grid-template-columns: var(--inbox-width, 320px) 4px 1fr;
  gap: 0;
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  width: 100%;
}

.messages-pane {
  display: flex;
  flex-direction: column;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  min-height: 0;
  min-width: 0;
}

.messages-pane-list {
  /* No left-rounded corner needed when the resize handle butts up
     against the right edge — keeps both panes visually anchored. */
  border-radius: 12px 0 0 12px;
}

.messages-pane-detail {
  border-radius: 0 12px 12px 0;
  border-left: 0;
}

.messages-pane-header {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 48px;
}

.messages-list-header {
  justify-content: space-between;
}

.messages-pane-header .page-title {
  margin: 0;
  font-size: 1.15rem;
}

.messages-pane-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1rem 1.25rem;
  min-height: 0;
}

.messages-pane-footer {
  border-top: 1px solid var(--border);
  padding: 0.75rem 1rem;
  background: var(--bg-2);
}

.messages-pane-footer:empty {
  display: none;
}

.messages-placeholder,
.messages-empty-state {
  color: var(--text-muted, #9aa0aa);
}

.messages-empty-state {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  text-align: center;
  padding: 2rem;
  font-size: 0.95rem;
}

/* ── Phase J.2.2.1 — resize handle, collapse, back arrow ───────── */

.messages-resize-handle {
  position: relative;
  cursor: col-resize;
  background: transparent;
  user-select: none;
  touch-action: none;
}

.messages-resize-handle::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 1px; right: 1px;
  background: var(--border);
  transition: background 0.15s;
}

.messages-resize-handle:hover::before,
.messages-resize-handle.is-dragging::before {
  background: var(--accent);
}

.messages-collapse-btn,
.messages-back-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--text-muted, #9aa0aa);
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}

.messages-collapse-btn:hover,
.messages-back-arrow:hover {
  background: var(--bg-hover);
  color: inherit;
}

.messages-back-arrow {
  /* Hidden on desktop; CSS-only show via media query when on mobile
     AND a conversation is selected. */
  display: none;
}

.messages-detail-header-slot {
  flex: 1 1 auto;
  min-width: 0;
}

/* Detail-pane header on desktop: until J.2.3 fills the slot, the
   back arrow is hidden and the slot is empty. Collapse the header
   to zero height so the right pane's empty state isn't sitting
   below an empty 48px strip. On mobile, the back arrow shows
   inside this header (via .has-active rule below) and collapses
   that selector by re-asserting min-height.
   J.2.3 — when thread.js fills the slot with
   .messages-thread-header-content, restore the normal header
   styling so the lead/agent name + subtitle have room. The :has()
   selector wins on specificity AND only matches when content
   exists, so the empty-state right pane stays uncluttered. */
@media (min-width: 821px) {
  .messages-pane-detail > .messages-detail-header {
    min-height: 0;
    padding: 0;
    border-bottom: 0;
  }
  .messages-pane-detail > .messages-detail-header:has(.messages-thread-header-content) {
    min-height: 48px;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--border);
  }
}

/* Collapsed left pane: shrink to a sliver, hide all body content,
   only the toggle button remains visible (now serving as expand). */
.messages-shell.is-collapsed-left {
  grid-template-columns: 56px 4px 1fr;
}

.messages-shell.is-collapsed-left .messages-pane-list .messages-pane-body,
.messages-shell.is-collapsed-left .messages-list-header .page-title {
  display: none;
}

.messages-shell.is-collapsed-left .messages-list-header {
  justify-content: center;
}

.messages-shell.is-collapsed-left .messages-resize-handle {
  pointer-events: none;
}

/* ── Mobile (≤820px) — single pane stack, back arrow on detail ── */

@media (max-width: 820px) {
  body.messages-view {
    /* Keep page lockscroll behaviour on mobile too. */
    overflow: hidden;
  }
  .app-main.messages-app-main {
    padding: 0.5rem 0.5rem 0.5rem;
  }
  .messages-shell {
    /* Single column; one pane visible at a time. */
    grid-template-columns: 1fr;
  }
  .messages-resize-handle,
  .messages-collapse-btn {
    display: none;
  }
  .messages-shell.has-active .messages-pane-list { display: none; }
  .messages-shell.no-active  .messages-pane-detail { display: none; }
  .messages-shell.has-active .messages-back-arrow { display: inline-flex; }
  .messages-pane-list,
  .messages-pane-detail {
    border-radius: 12px;
  }
}

/* =====================================================
   MESSAGES — inbox conversation list (Phase J.2.2)
   ===================================================== */

.messages-inbox {
  display: flex;
  flex-direction: column;
  height: 100%;
  margin: -1rem -1.25rem; /* break out of .messages-pane-body padding */
}

.messages-inbox-toolbar {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
  background: var(--bg-1);
  position: sticky;
  top: 0;
  z-index: 1;
}

.messages-search-wrap {
  position: relative;
  margin-bottom: 0.5rem;
}

.messages-search-input {
  width: 100%;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem 2rem 0.5rem 0.75rem; /* right padding for clear button */
  font: inherit;
  color: inherit;
}

.messages-search-input:focus {
  outline: none;
  border-color: var(--accent);
}

/* Hide the native browser X on type=search so our custom one is the
   only clear control. Webkit + Edge. */
.messages-search-input::-webkit-search-cancel-button,
.messages-search-input::-webkit-search-decoration { -webkit-appearance: none; }

.messages-search-clear {
  position: absolute;
  top: 50%;
  right: 0.4rem;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--text-muted, #9aa0aa);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.messages-search-clear:hover {
  background: var(--bg-hover);
  color: inherit;
}

.messages-search-clear[hidden] {
  display: none;
}

.messages-result-count {
  margin-top: 0.5rem;
  font-size: 0.75rem;
}

.messages-result-count:empty {
  display: none;
}

.messages-filter-chips {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.filter-chip {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted, #9aa0aa);
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8rem;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.filter-chip:hover {
  background: var(--bg-hover);
  color: inherit;
}

.filter-chip.is-active {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}

/* RAP-134 — the count that rides on each chip. Dimmer than the label so
   the chip still reads as its name first and its workload second. */
.chip-count {
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}

/* RAP-134 — the State group is a second, independent axis. The divider
   makes the two groups legible as two questions rather than one long
   undifferentiated row of chips. */
.messages-state-chips {
  padding-left: 0.5rem;
  border-left: 1px solid var(--border);
}

@media (max-width: 820px) {
  /* At phone width the toolbar already wraps; a left divider on a chip
     group that has wrapped onto its own line reads as a stray mark. */
  .messages-state-chips {
    padding-left: 0;
    border-left: 0;
  }
  /* Both chip rows stay one-tap targets rather than shrinking. */
  .filter-chip {
    padding: 0.35rem 0.75rem;
  }
}

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

.messages-conv-rows {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* RAP-100 — paging footer under the conversation rows: infinite-scroll
   sentinel, load-more button, in-flight text and retry, all in one slot.
   The generous vertical padding is deliberate on touch: it puts the
   button in a comfortable thumb target and gives the scroll handler room
   to fire before the user hits a hard stop. */
.messages-conv-more {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 6px;
  padding: 14px 12px 20px;
  text-align: center;
}

.messages-conv-more p {
  margin: 0;
}

.messages-conv-more button {
  width: 100%;
  max-width: 260px;
  min-height: 40px;
}

/* J.2.2.4 — iMessage-style three-column row:
       [● dot] [name         ] [time]
               [preview......]
   Dot is a fixed-width left-side indicator (visible only when
   unread > 0); main is name+preview stacked; time is the only
   meta. Eliminates the J.2.2.3 height mismatch where the meta
   column (time + counter badge) had a different stacked height
   than the main column (name + preview), which broke center
   alignment with mixed font-sizes. Aggregate unread count now
   lives on the nav tab. */
.messages-conv-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.1s;
}

.messages-conv-row:hover {
  background: var(--bg-hover);
}

/* J.2.3.1 — active row uses a subtle background highlight (Apple
   Messages convention) rather than the previous accent left-bar +
   accent-soft tint. The unread dot indicator stays accent-colored —
   it signals unread, not selection. */
.messages-conv-row.is-active {
  background: var(--bg-3);
}

.conv-row-unread-dot {
  flex: 0 0 8px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  /* Hidden when row not unread — keeps the indent consistent so
     unread rows don't visually shift left/right when count drops
     to 0. Hidden via visibility instead of display so the layout
     reserves the slot. */
  visibility: hidden;
}

.messages-conv-row.has-unread .conv-row-unread-dot {
  visibility: visible;
}

.conv-row-main {
  flex: 1 1 auto;
  min-width: 0; /* allows children to shrink below intrinsic width → ellipsis */
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

/* RAP-150 — the name line is a flex row so the temperature icon can sit
   beside the name (to its LEFT since Mauricio's 2026-08-01 review) and
   survive the name truncating. The ellipsis moved from this element onto
   .conv-row-name-text: with the text and the icon as siblings, only the text
   may shrink, so at 375px a long name clips and the flame stays put instead
   of being eaten by the overflow. Order is markup order — no `order`
   property, so the DOM reads the way it looks, which is also the order a
   screen reader announces it in. */
.conv-row-name {
  font-weight: 500;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

.conv-row-name-text {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Never a contributor to horizontal scroll on a phone: fixed, unshrinkable,
   and only ever present on a warm/hot lead row (cold and listing-agent rows
   render no element at all — see conversationTemperatureIcon). */
.conv-row-temp {
  flex: 0 0 auto;
  width: auto;
  font-size: 0.95rem;
  line-height: 1;
}

.messages-conv-row.has-unread .conv-row-name {
  font-weight: 700;
}

.conv-row-time {
  flex: 0 0 auto;
  font-size: 0.78rem;
  white-space: nowrap;
  align-self: center;
}

.conv-row-preview {
  font-size: 0.85rem;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-muted, #9aa0aa);
}

.messages-conv-row.has-unread .conv-row-preview {
  color: inherit;
}

/* =====================================================
   MESSAGES — thread (Phase J.2.3 right-pane)
   Bubble layout, day dividers, attribution, reactions.
   ===================================================== */

.messages-thread {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow-y: auto;
  padding: 1rem 1.25rem;
  /* J.2.5 — dropped the negative-margin "break out of pane padding"
     trick. It worked in the /messages right pane but bled past the
     host edges when the same module was embedded in the per-lead
     Messages tab (where the host has no surrounding padding). The
     small extra inset in the inbox is the cosmetic trade-off. */
}

.messages-thread-content {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  /* Push content to the bottom of the scroller when the thread
     doesn't fill the viewport — feels natural for short threads. */
  margin-top: auto;
}

.messages-day-group {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.messages-day-divider {
  position: relative;
  text-align: center;
  margin: 0.75rem 0 0.25rem;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-muted, #9aa0aa);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.messages-day-divider::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0; right: 0;
  height: 1px;
  background: var(--border);
}
.messages-day-divider span {
  position: relative;
  background: var(--bg-1);
  padding: 0 0.6rem;
}

/* Bubble wrapper: caps width and side-aligns. */
.message-bubble-wrap {
  display: flex;
  flex-direction: column;
  max-width: 70%;
  margin-bottom: 0.1rem;
  position: relative;
}

/* J.2.7.5 Bug J — owner-only delete affordance. Hidden by default,
 * fades in on hover. Positioned outside the bubble so it doesn't
 * eclipse content. From-me bubbles show it on the LEFT side of the
 * bubble; from-them bubbles show it on the RIGHT. */
.message-delete-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: var(--bg-3);
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease, background 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  z-index: 1;
}
.message-bubble-wrap:hover .message-delete-btn { opacity: 0.85; }
.message-delete-btn:hover { background: #c0392b; color: #fff; opacity: 1; }
.message-delete-btn:disabled { opacity: 0.4; cursor: wait; }
.message-bubble-wrap.is-from-me .message-delete-btn  { left:  -28px; }
.message-bubble-wrap.is-from-them .message-delete-btn { right: -28px; }
.message-bubble-wrap.is-from-me {
  align-self: flex-end;
  align-items: flex-end;
}
.message-bubble-wrap.is-from-them {
  align-self: flex-start;
  align-items: flex-start;
}

/* Stacked-bubble: same-author runs hide their own attribution + tighten. */
.message-bubble-wrap + .message-bubble-wrap {
  margin-top: -0.05rem;
}

.message-bubble {
  padding: 0.5rem 0.8rem;
  border-radius: 16px;
  word-wrap: break-word;
  overflow-wrap: anywhere;
  line-height: 1.35;
  font-size: 0.95rem;
}

/* J.2.3.1 — bubble colors per Apple Messages convention.
   Outbound iMessage: blue.   Outbound SMS: green.   Inbound (any): gray.
   The .is-from-them rule comes after, so inbound styling wins
   regardless of the service class on the wrapper.
   J.2.7.1 — .is-image-only wrappers don't get a bubble (Apple
   Messages convention: standalone photos float without a colored
   pill). When body OR non-image attachments exist alongside images,
   the bubble still renders for the text/non-image part. */
.message-bubble-wrap.is-from-me.is-imessage .message-bubble {
  background: #0b93f6;
  color: #fff;
  border-bottom-right-radius: 5px;
}
.message-bubble-wrap.is-from-me.is-sms .message-bubble {
  background: #34c759;
  color: #fff;
  border-bottom-right-radius: 5px;
}
.message-bubble-wrap.is-from-them .message-bubble {
  background: var(--bg-2);
  color: var(--text-primary);
  border-bottom-left-radius: 5px;
}

/* J.2.7.1 — multi-image stack (vertical, tight gap), and the
   image-only modifier that suppresses the bubble entirely. */
.message-images-stack {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.message-bubble-wrap.is-image-only .message-bubble { display: none; }
.message-bubble a {
  color: inherit;
  text-decoration: underline;
}

.message-attribution {
  font-size: 0.7rem;
  color: var(--text-muted, #9aa0aa);
  margin-top: 0.18rem;
  padding: 0 0.4rem;
}

/* Attachments — J.2.3 pill UI for non-images / images without
   thumbnails. J.2.7 inline <img> for images that have one.
   J.2.7.1 — pills are now <a> elements linking to /full; remove
   the default <a> underline + force inherit color so they keep
   reading as pills. */
.message-attachment-pill {
  margin-top: 0.3rem;
  padding: 0.35rem 0.6rem;
  background: var(--msg-pill-bg);
  border-radius: 8px;
  font-size: 0.85rem;
  color: inherit;
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
}
.message-attachment-pill:hover {
  background: var(--msg-pill-hover-bg);
}
.message-bubble-wrap.is-from-me .message-attachment-pill {
  background: rgba(255,255,255,0.18);
  color: #fff;
}
.message-bubble-wrap.is-from-me .message-attachment-pill:hover {
  background: rgba(255, 255, 255, 0.28);
}
/* J.2.7 — inline image attachment + onerror→pill fallback container.
   The pill is in the DOM at all times but hidden by default;
   .img-failed (set by the <img onerror>) hides the link and reveals
   the pill. */
.message-attachment-image-container {
  margin-top: 0.3rem;
}
.message-attachment-image-container .message-attachment-pill-fallback {
  display: none;
}
.message-attachment-image-container.img-failed > .message-attachment-image-link {
  display: none;
}
.message-attachment-image-container.img-failed .message-attachment-pill-fallback {
  display: inline-block;
}
.message-attachment-image-link {
  display: inline-block;
  line-height: 0; /* kill the inline-img descender gap */
}
.message-attachment-image {
  display: block;
  max-width: 280px;
  max-height: 360px;
  width: auto;
  height: auto;
  object-fit: cover;
  border-radius: 12px;
  cursor: pointer;
  background: rgba(0, 0, 0, 0.04); /* soft placeholder while loading */
}
@media (max-width: 820px) {
  .message-attachment-image {
    max-width: 220px;
    max-height: 280px;
  }
}

/* Reactions row sits just below the bubble. Slight overlap is the
   iMessage convention but we keep it separate for simplicity. */
.message-reactions {
  display: flex;
  gap: 0.2rem;
  margin-top: 0.2rem;
  padding: 0 0.4rem;
}
.message-reaction {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.1rem 0.4rem;
  font-size: 0.78rem;
  line-height: 1.2;
}

/* RAP-41 — tapback affordance. Rendered only on the newest inbound
   message (the only one Apple's mechanism can react to). The add-chip
   sits in the reactions row so it needs no hover to discover (phone
   friendly); the picker pops above it. */
/* No position:relative here — the picker anchors to the nearest
   positioned ancestor, .message-bubble-wrap, so it pops above the
   whole message instead of covering the bubble text. */
.message-tapback-area {
  display: inline-flex;
  align-items: center;
}
.message-tapback-add {
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: 999px;
  padding: 0.1rem 0.45rem;
  font-size: 0.78rem;
  line-height: 1.2;
  color: var(--text-secondary);
  cursor: pointer;
  opacity: 0.65;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}
.message-tapback-add:hover {
  opacity: 1;
  border-color: var(--accent);
}
.message-tapback-plus {
  font-size: 0.65rem;
  vertical-align: super;
}
.message-tapback-picker {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 0;
  display: flex;
  gap: 0.15rem;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.25rem 0.4rem;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  z-index: 5;
  white-space: nowrap;
}
.message-tapback-picker[hidden] { display: none; }
.message-tapback-option {
  background: transparent;
  border: none;
  border-radius: 50%;
  font-size: 1.05rem;
  line-height: 1;
  padding: 0.3rem;
  cursor: pointer;
  transition: transform 0.1s ease, background 0.15s ease;
}
.message-tapback-option:hover:not(:disabled) {
  transform: scale(1.25);
  background: var(--bg-1);
}
.message-tapback-option:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Reply quote line above its bubble. Truncated to one line. */
.message-reply-quote {
  border-left: 3px solid var(--accent);
  padding: 0.1rem 0.5rem;
  margin-bottom: 0.2rem;
  font-size: 0.78rem;
  color: var(--text-muted, #9aa0aa);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: var(--bg-1);
  border-radius: 4px;
}

.thread-load-top {
  text-align: center;
  padding: 0.5rem 0;
}

/* Thread header content (rendered into messages-detail-header-slot) */
.messages-thread-header-content {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
  /* RAP-57 — was 0.05rem, which put the phone subtitle <1px under the
     name so it visually crowded the top of its line. */
  gap: 0.25rem;
}
.messages-thread-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* RAP-214 — the linked thread title (lead threads since J.2.1, agent threads
   as of this issue).

   The hover used to swap the colour to var(--accent). Measured as TEXT on
   --bg-1 that is 2.76:1 on Dark — the default theme — 3.04:1 on Midnight and
   3.17:1 on Graphite, i.e. the hover state was harder to read than the
   resting one on three of seven schemes. It now takes the treatment the rest
   of the CRM already uses for an inline link (.onehome-lead-link, and RAP-198's
   .lead-phone-link): --text-primary, which is 13.44:1 at worst across all
   seven, with the underline carrying the affordance instead of the hue.

   :focus-visible is included so keyboard users get the same affordance —
   the element is a link with text-decoration:none, which otherwise leaves
   the focus ring as the only cue. */
a.messages-thread-title:hover,
a.messages-thread-title:focus-visible {
  color: var(--text-primary);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--text-muted);
  text-underline-offset: 0.2em;
}

/* RAP-214 — a 44px tap target on touch devices, per Apple's HIG minimum.
   Keyed on pointer:coarse (the RAP-200 rule: the trigger is the touch
   device, not a width breakpoint — an iPad at 1024px needs it and a narrow
   desktop window does not).

   Padding + a matching negative margin rather than min-height: the title
   relies on overflow:hidden + text-overflow:ellipsis to truncate at 375px,
   and switching it to a flex box to centre the text would kill the ellipsis.
   This grows the hit rectangle without moving the text or changing the
   header's layout box. */
@media (pointer: coarse) {
  a.messages-thread-title {
    padding-block: 0.6875rem;   /* 11px × 2 + ~22px line box ≈ 44px */
    margin-block: -0.6875rem;
  }
}
.messages-thread-subtitle {
  font-size: 0.72rem;
}

/* RAP-54 — owner-only thread archive controls */
.messages-thread-header-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: 1 1 auto;
  min-width: 0;
  /* RAP-57 — on narrow screens the action group (archived badge +
     Restore + Delete permanently) is wider than the space next to the
     title; without wrap it crushed the title column into mid-word
     button wraps. Let it drop to its own right-aligned line instead. */
  flex-wrap: wrap;
  row-gap: 0.35rem;
}
/* RAP-57 — when the header carries capture actions (Create lead /
   Link to lead), the right-side action group used to float mid-height,
   ~15px above the capture buttons' baseline. Bottom-align the row so
   both button groups sit on one clean baseline. Headers without
   capture actions (lead threads, archived threads) keep the centered
   layout, which is already correct for a title+subtitle stack. */
.messages-thread-header-row:has(.messages-thread-link-actions) {
  align-items: flex-end;
}
.messages-thread-header-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
  flex: 0 0 auto;
  /* RAP-57 — badge + Restore + Delete permanently can exceed a phone
     viewport; wrap right-aligned instead of clipping off-screen. */
  flex-wrap: wrap;
  justify-content: flex-end;
  row-gap: 0.35rem;
  min-width: 0;
  max-width: 100%;
}
.messages-archived-badge {
  font-size: 0.72rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: var(--danger-soft);
  color: var(--danger);
  border: 1px solid var(--danger);
  white-space: nowrap;
}
.messages-conv-row .conv-row-restore {
  flex: 0 0 auto;
  align-self: center;
}
.messages-conv-row.is-archived .conv-row-archived-meta {
  color: var(--danger);
  opacity: 0.85;
}

/* RAP-56 — delete-permanently-now controls */
.messages-conv-row .conv-row-purge {
  flex: 0 0 auto;
  align-self: center;
}
.purge-confirm-input {
  width: 100%;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.65rem 0.8125rem;
  color: var(--text-primary);
  font-size: 0.9375rem;
  font-family: inherit;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.purge-confirm-input:focus {
  outline: none;
  border-color: var(--danger);
  background: var(--bg-3);
  box-shadow: 0 0 0 3px var(--danger-soft);
}
.purge-confirm-input::placeholder { color: var(--text-muted); }

/* =====================================================
   MESSAGES — composer (Phase J.2.4)
   Right-pane footer: textarea + send button + meta line.
   ===================================================== */

.messages-composer {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.messages-composer-meta {
  font-size: 0.72rem;
  padding: 0 0.25rem;
}

.messages-composer-input-row {
  display: flex;
  gap: 0.5rem;
  align-items: flex-end;
}

.messages-composer-input {
  flex: 1 1 auto;
  min-height: 36px;
  max-height: 200px;
  padding: 0.55rem 0.85rem;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 18px;
  font: inherit;
  color: inherit;
  resize: none;
  overflow-y: auto;
  line-height: 1.35;
}

.messages-composer-input:focus {
  outline: none;
  border-color: #0b93f6;
  box-shadow: 0 0 0 2px rgba(11, 147, 246, 0.15);
}

.messages-composer-input:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Send button styled to match the iMessage-blue outbound bubble — the
   button literally produces those bubbles. Disabled state desaturates. */
.messages-composer-send {
  flex: 0 0 auto;
  background: #0b93f6;
  color: #fff;
  border: 0;
  padding: 0.55rem 1.1rem;
  border-radius: 18px;
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  transition: background 0.12s, opacity 0.12s;
}

.messages-composer-send:hover:not(:disabled) {
  background: #0a7fd4;
}

.messages-composer-send:disabled {
  background: var(--bg-3);
  color: var(--text-muted, #9aa0aa);
  cursor: not-allowed;
}

.messages-composer-error {
  margin-top: 0.25rem;
  font-size: 0.85rem;
}

/* =====================================================
   PER-LEAD MESSAGES SECTION (Phase J.2.5)
   Embeds the same thread + composer modules from the
   top-level /messages view inside a card on the lead
   detail page's Messages tab.
   ===================================================== */

.lead-messages-section .lead-messages-tabs {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  flex-wrap: wrap;
  padding: 0.5rem 0.75rem 0.6rem;
  border-bottom: 1px solid var(--border);
}

.lead-messages-tab {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted, #9aa0aa);
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8rem;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.lead-messages-tab:hover {
  background: var(--bg-hover);
  color: inherit;
}

.lead-messages-tab.is-active {
  background: var(--bg-3);
  color: var(--text-primary);
  border-color: var(--border-strong, var(--border));
}

/* K.G.C — per-tab unread badge for multi-contact threads */
.lead-messages-tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.2em;
  margin-left: 0.4em;
  padding: 0 0.35em;
  border-radius: 999px;
  background: var(--accent, #5a8fff);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 600;
  line-height: 1.4;
}

.lead-messages-hidden-note {
  margin-left: auto;
  font-size: 0.72rem;
}

/* The pane gives the embedded thread + composer a constrained
   container with explicit height — the thread.js module needs a
   sized parent for its internal flex / scroll layout to work
   (height: 100% on .messages-thread). 70vh feels right for desktop;
   capped so very tall windows don't make a single section dominate. */
.lead-messages-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 70vh;
  max-height: 720px;
  border-top: 0; /* tabs above already have border-bottom */
}

.lead-messages-thread-host {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.lead-messages-composer-host {
  flex: 0 0 auto;
  border-top: 1px solid var(--border);
  padding: 0.75rem 1rem;
  background: var(--bg-2);
}

/* Smaller mobile: shrink the pane to the available viewport so it
   doesn't push the lead-detail action footer off screen. */
@media (max-width: 820px) {
  .lead-messages-pane {
    height: 60vh;
    max-height: 560px;
  }
}

/* K.G.6.C — empty-tab state inside the thread host. Shown when the
   user selects a tab for a contact who has no conversation yet. */
.lead-messages-thread-host .lead-messages-empty-state {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.5rem;
  text-align: center;
}

/* =====================================================
   COMPOSER — attachment picker (Phase J.2.7.1)
   Paperclip button, file input, drag/drop visual state,
   row of preview thumbnails above the input.
   ===================================================== */

.messages-composer.is-drop-target {
  outline: 2px dashed #0b93f6;
  outline-offset: -4px;
  border-radius: 12px;
}

.messages-composer-attach {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--text-muted, #9aa0aa);
  cursor: pointer;
  padding: 0;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.messages-composer-attach:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.messages-composer-attach:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* RAP-8 — snippet picker + unresolved-variable warning */
.messages-composer-snippets-menu {
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-elevated, var(--bg-card, #1c1e24));
  padding: 0.35rem;
  margin: 0 0.25rem 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.messages-composer-snippet-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: 8px;
  padding: 0.45rem 0.55rem;
  cursor: pointer;
  color: var(--text-primary);
}
.messages-composer-snippet-item:hover {
  background: var(--bg-hover);
}
.messages-composer-snippet-name {
  font-weight: 600;
  font-size: 0.9rem;
}
.messages-composer-snippet-warn {
  font-size: 0.75rem;
  color: var(--warning, #e8a13c);
}
.messages-composer-snippet-preview {
  font-size: 0.78rem;
  color: var(--text-muted, #9aa0aa);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.messages-composer-unresolved {
  margin: 0 0.25rem 0.4rem;
}
.messages-composer-unresolved code {
  background: rgba(232, 76, 76, 0.15);
  border-radius: 4px;
  padding: 0 0.3em;
}

.messages-composer-attachments {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0 0.25rem;
}

.messages-composer-attachment-preview {
  position: relative;
  width: 64px;
  height: 64px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg-3);
  border: 1px solid var(--border);
}
.messages-composer-attachment-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.messages-composer-attachment-remove {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 0.85rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.messages-composer-attachment-remove:hover {
  background: rgba(0, 0, 0, 0.85);
}

/* ── J.2.7.6 Bug M — Intro Templates Settings section ─────────────── */
.intro-template-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
/* Theme fix (2026-07-04): this section referenced token names that were
   never defined (--bg-secondary/--bg-tertiary/--border-secondary), so
   the hardcoded dark fallbacks always won and light mode rendered
   dark-on-dark. Real tokens only — no literal fallbacks. */
.intro-template-row {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem 1rem;
  background: var(--bg-2);
}
.intro-template-row-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.4rem;
}
.intro-template-body {
  margin: 0.25rem 0 0.6rem;
  padding: 0.5rem 0.75rem;
  background: var(--bg-3);
  border-radius: 6px;
  font-family: inherit;
  font-size: 0.9rem;
  white-space: pre-wrap;
  word-break: break-word;
}
.intro-template-row-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.intro-template-row-actions button {
  padding: 0.35rem 0.75rem;
  font-size: 0.85rem;
}
.intro-template-editor {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem;
  background: var(--bg-2);
  border: 1px solid var(--accent);
  border-radius: 8px;
  margin-bottom: 1rem;
}
.intro-template-editor label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.85rem;
}
.intro-template-editor label.checkbox-row {
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
}
.intro-template-editor input[type="text"],
.intro-template-editor input[type="email"],
.intro-template-editor input[type="password"],
.intro-template-editor textarea {
  width: 100%;
  font-family: inherit;
  font-size: 0.95rem;
  /* Theme fix (2026-07-04): these were UA-default (white even in dark
     mode) — token them like the app's standard .field controls. */
  background: var(--bg-1);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.5rem 0.65rem;
}
/* J.2.7.7 Bug R — keep the checkbox at native size; the width:100%
 * rule above was making it stretch to fill the row, pushing it far
 * left of its label text. */
.intro-template-editor label.checkbox-row input[type="checkbox"] {
  width: auto;
  margin: 0;
  flex: 0 0 auto;
}
.intro-template-editor textarea {
  min-height: 8rem;
  resize: vertical;
}

/* ── J.2.7.6 — Send Intro Text modal: template picker dropdown ────── */
.sit-select {
  width: 100%;
  padding: 0.5rem 0.75rem;
  font-family: inherit;
  font-size: 0.95rem;
  /* Theme fix (2026-07-04): dead token names → always-dark fallbacks;
     match the app's standard form controls (.field select). */
  background: var(--bg-2);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 6px;
}

/* ── J.2.10 — Call Recorder modal + floating pill ───────────────────────── */

.cr-modal {
  max-width: 460px;
}
.cr-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}
.cr-header-btns {
  display: flex;
  gap: 0.25rem;
}
.cr-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  padding: 0.5rem 0.25rem;
  text-align: center;
}
.cr-icon {
  font-size: 2rem;
  line-height: 1;
}
.cr-icon-warn {
  filter: hue-rotate(-15deg);
}
.cr-hint {
  color: var(--text-secondary);
  font-size: 0.95rem;
  margin: 0;
}
.cr-rec-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.cr-rec-label {
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.cr-timer {
  font-variant-numeric: tabular-nums;
  font-size: 2.25rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-primary);
}
.cr-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
}
.cr-stages {
  list-style: none;
  padding: 0;
  margin: 0;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  width: 100%;
}
.cr-stage {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.95rem;
  color: var(--text-muted);
}
.cr-stage.cr-active { color: var(--text-secondary); }
.cr-stage.cr-done   { color: var(--text-primary); }
.cr-error-msg {
  color: var(--danger);
  font-weight: 500;
  margin: 0;
}

/* Recording dot — pulsing red, or static gray when paused. */
.cr-dot {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--danger);
  flex-shrink: 0;
}
.cr-dot.is-pulsing {
  animation: crPulse 1.2s ease-in-out infinite;
}
.cr-dot.is-static {
  background: var(--text-muted);
}
@keyframes crPulse {
  0%   { box-shadow: 0 0 0 0 var(--danger); opacity: 1; }
  60%  { box-shadow: 0 0 0 7px transparent; opacity: 0.55; }
  100% { box-shadow: 0 0 0 0 transparent;  opacity: 1; }
}

/* ── Floating pill (minimized state) ─────────────────────────────────────
 * The host is a pointer-events:none full-screen wrapper so the
 * underlying page receives clicks/scroll. The pill itself opts back
 * in to pointer-events. z-index 1000 sits above sticky bars and
 * dropdowns; the modal-backdrop is 100, but the two views are never
 * concurrent (minimized vs expanded). */
.cr-pill-host {
  position: fixed;
  inset: 0;
  z-index: 1000;
  pointer-events: none;
}
.cr-pill {
  position: absolute;
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 0.95rem;
  min-height: 30px;
  background: var(--bg-2);
  border: 2px solid var(--border);
  border-radius: 999px;
  box-shadow: var(--shadow-lg, 0 12px 34px rgba(0, 0, 0, 0.5));
  user-select: none;
  cursor: grab;
  font-size: 1rem;
  color: var(--text-primary);
  /* RAP-69 Phase 2: free-drag + resize use Pointer Events; disable native
     touch gestures on the pill so a drag/resize doesn't scroll the page. */
  touch-action: none;
}
/* RAP-69 Phase 2: corner grip to resize the pill (JS scales via transform,
   origin top-left, and persists the size). */
.cr-pill-resize {
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 16px;
  height: 16px;
  cursor: nwse-resize;
  opacity: 0.5;
  touch-action: none;
  background:
    linear-gradient(135deg,
      transparent 0 42%, var(--text-secondary) 42% 52%,
      transparent 52% 66%, var(--text-secondary) 66% 76%,
      transparent 76%);
}
.cr-pill-resize:hover { opacity: 0.95; }
.cr-pill.is-resizing { cursor: nwse-resize; }
/* FIX 4: bigger, always-visible status dot + label inside the pill. */
.cr-pill .cr-dot { width: 14px; height: 14px; }
.cr-pill-label {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.72rem;
  color: var(--danger);
}
/* (RAP-69 Phase 2 removed the FIX 4 corner-dock classes — the pill free-drags
   with a clamped on-screen position set inline by applyPillTransform().) */
.cr-pill.is-dragging {
  cursor: grabbing;
  opacity: 0.92;
}
/* Subtle red glow during active recording to keep the user aware. */
.cr-pill.is-recording {
  border-color: var(--danger);
  box-shadow: 0 0 0 2px var(--danger-soft, rgba(239, 68, 68, 0.18)),
              0 10px 28px rgba(0, 0, 0, 0.45);
}
.cr-pill-timer {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-size: 1.05rem;
}
.cr-pill-uploading {
  font-size: 0.85rem;
  color: var(--text-secondary);
  font-style: italic;
}
.cr-pill-btn {
  font-size: 0.8rem;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-3);
  color: var(--text-primary);
  cursor: pointer;
  white-space: nowrap;
}
.cr-pill-btn:hover { background: var(--bg-hover); }
.cr-pill-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.cr-pill-stop {
  background: var(--danger-soft, rgba(239, 68, 68, 0.12));
  border-color: var(--danger);
  color: var(--danger);
}
.cr-pill-stop:hover {
  background: var(--danger);
  color: #fff;
}
.cr-pill-expand {
  border-radius: 50%;
  width: 28px;
  height: 28px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* =====================================================
   K.E.1 — Lead detail Properties tab
   ===================================================== */

.portal-link-card {
  background: var(--bg-2);
  border: 1px solid var(--border);
}
.portal-link-card .portal-link-url {
  margin: 0.5rem 0 0.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: monospace;
  font-size: 0.875rem;
  word-break: break-all;
}
.portal-link-card .portal-link-url a { color: var(--accent); }
.portal-link-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.portal-link-status { margin: 0; }

.lead-properties-table .data-table-actions {
  text-align: right;
  white-space: nowrap;
}
.lead-properties-table .data-table-actions button + button {
  margin-left: 0.25rem;
}
.lead-properties-row-address { cursor: pointer; }
.lead-properties-row-address strong { display: block; }


/* =====================================================
   K.E.1.1 — Lead profile polish + bug-fix increment
   ===================================================== */

/* The portal URL is informational, not action — drop the accent
   (red) color and use the muted/secondary token so it reads as
   plain text. The Copy / Send buttons retain their colors. */
.portal-link-card .portal-link-url a {
  color: var(--text-secondary);
  text-decoration: none;
}
.portal-link-card .portal-link-url a:hover {
  color: var(--text-primary);
  text-decoration: underline;
}

/* Inline-sized portal action buttons. Default `button.primary` /
   `button.ghost` are width:100%; we override here so the buttons
   sit naturally next to each other inside .portal-link-actions. */
.portal-link-card .portal-link-actions button.primary,
.portal-link-card .portal-link-actions button.ghost {
  width: auto;
  padding: 0.4rem 0.85rem;
}

/* Send via iMessage button — iMessage blue, applied to portal card
   and any future send-via-iMessage CTA. */
.portal-link-card button[data-portal-send-imessage] {
  background: var(--color-imessage);
  color: #ffffff;
  border-color: var(--color-imessage);
}
.portal-link-card button[data-portal-send-imessage]:hover:not(:disabled) {
  background: var(--color-imessage-hover);
  border-color: var(--color-imessage-hover);
  color: #ffffff;
}

/* Inline-sized + Add Property by MLS# in the section header. */
.lead-properties-list .card-header button.primary[data-add-mls] {
  width: auto;
  padding: 0.4rem 0.85rem;
}

/* Wide modal variant — used by composeOutreachModal +
   propertyDetailModal to give the embedded thread / property detail
   room to breathe. Generic .modal caps at 480px; .modal-wide bumps
   to a more comfortable working width and pins the modal to a
   flexible height that still scrolls when content overflows. */
.modal-wide {
  max-width: 720px;
  width: 100%;
  max-height: calc(100vh - 3rem);
  display: flex;
  flex-direction: column;
}

.modal-wide .compose-outreach-modal-header,
.modal-wide .property-detail-modal-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.75rem;
  margin-bottom: 0.75rem;
}
.modal-wide .compose-outreach-modal-header .modal-title,
.modal-wide .property-detail-modal-header .modal-title {
  flex: 1 1 auto;
  margin-bottom: 0;
}
.modal-wide .compose-outreach-modal-close,
.modal-wide .property-detail-modal-close {
  flex-shrink: 0;
}

.compose-outreach-modal-body,
.property-detail-modal-body {
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}

/* K.E.2.1 — Picker shown when a property has 2+ interests. Sits
   above the thread/composer; lets the user pick which client to
   compose on behalf of. Matches dark-theme inputs elsewhere. */
.compose-picker {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  margin-bottom: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-2);
  font-size: 0.9rem;
}
.compose-picker label {
  color: var(--muted);
  white-space: nowrap;
}
.compose-picker select {
  padding: 0.25rem 0.5rem;
  background: var(--bg-1);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
}

/* The compose-outreach modal embeds thread.js + composer.js. Keep
   the thread bounded so the composer stays in view; matches the
   /agents/:id page rhythm but inside the modal frame. */
.compose-outreach-thread {
  min-height: 200px;
  max-height: 50vh;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.75rem;
  background: var(--bg-2);
}
.compose-outreach-composer {
  /* composer.js owns its own internal padding; just give it room. */
  min-height: 0;
}

.property-detail-modal-fullpage {
  text-decoration: none;
  white-space: nowrap;
}
.property-detail-modal-fullpage:hover {
  color: var(--text-primary);
  text-decoration: underline;
}

/* When propertyDetail.js's paint() runs inside the modal, the
   inline /properties breadcrumb at the top of its output is
   redundant (the modal title already shows the address). Hide it
   inside the modal body but keep it on the standalone page. */
.property-detail-modal-body > .breadcrumbs:not(.property-back-to-lead) {
  display: none;
}
/* The Back-to-lead breadcrumb is not relevant inside the modal —
   the user is already on the lead profile. */
.property-detail-modal-body > .property-back-to-lead {
  display: none;
}

/* Back-to-lead breadcrumb on the full /properties/:id page. */
.property-back-to-lead {
  margin-bottom: 0.5rem;
}
.property-back-to-lead a {
  color: var(--text-secondary);
  text-decoration: none;
  font-weight: 500;
}
.property-back-to-lead a:hover {
  color: var(--text-primary);
  text-decoration: underline;
}


/* =====================================================
   K.E.1.2 — Card padding fix
   The base .card has zero body padding (only .card-header
   is padded). Children directly inside the card therefore
   sit flush against the rounded corners — visible as text
   clipping on the lead Properties tab and the property
   detail modal. Add explicit horizontal+bottom padding to
   the body content of the affected cards. We avoid a global
   `.card > :not(.card-header)` rule because some cards
   (e.g. .property-interests with its data table, the
   leadProperties.js properties table) want their child
   elements to span edge-to-edge.
   ===================================================== */

/* Client portal card body content (lead Properties tab) */
.portal-link-card > :not(.card-header) {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}
.portal-link-card > :last-child {
  padding-bottom: 1rem;
}

/* Properties section empty state on the lead Properties tab.
   The data-table case keeps no padding so rows span edge-to-edge. */
.lead-properties-list .empty-state {
  padding: 1rem 1.25rem 1.25rem;
}

/* Property detail cards (full /properties/:id page AND the modal).
   Each card body needs horizontal padding so dl/dt/dd, paragraphs,
   and edit affordances clear the rounded corners. .property-interests
   is excluded — its <table> spans edge-to-edge by design. */
.property-header > :not(.card-header),
.property-listing-agent > :not(.card-header),
.property-details > :not(.card-header),
.property-notes > :not(.card-header) {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}
.property-header > :last-child,
.property-listing-agent > :last-child,
.property-details > :last-child,
.property-notes > :last-child {
  padding-bottom: 1.25rem;
}

/* The interests card's "+ Link to a client" picker and empty state
   need padding too — but the table inside data-interests-body should
   stay edge-to-edge, so target the empty-state and link-picker only. */
.property-interests .empty-state,
.property-interests .property-link-picker {
  padding: 0.5rem 1.25rem 1rem;
}


/* =====================================================
   K.E.1.3 — Agent detail card padding
   Extends the K.E.1.2 pattern to the agent detail page's
   cards. Same root cause: .card has zero body padding so
   the phone/email/license dl on .agent-header, the
   properties-listed paragraph on .agent-properties, and
   the iMessage thread embed on .agent-thread-card all sit
   flush against the rounded corners.
   ===================================================== */
.agent-header > :not(.card-header),
.agent-properties > :not(.card-header),
.agent-thread-card > :not(.card-header) {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}
.agent-header > :last-child,
.agent-properties > :last-child,
.agent-thread-card > :last-child {
  padding-bottom: 1.25rem;
}

/* =====================================================
   K.E.2 — Multi-agent list on property detail
   The Agents card may render N entries instead of one
   fixed listing-agent block. Each entry is separated by
   a hairline divider, and the per-entry name + role
   label sit on one row.
   ===================================================== */
.property-agents-list {
  display: flex;
  flex-direction: column;
}
.property-agent-entry {
  padding: 1rem 0;
  border-bottom: 1px solid var(--border);
}
.property-agent-entry:first-child {
  padding-top: 0;
}
.property-agent-entry:last-child {
  padding-bottom: 0;
  border-bottom: none;
}
.property-agent-entry-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}
.property-agent-entry-name {
  text-decoration: none;
  color: var(--text);
}
.property-agent-entry-name:hover strong {
  text-decoration: underline;
}
.property-agent-role-badge {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--overlay-pill-bg);
  color: var(--muted);
  white-space: nowrap;
}
.property-agent-role-listing_agent {
  background: rgba(59, 130, 246, 0.15);
  color: var(--blue, #60a5fa);
}
.property-agent-role-co_listing_agent {
  background: rgba(168, 162, 158, 0.18);
  color: var(--muted);
}
.property-agent-entry .property-agent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

/* Add-agent modal — narrower than the compose-outreach modal. */
.add-agent-modal {
  width: min(420px, calc(100vw - 2rem));
}
.add-agent-modal .modal-body {
  padding: 0 1.25rem 1.25rem;
}
.add-agent-modal .field {
  margin-bottom: 0.75rem;
}
.add-agent-modal .modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 1rem;
}

/* ─────────────────────────────────────────────────────────────────
   K.F.3.C.2 — Admin decision-state pill (5 states, locked colors)
   Same visual language as the property header "Available" badge:
   background at ~14% alpha, border at full alpha, text at full alpha.
   ───────────────────────────────────────────────────────────────── */

.decision-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.1875rem 0.625rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  border: 1px solid transparent;
  line-height: 1.4;
}
.decision-pill-gray {
  background: var(--decision-gray-bg);
  border-color: var(--decision-gray-bd);
  color: var(--decision-gray-fg);
}
.decision-pill-yellow {
  background: rgba(245, 158, 11, 0.14);
  border-color: rgba(245, 158, 11, 0.65);
  color: rgb(245, 158, 11);
}
.decision-pill-green {
  background: rgba(16, 185, 129, 0.14);
  border-color: rgba(16, 185, 129, 0.65);
  color: rgb(16, 185, 129);
}
.decision-pill-red {
  background: rgba(239, 68, 68, 0.14);
  border-color: rgba(239, 68, 68, 0.65);
  color: rgb(239, 68, 68);
}
.decision-pill-leased {
  background: var(--decision-leased-bg);
  border-color: var(--decision-leased-bd);
  color: var(--decision-leased-fg);
}
/* K.O B.1 — .decision-pill-off-market is GONE: the class override that
   routed Off market to a gray pill was removed from decisionStatePill.js;
   off_market + not_pursuing now inherit red from the canonical map. */

/* Property Workflow Hygiene polish C — Notes card layout. Two
   sections (Admin / Client) need explicit vertical rhythm and a
   visual divider so the user immediately sees the boundary between
   internal and portal-visible notes. */
.property-notes-grid {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.property-notes-section {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
/* Subtle horizontal divider between admin and client sections. Uses
   the same --border token as card edges so it disappears into the
   theme — present but not loud. */
.property-notes-section-client {
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}
/* RAP-128 — scoped with `.property-notes` so this actually wins.
   `.field label` (styles.css:186) is specificity 0,1,1 and sets `display: block`,
   so the bare `.property-notes-label` (0,1,0) never applied its flex column: the
   title and the sub-label were laid out inline and only LOOKED stacked because
   the stale `.property-notes { white-space: pre-wrap }` rule was rendering the
   template's newline between the two spans as a real line break. Deleting that
   stale rule exposed the override, so the stacking is declared properly now
   instead of depending on source whitespace. */
.property-notes .property-notes-label {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  margin-bottom: 0;
}
.property-notes-label-title {
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--text-primary);
  letter-spacing: 0;
}
.property-notes-label-sub {
  font-weight: 400;
  margin-left: 0;
}
.property-notes-footer {
  padding-top: 0.25rem;
}

/* Polish phase E — count badge in the lead-detail Properties section
   header. Subtle pill matching existing .badge defaults; spaced and
   vertically centered against the h2 text. */
.lead-properties-heading {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.lead-properties-count {
  font-size: 0.75rem;
  font-weight: 600;
}
/* Portal property detail — "Notes from your agent" block preserves
   newlines from the textarea. */
.portal-detail-client-notes-body {
  white-space: pre-line;
}

/* /properties aggregate-state badges — compact + clickable look. */
.properties-aggregate-badges {
  display: inline-flex; flex-wrap: wrap;
  gap: 0.375rem;
  margin-top: 0.25rem;
}
.properties-aggregate-badge {
  display: inline-flex; align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.4375rem;
  font-size: 0.6875rem;
  font-weight: 600;
  border-radius: 999px;
  border: 1px solid transparent;
}
.properties-aggregate-badge .properties-aggregate-count {
  font-weight: 700;
}

/* Phase D.2 — outreach indicator badge in the lead Properties tab.
   Reuses the decision-pill-green visual; only adds the inline spacing
   so it sits cleanly below the city/state/zip line in the address
   cell. Hover tooltip carries the exact ISO timestamp. */
.outreach-badge-row {
  margin-top: 0.25rem;
}
.outreach-badge {
  cursor: help;
}

/* K.F.3.C.1 — Client-note preview row.
   Sits beneath the property row in the lead detail table OR inline
   in the property-detail interests table's NOTES cell. */
.client-note-preview {
  display: inline-flex; align-items: center;
  gap: 0.375rem;
  max-width: 100%;
  cursor: pointer;
  background: none;
  border: 1px dashed rgba(148, 163, 184, 0.35);
  border-radius: var(--radius-sm);
  padding: 0.125rem 0.4375rem;
  color: var(--text-secondary);
  font-size: 0.75rem;
  text-align: left;
}
.client-note-preview:hover {
  border-color: rgba(148, 163, 184, 0.7);
  color: var(--text-primary);
}
.client-note-preview-icon { font-size: 0.875rem; opacity: 0.7; }
.client-note-preview-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 22ch;
}
.client-note-preview-count {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.client-notes-popover {
  margin-top: 0.375rem;
  padding: 0.625rem 0.75rem;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 0.8125rem;
  max-height: 220px;
  overflow-y: auto;
}
.client-notes-popover ul { list-style: none; padding: 0; margin: 0; }
.client-notes-popover li {
  display: flex; flex-direction: column; gap: 0.125rem;
  padding: 0.4375rem 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.18);
}
.client-notes-popover li:last-child { border-bottom: none; }
.client-notes-popover .author-tag {
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}
.client-notes-popover .author-tag.client { color: rgb(16, 185, 129); }
.client-notes-popover .author-tag.admin,
.client-notes-popover .author-tag.owner { color: rgb(99, 102, 241); }

/* K.F.3.C.3 — Status advance buttons in the lead-detail row. */
.lead-properties-status-cell {
  display: flex; flex-direction: column;
  gap: 0.25rem;
  align-items: flex-start;
}
/* UI/UX overhaul follow-up (Increment D) — bird's-eye access-type line
   under each per-lead row, mirroring the client portal mapping. */
.lead-properties-access-hint {
  line-height: 1.3;
  max-width: 22rem;
}
/* K.O Phase C — the Increment E toolbar select + header click-cycle
   are gone. One mechanism: a multi-select filter menu anchored in the
   Status column header, plus an explicit Date added sort toggle. */
.lead-properties-status-th {
  position: relative;
  white-space: nowrap;
}
.status-filter-toggle,
.date-sort-toggle {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-weight: inherit;
  color: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.status-filter-toggle:hover,
.date-sort-toggle:hover { color: var(--accent); }
.lead-properties-status-th.is-filtered .status-filter-toggle { color: var(--accent); }
.status-filter-menu {
  /* position: fixed — absolute positioning got clipped by the .card
     overflow:hidden + .table-wrap overflow-x:auto ancestors (K.O
     review finding). Fixed elements ignore ancestor overflow; the JS
     anchors top/left to the toggle on open and closes on scroll. */
  position: fixed;
  z-index: 60;
  min-width: 13rem;
  max-height: min(20rem, 70vh);
  overflow-y: auto;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  text-align: left;
}
.status-filter-menu[hidden] { display: none; }
.status-filter-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.375rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-weight: 400;
  font-size: 0.8125rem;
  color: var(--text-primary);
  white-space: nowrap;
}
.status-filter-option:hover { background: var(--bg-3); }
.status-filter-all {
  align-self: flex-start;
  padding: 0.2rem 0.375rem;
}
.lead-properties-no-match { text-align: center; padding: 1rem; }
.lead-properties-status-actions {
  display: flex; flex-wrap: wrap;
  gap: 0.25rem;
  align-items: center;
}
.lead-properties-status-actions button {
  font-size: 0.6875rem;
  padding: 0.125rem 0.4375rem;
}
/* K.F.4.A.4 — "+ Schedule tour" / "Reschedule tour" sits on yellow
   rows. Renders as an inline text link, not a button-pill. */
.lead-properties-status-actions .link-button {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
  font-size: 0.75rem;
}
.lead-properties-status-actions .link-button:hover { color: var(--accent-strong, var(--accent)); }

/* ─────────────────────────────────────────────────────────────────
   K.F.3.B.5 — Failed uploads (admin retry surface)
   ───────────────────────────────────────────────────────────────── */

.lead-failed-uploads {
  margin-top: 1rem;
}
.failed-uploads-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}
.failed-upload-row {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-3);
}
.failed-upload-thumb {
  width: 96px;
  aspect-ratio: 16 / 9;
  flex-shrink: 0;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  padding: 0; margin: 0;
  cursor: zoom-in;
}
.failed-upload-thumb:hover { border-color: var(--border-strong); }
.failed-upload-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
}
.failed-upload-thumb-placeholder {
  font-size: 1.5rem;
  opacity: 0.5;
  user-select: none;
}

/* ── In-page attachment viewer (RAP-69, promoted by RAP-79) ──────────────
 * The standard viewer for every attachment/document/file open in the CRM
 * (RAP-79) — files never front a new tab by default, which also keeps the
 * RAP-69 invariant: the recording tab never goes hidden (visibilitychange
 * would salvage the recording). z-index 900 sits above page chrome but
 * BELOW the recorder pill (.cr-pill-host, 1000) so Stop/Pause stay
 * reachable. */
.att-viewer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 900;
  background: rgba(0, 0, 0, 0.8);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  animation: modalFade 0.15s ease;
}
.att-viewer {
  display: flex;
  flex-direction: column;
  max-width: min(1100px, 96vw);
  max-height: 94vh;
  min-width: min(340px, 92vw);
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  animation: modalIn 0.18s cubic-bezier(0.2, 0.8, 0.3, 1);
}
.att-viewer-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.55rem 0.9rem;
  border-bottom: 1px solid var(--border);
  flex: none;
}
.att-viewer-name {
  font-size: 0.9rem;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.att-viewer-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: none;
}
.att-viewer-body audio {
  width: min(420px, 86vw);
  margin: 2rem 1.25rem;
}
.att-viewer-body {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  min-height: 120px;
}
.att-viewer-body img {
  max-width: 100%;
  max-height: calc(94vh - 56px);
  display: block;
  object-fit: contain;
}
.att-viewer-body iframe {
  width: min(1000px, 94vw);
  height: calc(94vh - 56px);
  border: 0;
}
.att-viewer-nopreview {
  padding: 2rem 1.5rem;
  max-width: 420px;
  text-align: center;
}
.failed-upload-body {
  flex: 1;
  min-width: 0;
  display: flex; flex-direction: column; gap: 0.125rem;
}
.failed-upload-actions {
  display: flex; flex-direction: column; gap: 0.375rem;
  flex-shrink: 0;
}
.failed-upload-row.is-processing { opacity: 0.72; }
.failed-upload-processing {
  display: inline-flex; align-items: center; gap: 0.375rem;
  padding: 0.375rem 0.625rem;
  border: 1px dashed var(--border-color, #444);
  border-radius: 6px;
  white-space: nowrap;
}
@media (max-width: 560px) {
  .failed-upload-row { flex-wrap: wrap; }
  .failed-upload-thumb { width: 80px; }
  .failed-upload-actions { flex-direction: row; width: 100%; }
  .failed-upload-actions button { flex: 1; }
}

/* =====================================================
   PHASE K.F.5.B — PER-INTEREST SHARE ACCESS
   ===================================================== */

/* Property details card header gets two right-side slots: the share-
   access button (admin opt-in) and the auto-save status flash. */
.property-details-header-right {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
}

.property-share-access-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.375rem 0.75rem;
  border-radius: var(--radius-md);
  background: var(--bg-3);
  color: var(--text-secondary);
  border: 1px solid var(--border);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.property-share-access-btn:hover:not(:disabled) {
  background: var(--bg-hover);
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.property-share-access-btn.is-sharing {
  background: rgba(16, 185, 129, 0.18);
  border-color: rgba(16, 185, 129, 0.55);
  color: rgb(16, 185, 129);
}
.property-share-access-btn.is-sharing:hover:not(:disabled) {
  background: rgba(16, 185, 129, 0.28);
  border-color: rgba(16, 185, 129, 0.75);
  color: rgb(16, 185, 129);
}
.property-share-access-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* =====================================================
   K.G.B — Lead detail Contacts section
   (K.O Phase D: embedded inside the Household card, which owns the
   outer margin — the embedded div keeps only the list rhythm.)
   ===================================================== */
section#lead-contacts-section {
  margin-bottom: 1rem;
}
#lead-contacts-section .contacts-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

/* K.O Phase D — Household section: one card for contacts + linked
   leads, with the "+ Add person" chooser menu. */
#lead-household-section {
  margin-bottom: 1rem;
}
#lead-household-section .relationships-list {
  margin-top: 0.625rem;
}
.household-add-wrap {
  position: relative;
}
.household-add-menu {
  /* position: fixed for the same overflow-clipping reason as
     .status-filter-menu — the Household .card has overflow:hidden. */
  position: fixed;
  z-index: 60;
  min-width: 17rem;
  max-width: min(22rem, calc(100vw - 1rem));
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  padding: 0.375rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.household-add-menu[hidden] { display: none; }
.household-add-option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.125rem;
  text-align: left;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.625rem;
  color: var(--text-primary);
  cursor: pointer;
  font: inherit;
}
.household-add-option:hover { background: var(--bg-3); }
.household-add-option .muted { white-space: normal; }
.chip-link {
  cursor: pointer;
  border: 1px solid var(--border);
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: 0.6875rem;
}
.chip-link:hover { border-color: var(--accent); }
.contact-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem 0.85rem;
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  border: 1px solid var(--border);
}
.contact-row-primary {
  border-color: var(--accent, #5a8fff);
  background: rgba(90, 143, 255, 0.06);
}
.contact-row-main {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
  flex: 1;
}
.contact-name {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.chip-primary {
  background: rgba(90, 143, 255, 0.18);
  color: rgb(140, 175, 255);
  border-color: rgba(90, 143, 255, 0.45);
}
/* RAP-261 item 9 — the MINOR chip on a household row.
   Built from the --badge-indigo-* family rather than from fresh literals, so
   all seven RAP-179 schemes redefine it with the rest of their palette. The
   neighbouring .chip-primary above predates that rule and carries raw rgba;
   it is left alone (it is not what this issue is about) rather than restyled
   as a drive-by. */
.chip-minor {
  background: var(--badge-indigo-bg);
  color: var(--badge-indigo-fg);
  border-color: var(--badge-indigo-bd);
}
/* RAP-261 item 9 — the birthday question on the ADD form only. `fieldset`
   carries the browser's own border and padding, which would box one question
   inside an already-boxed form; the legend is the label. */
.contact-minor-ask {
  border: none;
  margin: 0;
  padding: 0;
  min-width: 0;
}
/* Matches the sibling `.field span` labels exactly. Without the resets this
   inherits `.form-grid legend`, which is an uppercase section heading — so the
   one question in the middle of the form shouted while every label around it
   did not. The values are read off `.field span`, not invented. */
.contact-minor-ask legend {
  padding: 0;
  font-size: 0.9375rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text-primary);
  margin-bottom: 0.25rem;
}
/* Two class selectors, deliberately: `.field label { display: block }` at the
   top of this file is (0,1,1) and would otherwise stack the radio above its own
   word, which is exactly what it did on the first pass. */
.contact-minor-ask .contact-minor-radio {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 1rem 0 0;
  font-size: 0.9375rem;
  font-weight: 400;
  letter-spacing: normal;
  color: var(--text-primary);
  cursor: pointer;
}
.contact-minor-ask .contact-minor-radio input {
  margin: 0;
  width: auto;
  cursor: pointer;
}
.field-hint { display: block; margin-top: 0.2rem; }
.contact-fields {
  display: flex;
  gap: 0.85rem;
  flex-wrap: wrap;
}
.contact-doc-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 0.25rem;
  cursor: pointer;
  user-select: none;
}
.contact-doc-toggle input {
  margin: 0;
  cursor: pointer;
}
/* RAP-255 — per-member client event notification controls. Sits under the
   document toggle on every contact row, primary included: a partner is a
   first-class recipient here, unlike the document grid.

   Wraps rather than scrolls, because at 375px the two labels plus the channel
   chip cannot share a row and a horizontally-scrolling control row is the one
   thing RAP-200 spent a whole phase removing. */
.contact-notify {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.75rem;
  margin-top: 0.3rem;
}
.contact-notify-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}
.contact-notify-toggle input {
  margin: 0;
  cursor: pointer;
}
/* RAP-261 item 5 — the hint sits on its OWN line under the controls.
   `flex-basis: 100%` inside the wrapping row is what forces the break without
   a second container; the row already wraps at 375px, so the hint simply takes
   the width it needs at every size. */
.contact-notify-hint {
  flex-basis: 100%;
  margin: 0.15rem 0 0;
  line-height: 1.4;
}
/* The channel badge. `--text-secondary`, not `--text-muted`: this is the only
   indicator of HOW a client is reached, and RAP-200 measured muted at 2.96:1
   on Dark — under the 3:1 floor for a control-adjacent affordance. Secondary
   is 7.01:1 at worst across the seven schemes. */
.chip.chip-channel {
  font-size: 0.72rem;
  padding: 0.1rem 0.45rem;
  color: var(--text-secondary);
  background: var(--bg-3);
  border: 1px solid var(--border);
}

/* Phase Employment-Types.2 — per-contact employment type select. Compact
   inline control matching the muted-label aesthetic of .contact-doc-toggle. */
.contact-employment-type {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 0.25rem;
  flex-wrap: wrap;
}
.contact-employment-type select,
.documents-employment-type select {
  appearance: none;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: inherit;
  font-size: 0.8rem;
  padding: 0.3rem 1.7rem 0.3rem 0.6rem;
  cursor: pointer;
  max-width: 100%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'><path d='M1 1L5 5L9 1' stroke='%239aa0ac' stroke-width='1.5' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.contact-employment-type select:hover:not(:focus),
.documents-employment-type select:hover:not(:focus) {
  border-color: var(--border-strong);
}
.contact-employment-type select:focus,
.documents-employment-type select:focus {
  outline: none;
  border-color: var(--accent);
  background-color: var(--bg-3);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* Phase Employment-Types.2.1 — employment-type control relocated into each
   contact's Documents card, above the upload-link toolbar. Aligns to the
   card's horizontal padding; reuses the select styling above. */
.documents-employment-type {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  padding: 0.5rem 1.25rem 0;
  flex-wrap: wrap;
}
.contact-row-actions {
  display: flex;
  gap: 0.15rem;
  flex-shrink: 0;
}
.contact-form .form-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.75rem;
}
@media (max-width: 640px) {
  .contact-form .form-grid {
    grid-template-columns: 1fr;
  }
  .contact-row {
    flex-direction: column;
    align-items: flex-start;
  }
  .contact-row-actions {
    align-self: flex-end;
  }
}

/* ── Phase K.G.4 — Notifications banner ───────────────────────────── */

#notifications-banner-root {
  position: sticky;
  top: 0;
  z-index: 1000;
}

.notif-banner {
  background: var(--notif-bg);
  color: var(--notif-fg);
  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  font-size: 0.92rem;
  line-height: 1.3;
}
.notif-banner-inner {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0.55rem 1rem;
  width: 100%;
}

.notif-banner-collapsed .notif-banner-inner {
  background: transparent;
  border: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  width: 100%;
  text-align: left;
}

.notif-icon {
  font-size: 1.05rem;
  flex-shrink: 0;
}

.notif-message {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.notif-actions {
  display: flex;
  gap: 0.35rem;
  flex-shrink: 0;
}

.notif-view,
.notif-dismiss,
.notif-dismiss-all,
.notif-expand,
.notif-collapse {
  background: var(--notif-btn-bg);
  border: 1px solid var(--notif-btn-bd);
  color: inherit;
  padding: 0.2rem 0.55rem;
  border-radius: 4px;
  font-size: 0.85rem;
  cursor: pointer;
  font-weight: 500;
}
.notif-view:hover,
.notif-dismiss:hover,
.notif-dismiss-all:hover {
  background: rgba(255, 255, 255, 0.32);
}

.notif-dismiss {
  padding: 0.2rem 0.45rem;
  font-size: 0.95rem;
  line-height: 1;
}

.notif-banner-collapsed {
  display: flex;
  align-items: center;
}

.notif-banner-collapsed .notif-dismiss-all {
  margin-right: 1rem;
  flex-shrink: 0;
}

.notif-chevron {
  margin-left: auto;
  font-size: 0.8rem;
}

.notif-banner-expanded .notif-banner-inner {
  align-items: center;
}

/* 2026-07-04 — the title strip is no longer the collapse control (its
   only visible cue was a tiny ▴ flush against Clear all → accidental
   Clear all on mobile). Collapse is an explicit labeled button with a
   44px tap target, pushed right but separated from Clear all by a
   deliberate gap; Clear all gets an armed confirm state. */
.notif-expanded-title {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: 1;
  min-width: 0;
}
.notif-banner-expanded .notif-banner-inner { flex-wrap: wrap; }
.notif-banner-expanded .notif-collapse {
  min-height: 44px;
  padding: 0.45rem 0.9rem;
  margin-right: 1.25rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
}
.notif-banner-expanded .notif-collapse .notif-chevron {
  margin-left: 0;
  font-size: 0.7rem;
}
.notif-banner-expanded .notif-dismiss-all {
  min-height: 44px;
  padding: 0.45rem 0.9rem;
}
.notif-dismiss-all.is-armed {
  background: #b91c1c;
  border-color: #7f1d1d;
  color: #fff;
  font-weight: 700;
}
.notif-dismiss-all.is-armed:hover { background: #991b1b; }

.notif-list {
  list-style: none;
  margin: 0;
  padding: 0 1rem 0.4rem;
  max-width: 1280px;
  margin: 0 auto;
}

.notif-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.35rem 0;
  border-top: 1px solid var(--notif-item-line);
}
@media (max-width: 640px) {
  .notif-banner-inner {
    padding: 0.45rem 0.65rem;
    gap: 0.45rem;
    font-size: 0.85rem;
  }
  .notif-list {
    padding: 0 0.65rem 0.35rem;
  }
  .notif-item .notif-message {
    font-size: 0.85rem;
  }
}

/* ── Phase K.G.5 — Tasks view + filters ───────────────────────────── */

.tasks-section .card-header {
  margin-bottom: 0.5rem;
}

.tasks-filters {
  display: flex;
  gap: 0.35rem;
  margin: 0.25rem 0 1rem;
  flex-wrap: wrap;
}

.tasks-filters-inline {
  margin: 0 0 0.75rem;
}

.tasks-filter {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: 0.85rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.tasks-filter:hover {
  background: var(--bg-2);
  color: var(--text-primary);
}

.tasks-filter.is-active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--text-primary);
}

.tasks-filter-count {
  background: var(--bg-3);
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  font-size: 0.75rem;
  min-width: 1.2rem;
  text-align: center;
}

.tasks-filter-count.is-overdue {
  background: var(--danger-soft);
  color: #fff;
}

.task-person {
  margin-top: 0.15rem;
}

.task-toggle.is-readonly {
  cursor: default;
  opacity: 0.7;
}

/* RAP-159 — one line under each section heading saying what the section is.
   Three similarly-shaped lists need it: "My tasks" and "Assigned to me by
   others" are only told apart by who put the task there. */
.tasks-section-hint {
  margin: -0.25rem 0 0.6rem;
}

/* Three stacked sections is a lot of vertical run on a phone. Tighten the
   gaps that only exist to separate cards on a wide screen, and let the chip
   row scroll sideways rather than wrap to two lines above every list. */
@media (max-width: 480px) {
  .tasks-section {
    margin-bottom: 0.75rem;
  }
  .tasks-section .card-header h2 {
    font-size: 1rem;
  }
  .tasks-section-hint {
    margin: -0.15rem 0 0.5rem;
  }
  .tasks-section .tasks-filters {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin-bottom: 0.6rem;
  }
  .tasks-section .tasks-filters::-webkit-scrollbar {
    display: none;
  }
  .tasks-section .tasks-filter {
    flex: 0 0 auto;
  }
}

/* ──────────────────────────────────────────────────────────────
 * Phase K.I.C/D — Activity feed (global view + per-lead tab).
 * ────────────────────────────────────────────────────────────── */
.activity-filters .card-body { padding: 1rem; }
.activity-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: end;
}
.activity-filter-row label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.activity-filter-row label.checkbox-inline {
  flex-direction: row;
  align-items: center;
  gap: 0.4rem;
  padding-bottom: 0.5rem;
}
.activity-filter-row input[type="date"],
.activity-filter-row select {
  background: var(--bg-2);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.4rem 0.6rem;
  min-width: 12ch;
}
.activity-attention { border-left: 3px solid #d99a2b; }
.activity-feed {
  list-style: none;
  margin: 0;
  padding: 0;
}
.activity-feed-attention { border-left: none; }
.activity-row {
  display: grid;
  grid-template-columns: 8rem 1fr;
  gap: 0.75rem;
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--border-subtle, var(--border));
  line-height: 1.4;
  font-size: 0.95rem;
}
.activity-row:last-child { border-bottom: none; }
.activity-row-attention {
  background: rgba(217, 154, 43, 0.07);
}
.activity-row-time { white-space: nowrap; }
.activity-row-desc code {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 0.85em;
  padding: 0.05rem 0.3rem;
  background: var(--bg-3);
  border-radius: 3px;
}
.activity-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem;
  border-top: 1px solid var(--border-subtle, var(--border));
}
@media (max-width: 720px) {
  .activity-row { grid-template-columns: 1fr; }
  .activity-row-time { order: 2; }
}

/* ──────────────────────────────────────────────────────────────
 * Phase K.I.E.3 — MTD widget (dashboard).
 * ────────────────────────────────────────────────────────────── */
.mtd-card { margin-top: 1rem; }
.mtd-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
.mtd-table th, .mtd-table td {
  padding: 0.55rem 1rem;
  text-align: left;
  border-bottom: 1px solid var(--border-subtle, var(--border));
}
.mtd-table th {
  color: var(--text-secondary);
  font-weight: 500;
  font-size: 0.85rem;
}
.mtd-table th.num, .mtd-table td.num { text-align: right; }
.mtd-table tbody tr:last-child td { border-bottom: none; }

/* Clickable count cells — Dashboard MTD drill-through. The number sits
 * inside an inline anchor so the underlying SPA router intercepts it.
 * Underline + accent on hover signals "this is interactive" without
 * adding a heavy button chrome that would jar against the table density. */
.mtd-table .mtd-drill {
  color: var(--text-primary);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  transition: background 0.12s ease, color 0.12s ease;
}
.mtd-table .mtd-drill:hover {
  color: var(--accent);
  background: var(--accent-soft);
  text-decoration: none;
}
.mtd-table .mtd-drill:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Drill-through banner on /leads. Rendered above the filter bar when
 * created_in or closed_in is in the URL — distinct from the in-page
 * filter dropdowns to communicate "you arrived here via a link". */
.drill-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1rem;
  margin-top: 1rem;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
}
.drill-banner-body {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.drill-banner-icon {
  color: var(--accent);
  font-size: 1.05rem;
  line-height: 1;
}
.drill-banner-text {
  color: var(--text-primary);
  font-size: 0.95rem;
  line-height: 1.35;
}
.drill-banner-text strong { font-weight: 600; }
.drill-banner-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}
.drill-banner-back,
.drill-banner-clear {
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--accent);
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  line-height: 1.2;
  transition: background 0.12s ease, color 0.12s ease;
}
.drill-banner-back:hover,
.drill-banner-clear:hover {
  background: var(--accent);
  color: var(--on-accent);
  text-decoration: none;
}
@media (max-width: 600px) {
  .drill-banner {
    flex-direction: column;
    align-items: flex-start;
  }
  .drill-banner-actions { align-self: flex-end; }
}

/* ──────────────────────────────────────────────────────────────
 * Phase K.I.F.2 — Settings → Users login summary emphasis.
 * Amber for > 3 failed attempts in 30d; red for > 10.
 * ────────────────────────────────────────────────────────────── */
.failed-logins-amber {
  color: #d99a2b;
  font-weight: 600;
}
.failed-logins-red {
  color: #d94d4d;
  font-weight: 700;
  background: rgba(217, 77, 77, 0.12);
  padding: 0.1rem 0.45rem;
  border-radius: 4px;
}

/* ──────────────────────────────────────────────────────────────
 * Phase K.J.C — /onehome Pending Outreach queue.
 * Card-per-row layout, action footer on the right, optimistic
 * fade on mark-sent. The toast pins to the bottom-right and
 * coexists with the global notifications banner.
 * ────────────────────────────────────────────────────────────── */
.onehome-page { max-width: 920px; }

.onehome-list {
  display: flex; flex-direction: column;
  gap: 0.75rem;
  margin-top: 1rem;
}

.onehome-row {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  transition: opacity .2s ease, transform .2s ease;
}
.onehome-row-fading {
  opacity: 0;
  transform: translateY(-2px);
  pointer-events: none;
}

.onehome-row-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; margin-bottom: 0.5rem;
}
.onehome-row-title {
  font-weight: 600; font-size: 0.95rem;
  display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: baseline;
}
.onehome-lead-link {
  color: var(--text-primary);
  text-decoration: none;
}
.onehome-lead-link:hover { text-decoration: underline; }
.onehome-row-time { font-size: 0.8rem; white-space: nowrap; }

.onehome-row-body {
  display: flex; flex-direction: column;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-top: 1px dashed var(--border);
}

.onehome-property-address { font-weight: 500; }
.onehome-property-mls { font-size: 0.8rem; }

.onehome-agent { font-size: 0.9rem; }
.onehome-agent-contact {
  display: flex; flex-wrap: wrap; gap: 0.4rem;
  margin-top: 0.25rem;
}
.onehome-agent-pill {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  font-size: 0.8rem;
  color: var(--text-secondary);
  background: var(--bg-2);
  border-radius: 999px;
}
.onehome-agent-pending {
  font-size: 0.85rem;
  font-style: italic;
}

.onehome-row-actions {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  margin-top: 0.5rem;
  justify-content: flex-end;
}

.onehome-load-more { display: flex; justify-content: center; margin: 1rem 0 2rem; }

.onehome-empty {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 2.5rem 1rem;
  text-align: center;
  margin-top: 1rem;
}
.onehome-empty-icon { font-size: 2rem; margin-bottom: 0.5rem; }
.onehome-empty-text { font-weight: 600; }
.onehome-empty-sub { font-size: 0.875rem; margin-top: 0.25rem; }

.onehome-toast {
  position: fixed;
  right: 1rem; bottom: 1rem;
  z-index: 50;
  display: inline-flex; align-items: center; gap: 0.75rem;
  padding: 0.6rem 0.9rem;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  font-size: 0.875rem;
}
.onehome-toast[hidden] { display: none; }
.onehome-toast .error { color: #d94d4d; }

/* K.J.C — nav overflow polish. With 8+ items the bar can get crowded
   on tablet/mobile. Allow horizontal scroll without wrapping; the
   brand + user controls stay fixed at the edges. */
@media (max-width: 820px) {
  .nav-links {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    flex-wrap: nowrap;
  }
  .nav-links::-webkit-scrollbar { display: none; }
  .nav-link { flex-shrink: 0; }
}


/* =====================================================
   K.M A.1 — MOBILE NAV DRAWER (<=820px; desktop unchanged)
   ===================================================== */

.nav-hamburger {
  display: none;
  position: relative;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 1.05rem;
  line-height: 1;
  padding: 0.45rem 0.6rem;
  cursor: pointer;
}
.nav-hamburger-badge {
  position: absolute;
  top: -6px;
  right: -6px;
}

.nav-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 55;
}
.nav-drawer {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: min(78vw, 300px);
  background: var(--bg-1);
  border-right: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  z-index: 60;
  display: flex;
  flex-direction: column;
  padding: 1rem 0.75rem;
  transform: translateX(-105%);
  transition: transform 0.18s ease;
  overflow-y: auto;
}
.nav-drawer.is-open { transform: translateX(0); }
.nav-drawer-links {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}
.nav-drawer-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 0.875rem;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  text-decoration: none;
}
.nav-drawer-link:hover { color: var(--text-primary); background: var(--bg-2); }
.nav-drawer-link.is-active {
  color: var(--text-primary);
  background: var(--bg-2);
}
.nav-drawer-footer {
  margin-top: auto;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
  /* K.N A — Sign out left, avatar + account name right. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.nav-drawer-signout { text-align: left; padding: 0.7rem 0.875rem; flex: 0 0 auto; }
.nav-drawer-account {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.85rem;
}
.nav-drawer-account:hover { background: var(--bg-2); color: var(--text-primary); }
.nav-drawer-avatar { width: 28px; height: 28px; font-size: 0.75rem; flex: 0 0 auto; }
.nav-drawer-account-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 820px) {
  .nav-hamburger { display: inline-flex; align-items: center; }
  .nav .nav-links { display: none; }
  .nav .nav-signout { display: none; }
  .nav-inner { gap: 0.75rem; padding: 0.5rem 0.875rem; }
}
@media (min-width: 821px) {
  .nav-drawer, .nav-drawer-backdrop { display: none !important; }
}

/* =====================================================
   K.M A.2 — LEADS CALENDAR FITS PHONE WIDTH
   (aspect-ratio + min-height forced ~80px/cell = 584px min row;
    at small widths relax both so 7 columns fit in 390px)
   ===================================================== */

.cal-weekday-short { display: none; }

@media (max-width: 560px) {
  .cal-weekday-full { display: none; }
  .cal-weekday-short { display: inline; }
  .cal-weekdays, .cal-days { gap: 2px; }
  .calendar-grid { padding: 0.5rem 0.375rem 0.75rem; }
  .cal-day {
    aspect-ratio: auto;
    min-height: 46px;
    min-width: 0;
    padding: 0.2rem 0.25rem 0.2rem;
    border-radius: 4px;
  }
  .cal-date { font-size: 0.7rem; }
}

/* K.M C.8 — permission descriptions in the team drawer */
.drawer-perm-desc { display: block; font-weight: 400; margin-top: 2px; max-width: 260px; }

/* K.M C.1 — leads-calendar status legend + larger dots */
.cal-status-legend {
  display: flex; flex-wrap: wrap; gap: 0.4rem 0.9rem;
  padding: 0.6rem 0.75rem 0.9rem;
  border-top: 1px solid var(--border);
  margin-top: 0.5rem;
}
.cal-status-legend .legend-item {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: 0.75rem; color: var(--text-secondary);
}
.cal-dots .status-dot { width: 11px; height: 11px; }
@media (max-width: 560px) {
  .cal-dots .status-dot { width: 8px; height: 8px; }
}

/* K.M C.6 — agent page property list */
.agent-property-list { list-style: none; margin: 0; padding: 0 0 0.25rem; display: flex; flex-direction: column; gap: 0.45rem; }
.agent-property-list li { display: flex; align-items: baseline; gap: 0.6rem; }

/* K.M C.7 — unknown-texter capture actions + lead picker */
.messages-thread-link-actions { display: flex; gap: 0.4rem; margin-top: 0.35rem; position: relative; flex-wrap: wrap; }
.thread-link-picker {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 40;
  width: min(320px, 90vw);
  background: var(--bg-1); border: 1px solid var(--border-strong);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  padding: 0.6rem;
}
.thread-link-picker-input { width: 100%; margin-bottom: 0.5rem; }
.thread-link-picker-results { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow-y: auto; }
.thread-link-picker-row {
  display: flex; justify-content: space-between; gap: 0.75rem; align-items: baseline;
  background: none; border: none; color: var(--text-primary);
  padding: 0.45rem 0.5rem; border-radius: var(--radius-sm); cursor: pointer; text-align: left;
}
.thread-link-picker-row:hover { background: var(--bg-2); }

/* K.M D.1 — lead header ⋯ More menu */
.lead-more-wrap { position: relative; display: inline-block; }
.lead-more-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 45;
  min-width: 200px;
  background: var(--bg-1); border: 1px solid var(--border-strong);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  padding: 0.35rem; display: flex; flex-direction: column; gap: 2px;
}
.lead-more-item {
  background: none; border: none; text-align: left; cursor: pointer;
  color: var(--text-primary); padding: 0.55rem 0.7rem; border-radius: var(--radius-sm);
  font-size: 0.875rem;
}
.lead-more-item:hover { background: var(--bg-2); }
.lead-more-item.is-danger { color: var(--danger, #f87171); }

/* K.M D.2 — collapsed notifications: compact pill, not a full-width bar */
.notif-banner-pill {
  width: auto !important;
  display: inline-flex;
  border-radius: 999px;
  margin: 0.35rem 0 0.35rem 0.75rem;
  padding: 0 !important;
}
.notif-banner-pill .notif-banner-inner {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
}

/* K.M D.5 — portal "No longer interested".
   RAP-127 retired the low-key link + its .portal-not-interested wrapper:
   it was a second decline control competing with the "Not for me" button,
   and it did something different (notified staff, changed nothing). The
   done-message style stays — the notify outcome still renders it. */
.portal-not-interested-done { margin: 1.2rem 0 0; text-align: center; }
.lead-more-menu[hidden] { display: none; }

/* K.M fixup — notifications pill inside the open drawer.
   The banner root is sticky z-1000 at page top; while the drawer is
   open it relocates into this header row (nav.js setDrawerOpen) so it
   can't overlap the Dashboard item at any badge width. */
.nav-drawer-header { display: flex; justify-content: flex-end; }
.nav-drawer-header:has(#notifications-banner-root:not(:empty)) {
  padding-bottom: 0.6rem;
  margin-bottom: 0.6rem;
  border-bottom: 1px solid var(--border);
}
.nav-drawer-header #notifications-banner-root {
  position: static;
  z-index: auto;
  width: 100%;
  display: flex;
  justify-content: flex-end;
}
.nav-drawer-header .notif-banner-pill { margin: 0; }

/* =====================================================
   K.N A — MOBILE HEADER CONSOLIDATION (≤820px)
   One header row: ☰ + logo left; notifications pill + theme
   toggle right (toggle at the far end). The pill root's THIRD
   home is #nav-notif-slot (drawer-closed, phone width) — see
   placeNotifRoot() in notificationsBanner.js for the decision.
   Desktop (≥821px) keeps the banner above the nav; the slot is
   display:none so the .nav-user flex gap doesn't shift.
   ===================================================== */

.nav-notif-slot { display: none; }

@media (max-width: 820px) {
  /* .nav-links is display:none here, so its flex:1 no longer pushes
     .nav-user to the right edge — do it explicitly. */
  .nav .nav-user { margin-left: auto; }
  /* Avatar leaves the header at phone width (it had no function);
     it lives in the drawer footer now. Scoped to .nav so the drawer
     footer's avatar (same base class) is untouched. */
  .nav .nav-avatar { display: none; }

  .nav-notif-slot {
    display: flex;
    align-items: center;
    min-width: 0;
  }
  .nav-notif-slot #notifications-banner-root {
    position: static;
    z-index: auto;
  }
  .nav-notif-slot .notif-banner-pill { margin: 0; }
  /* Single-notification banner and the expanded list would explode the
     header row — render them as a full-width sheet anchored under the
     sticky .nav instead (the nav is the containing block). Below the
     drawer/backdrop (z 55/60), above page content. */
  .nav-notif-slot .notif-banner-single,
  .nav-notif-slot .notif-banner-expanded {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 30;
    box-shadow: var(--shadow-md);
  }
}

/* =====================================================
   K.N B — SHARED UNDO TOAST (components/toast.js)
   The .toast base (call-recorder.css) is pointer-events:none;
   the component opts back in inline. Undo is a padded,
   thumb-sized target per the K.L lesson — not a text link.
   ===================================================== */

.toast-undo {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  /* K.N visual-pass catch — the .toast base is fixed at left:50%, so
     shrink-to-fit width caps at HALF the viewport (195px at 390px) and
     the message wrapped into a tall ribbon. max-content restores the
     natural one-line width; the cap keeps it inside the phone screen. */
  width: max-content;
  max-width: calc(100vw - 2rem);
}
.toast-undo-btn {
  margin-left: 0.35rem;
  padding: 0.55rem 1.1rem;
  min-height: 40px;
  font-weight: 600;
  flex: 0 0 auto;
}

/* =====================================================
   K.N D — COLLAPSIBLE LEAD FILTERS (≤820px only)
   Search stays visible; the rest of the controls live in
   .filter-collapsible behind the "Filters (N)" button. On
   desktop the wrapper is display:contents — its children
   participate in .filter-bar's flex exactly as before, and
   the toggle button doesn't exist visually.
   ===================================================== */

.filter-toggle { display: none; }
.filter-collapsible { display: contents; }

@media (max-width: 820px) {
  .filter-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.55rem 0.75rem;
  }
  .filter-collapsible { display: none; }
  .filter-bar.filters-open .filter-collapsible {
    display: flex;
    flex-direction: column;
    /* match .filter-bar's own stacked rhythm */
    gap: 0.75rem;
  }
}

/* ── K.P — /sweeps (CRMLS sweep reports) ─────────────────────────── */
.sweeps-list { list-style: none; margin: 0; padding: 0; }
.sweeps-row + .sweeps-row { border-top: 1px solid var(--border, rgba(255,255,255,0.08)); }
.sweeps-row-link {
  display: block; padding: 0.85rem 0.5rem;
  color: inherit; text-decoration: none; border-radius: 8px;
}
.sweeps-row-link:hover { background: var(--overlay-rowhover-bg); }
.sweeps-row-head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.2rem; }
.sweep-items { list-style: none; margin: 0; padding: 0; }
.sweep-item { padding: 0.7rem 0.35rem; }
.sweep-item + .sweep-item { border-top: 1px solid var(--border, rgba(255,255,255,0.08)); }
.sweep-item-head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.sweep-item-error { border-left: 3px solid var(--danger); padding-left: 0.6rem; }
.sweep-item-skipped_abort { opacity: 0.75; }

/* ── RAP-13 — /read-sync (iMessage read-sync health) ─────────────── */
/* Smaller stat-value variant for text values ("3 hours ago"). */
.stat-value-sm { font-size: 1.1rem; line-height: 1.5; }
.property-crmls-freshness { white-space: nowrap; }

/* ── RAP-97 — "Refresh to update" prompt ──────────────────────────── */
/* Shown when the server is running a newer build than this tab. Fixed
   bottom pill so it never reflows the page or covers the composer's
   send button. z-index 880 keeps it BELOW the recording-salvage banner
   (900) — an unsaved recording is always the more urgent message. */
.update-prompt {
  position: fixed;
  right: 1rem;
  bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  z-index: 880;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  max-width: min(28rem, calc(100vw - 2rem));
  padding: 0.6rem 0.75rem;
  border-radius: 10px;
  background: var(--update-prompt-bg);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.14));
  box-shadow: var(--update-prompt-shadow);
  font-size: 0.9rem;
  color: var(--update-prompt-fg);
}
.update-prompt-icon { flex-shrink: 0; opacity: 0.8; }

.update-prompt-text {
  flex: 1;
  min-width: 0;
  line-height: 1.3;
}

.update-prompt-refresh {
  flex-shrink: 0;
  padding: 0.35rem 0.7rem;
  border-radius: 7px;
  border: 0;
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  background: var(--accent, #4f7cff);
  color: var(--on-accent);
}

.update-prompt-refresh:hover { filter: brightness(1.08); }

.update-prompt-dismiss {
  flex-shrink: 0;
  padding: 0 0.3rem;
  border: 0;
  background: transparent;
  color: inherit;
  opacity: 0.6;
  cursor: pointer;
  font: inherit;
  font-size: 1.1rem;
  line-height: 1;
}

.update-prompt-dismiss:hover { opacity: 1; }

@media (max-width: 520px) {
  .update-prompt {
    left: 1rem;
    right: 1rem;
    max-width: none;
  }
}

/* =====================================================
   RAP-143 — LEAD TEMPERATURE
   ===================================================== */

/* ── the read-only icon (list rows, calendar day panel) ───────────── */

.temp-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  font-size: 1.05rem;
  /* Emoji render at wildly different optical weights across platforms;
     a fixed box keeps a column of them aligned regardless. */
  width: 1.4rem;
}
/* Cold is the default on nearly every lead, so at full strength it would
   be the loudest thing in the table purely by repetition — the opposite
   of the point. Dimming it lets 🔥 and ☀️ carry the scan. */
.temp-icon.temp-cold { opacity: var(--temp-cold-opacity); }
.temp-icon.temp-warm { opacity: 0.95; }
.temp-icon.temp-hot  { opacity: 1; }
/* Inline before a name (calendar day panel), rather than in its own cell. */
.temp-icon-inline {
  width: auto;
  margin-right: 0.35rem;
  vertical-align: -0.1em;
}

/* The list table's leading column: as narrow as the glyph, never a
   contributor to horizontal scroll on a phone. */
.data-table th.temp-cell,
.data-table td.temp-cell {
  width: 1.9rem;
  padding-left: 0.5rem;
  padding-right: 0;
  text-align: center;
}

/* ── the one-tap toggle (lead detail header) ──────────────────────── */

/* Sits inline with the lead's name; the row wraps as a unit so a long
   name never pushes the control off its own line. */
.lead-title-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-width: 0;
}
.lead-title-row .page-title {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.temp-toggle {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px — the thumb-target floor. This is the control Mauricio taps most
     on a phone, and it is inline with a heading, so it does not get to be
     a decoration sized to its glyph. */
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-hover);
  cursor: pointer;
  line-height: 1;
  transition: transform 0.12s, background 0.12s, border-color 0.12s, box-shadow 0.12s;
  /* Rapid tapping on iOS otherwise selects the emoji / fires a magnifier,
     and double-tap-to-zoom adds a 300ms delay that makes a cycle feel
     broken. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.temp-toggle .temp-toggle-emoji { font-size: 1.35rem; }
.temp-toggle:hover { border-color: var(--text-muted); }
.temp-toggle:active { transform: scale(0.92); }
.temp-toggle:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
  border-color: var(--accent);
}

/* Warm and hot get a tinted ring so the state is legible from across the
   page, not only from the glyph. Cold stays neutral: it is the resting
   state and should not read as a badge. Colours are literals rather than
   theme tokens because they mean temperature, not brand — and they are
   tuned per theme so the tint survives a light background. */
.temp-toggle.temp-warm {
  background: var(--temp-warm-bg);
  border-color: var(--temp-warm-bd);
}
.temp-toggle.temp-hot {
  background: var(--temp-hot-bg);
  border-color: var(--temp-hot-bd);
}

/* ── the filter chips (Leads list) ────────────────────────────────── */

/* Reuses .filter-chip from the Messages inbox so the two chip rows in the
   app look like one idea. This row lives OUTSIDE .filter-collapsible, so
   it stays on screen at phone width when the other filters collapse. */
.temp-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0 1.25rem 1rem;
}
.temp-chip {
  /* Chips are tapped as often as the toggle; 36px keeps them comfortably
     hittable without turning the row into a wall of pills. */
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.temp-chip.temp-chip-hot.is-active {
  background: var(--temp-chip-hot-bg);
  color: var(--temp-chip-hot-fg);
  border-color: var(--temp-chip-hot-bd);
}
.temp-chip.temp-chip-warm.is-active {
  background: var(--temp-chip-warm-bg);
  color: var(--temp-chip-warm-fg);
  border-color: var(--temp-chip-warm-bd);
}
@media (max-width: 820px) {
  .temp-chip-row { padding: 0 0.75rem 0.75rem; }
}

/* =====================================================
   RAP-179 — SETTINGS → APPEARANCE (scheme picker)

   Every swatch paints itself from the scheme it names: the preview
   carries `data-scheme-preview="<id>"`, which sits on the same CSS rule
   as that scheme's real palette, so these rules only ever reference
   var(--bg-0) / var(--bg-1) / var(--accent) and can never disagree with
   the thing they preview.
   ===================================================== */

.scheme-body {
  padding: 1.25rem;
  display: flex; flex-direction: column; gap: 1.25rem;
}
.scheme-intro, .scheme-footnote { margin: 0; line-height: 1.5; }

.scheme-group { display: flex; flex-direction: column; gap: 0.6rem; }
.scheme-group-title {
  font-size: 0.75rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0;
}
.scheme-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.6rem;
}

.scheme-option {
  display: flex; align-items: center; gap: 0.75rem;
  width: 100%;
  /* 56px of row: this is a tap target on a phone before it is a list. */
  padding: 0.6rem 0.75rem;
  text-align: left;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: inherit; font-size: 0.875rem;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
  -webkit-tap-highlight-color: transparent;
}
.scheme-option:hover { border-color: var(--border-strong); background: var(--bg-hover); }
.scheme-option:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.scheme-option.is-active {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.scheme-option-text {
  display: flex; flex-direction: column; gap: 0.1rem;
  flex: 1 1 auto; min-width: 0;
}
.scheme-option-label { font-weight: 600; }
.scheme-option-hint {
  font-size: 0.75rem; color: var(--text-secondary);
  line-height: 1.35;
}
.scheme-option-check {
  flex: 0 0 auto;
  font-weight: 700;
  color: var(--accent);
  opacity: 0;
}
.scheme-option.is-active .scheme-option-check { opacity: 1; }

/* ── the swatch: a page (bg-0) holding a card (bg-1) ─────────────── */
.scheme-swatch {
  flex: 0 0 auto;
  display: block;
  width: 46px; height: 34px;
  padding: 5px;
  border-radius: 6px;
  background: var(--bg-0);
  border: 1px solid var(--border-strong);
  overflow: hidden;
}
.scheme-swatch-card {
  display: flex; flex-direction: column; justify-content: center;
  gap: 3px;
  width: 100%; height: 100%;
  padding: 4px;
  border-radius: 3px;
  background: var(--bg-1);
  border: 1px solid var(--border);
}
.scheme-swatch-line {
  display: block; height: 2px; border-radius: 1px;
  background: var(--text-secondary);
  width: 100%;
}
.scheme-swatch-line-strong { background: var(--text-primary); width: 70%; }
.scheme-swatch-accent {
  display: block; height: 4px; width: 45%;
  border-radius: 2px;
  background: var(--accent);
}

@media (max-width: 520px) {
  .scheme-grid { grid-template-columns: 1fr; }
  .scheme-body { padding: 1rem; }
}

/* ============================================================
   RAP-200 — iOS auto-zoom on focus (the "Settings opens zoomed
   in" bug)
   ============================================================
   iOS Safari zooms the viewport whenever a text-entry control
   smaller than 16px takes focus, and it does NOT zoom back out
   on blur. Every control in this file was 15px: the shared
   rules say `font-size: 0.9375rem`, and `rem` resolves against
   the ROOT (16px), not `body { font-size: 15px }` — so
   0.9375rem is 15px, exactly one pixel under the threshold.

   Settings is where it was reported because renderSettings()
   focuses #current_password programmatically at render time —
   so the page zooms on OPEN, with no tap. The same latent bug
   sat on every other form in the app, one tap away; these are
   the shared rules, so fixing Settings fixes those too.

   Scoped to `pointer: coarse` rather than a width breakpoint:
   the trigger is the touch device, not the viewport, so this
   has to cover an iPad at 1024px too while leaving a narrow
   desktop window alone.

   SELECTS ARE DELIBERATELY EXCLUDED. iOS renders <select> as a
   native picker wheel rather than an inline caret, and the
   compact ones here (.role-select at 13px, the filter-bar
   controls, .property-status-select) are sized to fit rows
   that a 16px bump would reflow. No <select> was among the
   offenders measured on Settings.

   NOT fixed with `maximum-scale=1` in the viewport meta: that
   would also block deliberate pinch-zoom, which is an
   accessibility regression for anyone who needs to magnify.
   Sizing the text correctly removes the cause instead of
   suppressing the symptom.

   `test:rap200` pins this — it re-derives the effective size
   of every text-entry rule in this file and fails if one drops
   under 16px on touch again. */
@media (pointer: coarse) {
  .field input:not([type="checkbox"]):not([type="radio"]),
  .field textarea,
  .inline-form input:not([type="checkbox"]):not([type="radio"]),
  .inline-form textarea,
  input.inline-edit-control,
  textarea.inline-edit-control,
  .sit-textarea,
  /* Found by test:rap200's sweep rather than by reading the page — the
     first pass covered only the shared .field/.inline-form rules and
     missed every control with a class of its own. Two of these matter
     as much as the reported bug: .messages-composer-input is where the
     phone gets used most, and .intro-template-editor is INSIDE the
     Settings page that was reported. */
  .messages-composer-input,
  .messages-search-input,
  .merge-search-input,
  .purge-confirm-input,
  .intro-template-editor input[type="text"],
  .intro-template-editor input[type="email"],
  .intro-template-editor input[type="password"],
  .intro-template-editor textarea {
    font-size: 1rem;
  }
}

/* ============================================================
   RAP-200 — collapsible settings cards
   ============================================================
   Behaviour and the reasoning behind the markup shape live in
   public/js/views/settingsCollapse.js. Every colour here is a
   token (RAP-179): these rules are theme-BLIND and must render
   in all seven schemes without a single per-scheme override. */

.card-collapse-heading {
  /* Takes the free space so the whole row is tappable, while any
     action button in the header stays a flex sibling on the right. */
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}

/* Pre-existing, surfaced by the accordion: `button.primary` carries
   `width: 100%` from the login form (line ~962), so the "+ New
   template" / "+ New snippet" buttons took a 100%-of-header flex
   basis and squeezed their own card title down to a few characters
   at 375px — "Snippets" rendered as "Snippe". Sizing them to their
   text is the same override .lead-properties-list already needed for
   its "+ Add Property by MLS#" button. */
.card-header > button.primary { width: auto; }

/* A header action ("+ New template") opens an editor INSIDE the card
   body, so while the card is collapsed it is a control whose entire
   effect is invisible — and at 375px "Follow-up templates" plus its
   button cannot share a row, so keeping it forced both the title and
   the button to wrap. Hiding it until the card is open is what makes
   every collapsed row a single clean line of text.
   Scoped to button/a rather than "everything that isn't the heading"
   so a future status badge in a header still shows in the index. */
.card.is-collapsible:not(.is-open) > .card-header > button,
.card.is-collapsible:not(.is-open) > .card-header > a { display: none; }

.card-collapse-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  /* 44px tap target without changing the header's height: grow the
     hit area with padding, take it straight back with an equal
     negative margin. Same trick as RAP-198's .lead-phone-link. */
  padding: 0.625rem 0;
  margin: -0.625rem 0;
}
.card-collapse-toggle:focus-visible {
  outline: none;
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.card-collapse-title { min-width: 0; }
.card-collapse-chevron {
  flex: none;
  display: flex;
  align-items: center;
  /* --text-secondary, NOT --text-muted. The chevron is the only thing
     that says whether a section is open, so WCAG 1.4.11 asks 3:1 of
     it. Measured against the card surface in all seven schemes, muted
     is 2.96:1 on Dark — the default theme — and fails; secondary is
     7.01:1 at its worst (Dark) and 17.04:1 at its best. */
  color: var(--text-secondary);
  transition: transform .24s ease, color .15s ease;
}
.card-collapse-toggle:hover .card-collapse-chevron { color: var(--text-primary); }
.card.is-open > .card-header .card-collapse-chevron { transform: rotate(90deg); }

/* Collapsed, the header's rule would sit directly on the card's own
   bottom edge and read as a double line. Fading the COLOUR rather
   than removing the border keeps the header's height identical in
   both states, so opening a card never jumps the rows below it. */
.card.is-collapsible > .card-header {
  border-bottom-color: transparent;
  transition: border-color .24s ease;
}
.card.is-collapsible.is-open > .card-header { border-bottom-color: var(--border); }

/* 0fr -> 1fr animates to the content's natural height, which
   max-height cannot do without hardcoding a guess that clips the
   long cards (Snippets grows with the library). */
.card-collapse {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .24s ease;
}
.card.is-open > .card-collapse { grid-template-rows: 1fr; }
.card-collapse-inner { overflow: hidden; min-height: 0; }

@media (prefers-reduced-motion: reduce) {
  .card-collapse,
  .card-collapse-chevron,
  .card.is-collapsible > .card-header { transition: none; }
}

/* =====================================================
   RAP-220 — UNASSIGNED / ORPHANED LEADS
   =====================================================
   Every colour here is an EXISTING token, deliberately. RAP-179 made the
   palette semantic across seven schemes, and each of these tokens is already
   redefined on the light-polarity selector list; introducing a literal (or a
   new token defined only on :root) would render this banner with dark values
   on Sepia and the two light schemes. Nothing below needs a
   [data-theme] block for the same reason. */

/* RAP-225 — the banner had NO padding, so its text sat flush against the
   rounded border and the button was clipped by `.card { overflow: hidden }`.
   This is the K.E.1.2 class of bug (see the comment above that section): the
   base `.card` deliberately carries zero body padding — only `.card-header`
   is padded — so any card that puts content directly inside itself has to
   supply its own. `1rem 1.25rem` is `.card-header`'s exact padding, which is
   what makes this banner line up with every other card on the page. It is set
   on the section rather than on `.orphan-banner-head` so the padding belongs
   to the card, not to one of its children. */
.orphan-banner {
  border-color: var(--badge-amber-bd);
  background: var(--badge-amber-bg);
  padding: 1rem 1.25rem;
}

.orphan-banner-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* RAP-248 — "Show them" and the dismiss × travel together, so they wrap as a
   unit rather than the × peeling off onto a line of its own. */
.orphan-banner-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

/* RAP-248 — the dismiss control.
   --text-secondary, not --text-muted: same measurement the banner's own
   .muted rule below records — muted is 2.68:1 against this amber tint on
   Dark, and a × that is the only way to close something is a control, so
   WCAG 1.4.11 wants 3:1. Secondary clears it in all seven RAP-179 schemes.
   No hardcoded colour anywhere here, for the same reason.
   2rem square is the touch target, not the glyph size — RAP-200's finding was
   that this app gets used from a phone far more than its desktop-first CSS
   assumed, and a bare × sized to its own text is a ~12px tap. */
.orphan-banner-dismiss {
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm, 4px);
  background: transparent;
  color: var(--text-secondary);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.orphan-banner-dismiss:hover {
  border-color: var(--badge-amber-bd);
  color: var(--text-primary);
}

/* At 375px the heading and the buttons cannot share a row, so they wrap
   underneath. Left-aligning them there keeps them on the same optical edge as
   the text above instead of stranding them against the right border. */
@media (max-width: 480px) {
  .orphan-banner-head { flex-direction: column; align-items: stretch; }
  .orphan-banner-head > .orphan-banner-actions { align-self: flex-start; }
}

.orphan-banner-head p { margin: 0.25rem 0 0; }

/* --text-muted is 2.96:1 on Dark against the plain surface (the RAP-200
   finding) and drops to 2.68:1 once the amber tint is under it. Everything
   in this banner is information someone is meant to act on — the count of
   leads a departed user left behind is the whole point — so it uses
   --text-secondary, which measures 7:1+ on Dark and 4.2:1+ on Light here. */
.orphan-banner .muted { color: var(--text-secondary); }

/* RAP-225 — `.orphan-former-list` / `.orphan-former` / `.orphan-former-name`
   were removed along with the per-departed-user rows they styled. The data and
   the bulk-reassign API they drove are untouched; only the banner rows went. */

/* The chip that replaced the em dash in the Assigned column. Amber rather
   than muted grey: "nobody owns this" is a state to act on, and the muted
   treatment is exactly what let 29 of them sit unread. */
.unassigned-chip {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-sm, 4px);
  font-size: 0.75rem;
  font-weight: 600;
  background: var(--badge-amber-bg);
  /* --text-primary, NOT --badge-amber-fg. Measured, not guessed: the amber
     foreground over the amber tint is 4.36:1 in Light, and at 12px bold this
     is normal text by WCAG (large text starts at 18.66px bold), so it misses
     AA's 4.5:1. The background and border still carry the amber signal; only
     the glyphs move to the high-contrast token. */
  color: var(--text-primary);
  border: 1px solid var(--badge-amber-bd);
  white-space: nowrap;
}

.select-cell { width: 2.25rem; text-align: center; }
.select-cell input { cursor: pointer; }

.orphan-selection-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.75rem 0 0;
  margin-top: 0.75rem;
  border-top: 1px solid var(--border);
}

/* `button.primary { width: 100% }` comes from the login form and is global.
   It stretched "Assign to…" across the whole bar and wrapped "Clear" onto its
   own line. Same override .card-header > button.primary and
   .lead-properties-list already needed — see the RAP-200 notes. */
.orphan-selection-bar > button.primary { width: auto; }

/* The modal's inline validation message. Reuses the danger token so it reads
   as the same kind of "fix this" the rest of the app uses. */
.error-text { color: var(--danger, #f87171); }

/* ═══════════════════════════════════════════════════════════════════════
   RAP-235 — the client hub (/h/:code)
   ═══════════════════════════════════════════════════════════════════════

   Every colour here is a THEME TOKEN, never a literal. That is what makes
   the sepia default and the dark treatment fall out of the same markup:
   theme.js stamps data-theme="sepia" (or "dark") on <html> for client
   routes, and these rules follow it with no client-specific palette of
   their own. A hardcoded hex here would look correct in one treatment and
   wrong in the other — the exact failure RAP-179 spent a phase removing.

   Mobile-first: Mauricio's clients are on phones, and this page is reached
   from a text message. The layout is a single column at every width with a
   max-width for desktop, so there is no breakpoint at which it reflows. */

.hub-shell {
  max-width: 40rem;
  margin: 0 auto;
  padding: 1.25rem 1rem 3rem;
  min-height: 100vh;
  background: var(--bg-0);
  color: var(--text-primary);
}

/* RAP-261 item 6 — the header now holds the controls ALONE; the logo sits
   centered on its own row below (.hub-brand). flex-end keeps the two
   controls where they already were, at the top right. */
.hub-header {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1rem;
  margin-bottom: 0.5rem;
}
.hub-brand-name { font-weight: 700; letter-spacing: 0.01em; }
.hub-controls { display: flex; gap: 0.5rem; }

/* The two controls are the only affordances in the header, so they must
   read as controls: --text-secondary rather than --text-muted, which
   RAP-200 measured at 2.96:1 on Dark and therefore under the 3:1 floor
   WCAG 1.4.11 asks of a non-text control. */
.hub-icon-btn {
  min-width: 2.25rem;
  height: 2.25rem;
  padding: 0 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-2);
  color: var(--text-secondary);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.hub-icon-btn:hover { background: var(--bg-hover); color: var(--text-primary); }

.hub-card {
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 12px);
  padding: 1.25rem;
  margin-bottom: 1.25rem;
  box-shadow: var(--shadow-sm);
}
.hub-greeting { margin: 0 0 0.4rem; font-size: 1.35rem; }
.hub-intro { margin: 0; color: var(--text-secondary); line-height: 1.5; }

/* ── member picker ── */
.hub-members { margin-bottom: 1.25rem; }
.hub-members h3 {
  margin: 0 0 0.6rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-secondary);
}
/* Wraps rather than scrolls — a horizontally scrolling row of names is the
   pattern RAP-200 removed from Settings, and a household can have four
   members with long names at 375px. */
.hub-member-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.hub-member {
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-2);
  color: var(--text-primary);
  font-size: 0.9rem;
  cursor: pointer;
}
.hub-member.is-active {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.hub-member-tag { color: var(--text-muted); font-size: 0.75rem; }
/* RAP-261 item 9 — the "minor" pill inside a member button. --text-secondary
   rather than --text-muted because it is the only thing distinguishing that
   member from every other one: --text-muted is 2.68:1 on Dark over this
   surface (measured for RAP-248's dismiss control), which is under the 3:1
   WCAG 1.4.11 floor for anything load-bearing. */
.hub-member-tag-minor { color: var(--text-secondary); }
/* The line that says WHY the selected member has no checklist. Sits between
   the picker and the income question, both of which it can replace. */
.hub-member-note {
  margin: 0.85rem 0 0;
  color: var(--text-secondary);
  line-height: 1.45;
}
.hub-member-add { border-style: dashed; color: var(--text-secondary); }

.hub-add-form {
  margin-top: 0.85rem;
  display: grid;
  gap: 0.6rem;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 12px);
  padding: 1rem;
}
.hub-add-form label { display: grid; gap: 0.25rem; font-size: 0.85rem; color: var(--text-secondary); }
/* 16px minimum. Below it, iOS zooms the viewport on focus and never zooms
   back out on blur — the RAP-200 finding, and this form is reached from a
   phone by definition. */
.hub-add-form input {
  font-size: 1rem;
  padding: 0.55rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-0);
  color: var(--text-primary);
}
/* RAP-261 item 9 — "Is this person under 18?". Same fieldset reset as the
   staff form: the browser's own fieldset border would draw a box inside a box.
   The two options sit on one row, which fits at 375px because both labels are
   one short word in EN and in ES. */
/* A plain block, NOT a grid. `display: grid` blockifies its children, so the
   two inline-flex options each took a row of their own and "Yes" and "No"
   stacked. They fit on one line at 375px in both languages. */
.hub-add-minor {
  border: none;
  margin: 0;
  padding: 0;
  min-width: 0;
  display: block;
}
.hub-add-minor legend {
  padding: 0;
  margin-bottom: 0.35rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
/* Two class selectors, deliberately: `.hub-add-form label` above is (0,1,1)
   and sets `display: grid`, which would stack each radio above its own word.
   The staff form has the identical collision with `.field label`. */
.hub-add-minor .hub-add-radio {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-right: 1rem;
  font-size: 1rem;
  color: var(--text-primary);
  cursor: pointer;
}
/* The radio itself, not the label, is the tap target on a phone; 1.05rem
   keeps it comfortably over the 44px row height the label already provides. */
.hub-add-minor .hub-add-radio input {
  margin: 0; width: 1.05rem; height: 1.05rem; cursor: pointer;
}
.hub-add-actions { display: flex; align-items: center; gap: 0.75rem; }
.hub-add-error { margin: 0; color: var(--danger); font-size: 0.85rem; }
.hub-link-btn {
  background: none; border: none; padding: 0;
  color: var(--text-secondary); text-decoration: underline; cursor: pointer;
  font-size: 0.9rem;
}

/* ── the checklist ── */
.hub-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.85rem; }
.hub-step {
  display: flex;
  gap: 0.85rem;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 12px);
  padding: 1rem;
}
/* A locked step is DIMMED, never hidden: seeing the whole path is what makes
   this a hub rather than three links. Opacity only, so every scheme keeps its
   own contrast relationships rather than being overridden by a fixed grey. */
.hub-step-locked { opacity: 0.6; }
.hub-step-done { border-color: var(--success); }
.hub-step-num { font-size: 1.5rem; line-height: 1.2; }
.hub-step-body { flex: 1; min-width: 0; }
.hub-step-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem; margin-bottom: 0.3rem;
}
.hub-step-head h2 { margin: 0; font-size: 1.02rem; }
.hub-step-desc { margin: 0; color: var(--text-secondary); font-size: 0.92rem; line-height: 1.45; }
.hub-step-note { margin: 0.45rem 0 0; color: var(--text-muted); font-size: 0.83rem; }

.hub-chip {
  flex: none;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  background: var(--bg-3);
}
.hub-chip-done { color: var(--success); border-color: var(--success); }
.hub-chip-open { color: var(--accent); border-color: var(--accent); }

/* The one filled control on the page, so it uses --on-accent rather than a
   hardcoded white. RAP-179 added that token precisely because four controls
   filled with var(--accent) hardcoded their label white and were therefore
   theme-BLIND — unreadable in any scheme with a light accent. */
.hub-step-action {
  display: inline-block;
  margin-top: 0.75rem;
  padding: 0.5rem 1.1rem;
  border: none;
  border-radius: var(--radius-sm, 8px);
  background: var(--accent);
  color: var(--on-accent, #fff);
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
  cursor: pointer;
}
.hub-step-action:hover { background: var(--accent-hover); }

.hub-footer {
  margin: 1.75rem 0 0;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.85rem;
}
.hub-loading { text-align: center; color: var(--text-muted); padding: 3rem 0; }
.hub-empty { text-align: center; }
.hub-empty h1 { font-size: 1.15rem; margin: 0 0 0.5rem; }
.hub-empty p { margin: 0; color: var(--text-secondary); line-height: 1.5; }

/* RAP-235 UX corrections (Mauricio, Aug 18) — review panel, prompt, greeting.
   The logo half of that pass was superseded by RAP-261 item 6, below. */

/* RAP-261 item 6 — the centered wordmark, matching /upload/:token. The width
   cap is smaller than the uploader's 280px because the hub greeting sits
   directly under it: at 375px a 280px logo pushed "Hi Joshua, let's find your
   next home." to the fold, and the greeting is the thing the client came for.
   .rr-logo already supplies max-width/height/--logo-filter. */
.hub-brand {
  display: flex;
  justify-content: center;
  margin-bottom: 1rem;
  color: var(--text-primary);
}
.hub-brand .hub-logo { width: 100%; max-width: 220px; }

.hub-logo { display: block; }
.hub-greeting {
  margin: 0 0 1.5rem;
  font-size: 1.3rem;
  line-height: 1.35;
  font-weight: 650;
}
/* RAP-261 item 4 — the income-type picker, under the member row inside
   Step 2. It reuses the member pill's shape so the two read as one question
   each rather than as two different kinds of control. */
.hub-income-block { margin-top: 1rem; }
.hub-income-block h3 {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0 0 0.5rem;
}
.hub-income-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.hub-income {
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-2);
  color: var(--text-secondary);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}
.hub-income:hover { color: var(--text-primary); background: var(--bg-hover); }
.hub-income.is-active {
  border-color: var(--accent);
  color: var(--text-primary);
  background: var(--bg-hover);
}
/* The fixed state is not a disabled button: it is not a control at all, so it
   carries no pointer, no hover and no pressed state. */
.hub-income-fixed { cursor: default; color: var(--text-primary); }
.hub-income-fixed:hover { background: var(--bg-2); }

/* The member picker now lives INSIDE Step 2, so it indents under the step
   body rather than sitting as a page-level section. */
.hub-step .hub-members { margin: 1rem 0 0; }

/* ── RAP-235 Phase 4 — the OneHome intake, rendered inside Step 1 ─────────
   Every colour is a token, so all seven RAP-179 schemes and the client sepia
   default work with no per-scheme rule. The 16px minimum on inputs is RAP-200's
   rule and matters more here than anywhere else in the app: this is the longest
   form a client ever fills in, on a phone, and iOS never zooms back out. */
.hub-intake { margin-top: 1rem; }
.hub-intake-sub {
  margin: 0 0 1rem;
  color: var(--text-secondary);
}
.hub-intake-legend {
  margin: 1.25rem 0 0.6rem;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.hub-intake-legend:first-child { margin-top: 0; }
.hub-intake-row {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.hub-intake-row > .hub-intake-field { flex: 1 1 160px; }
.hub-intake-field {
  display: block;
  margin-bottom: 0.85rem;
}
.hub-intake-field > span {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.hub-intake-field input,
.hub-intake-field select {
  width: 100%;
  padding: 0.65rem 0.75rem;
  font-size: 1rem;              /* 16px — see the block comment above */
  color: var(--text-primary);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.hub-intake-field input:focus,
.hub-intake-field select:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.hub-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.hub-chips:not(:empty) { margin-bottom: 0.45rem; }
.hub-chip-city {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.85rem;
  color: var(--text-primary);
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: 999px;
}
/* An unmatched city is FLAGGED, never rejected — RAP-72's rule. So it borrows
   the AMBER badge tokens (the same family RAP-220's unassigned banner uses),
   not the danger ones: "we'll check this" rather than "this is invalid".
   `--warning` deliberately not used — no such token exists in any of the seven
   schemes, so `var(--warning, #hex)` would be a hardcoded colour wearing a
   token's clothes, which is what the hub CSS assertion exists to catch. */
.hub-chip-city.is-unmatched {
  border-color: var(--badge-amber-bd);
  background: var(--badge-amber-bg);
  color: var(--text-primary);
}
.hub-chip-city button {
  padding: 0;
  width: 1.1rem;
  height: 1.1rem;
  line-height: 1;
  color: var(--text-secondary);
  background: none;
  border: 0;
  cursor: pointer;
  font-size: 1rem;
}
.hub-city-suggest {
  display: flex;
  flex-direction: column;
  margin-top: 0.3rem;
  overflow: hidden;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.hub-city-option {
  padding: 0.55rem 0.75rem;
  text-align: left;
  font-size: 0.95rem;
  color: var(--text-primary);
  background: none;
  border: 0;
  cursor: pointer;
}
.hub-city-option:hover { background: var(--bg-3); }
.hub-intake-warn,
.hub-intake-error {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.hub-intake-error { color: var(--danger); }
.hub-intake-submit {
  width: 100%;
  margin-top: 1rem;
  padding: 0.8rem 1rem;
  font-size: 1rem;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--accent);
  border: 0;
  border-radius: 10px;
  cursor: pointer;
}
.hub-intake-submit:hover { background: var(--accent-hover); }
.hub-intake-submit:disabled { opacity: 0.6; cursor: default; }
.hub-step .hub-members h3 { font-size: 0.82rem; }

.hub-prompt { border-style: dashed; }
.hub-prompt p { margin: 0 0 0.85rem; color: var(--text-secondary); line-height: 1.45; }

/* ── the install prompt (RAP-235 item 9) ──────────────────────────────────
   Dashed like .hub-prompt, because both are the same KIND of thing: an aside
   the client may ignore, visually distinct from the three steps, which are the
   job. Every colour is a token, so all seven RAP-179 schemes keep their own
   contrast rather than being overridden by a fixed value. */
.hub-install { border-style: dashed; }
.hub-install-banner {
  margin: 0 0 0.35rem;
  color: var(--text-primary);
  font-weight: 600;
  line-height: 1.4;
}
/* The iOS steps are secondary to the banner they explain, but they are the
   ACTIONABLE half on that platform, so they stay at body size rather than
   shrinking to a caption. */
.hub-install-steps {
  margin: 0 0 0.85rem;
  color: var(--text-secondary);
  line-height: 1.45;
}
/* Both trailing controls are links; space them off whatever sits above and
   from each other, so "Turn on notifications" and "Skip" never collide on a
   narrow phone. */
/* RAP-261 item 8 — the six-step iPhone walkthrough.
   A heading and an ordered list rather than one paragraph, because the client
   reading it is holding the phone and looking for the step they are on. The
   list keeps its own numbers (list-style stays default) for the same reason:
   they are what "step 4" refers to. */
.hub-install-title {
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-primary);
}
.hub-install-list {
  margin: 0;
  padding-left: 1.25rem;
  color: var(--text-secondary);
  line-height: 1.5;
}
.hub-install-list li { margin-bottom: 0.3rem; }
.hub-install .hub-link-btn { display: block; margin-top: 0.85rem; }
.hub-install .hub-step-action + .hub-link-btn { margin-top: 1rem; }

/* ── whose phone is this (RAP-261 item 10) ────────────────────────────────
   The status line REPLACES the prompt on a device that already holds a
   subscription, so it sits where that link sat and keeps its top margin. It is
   a statement rather than a control, so it is body text in --text-secondary
   (the same weight as .hub-install-steps) and is deliberately not underlined:
   an underlined line that does nothing when tapped is the "reads as broken"
   this item is about, wearing different clothes. */
.hub-push-status {
  margin: 0.85rem 0 0;
  color: var(--text-secondary);
  line-height: 1.45;
}
/* The question and its name pills. The pills are .hub-member, the same control
   the document picker uses, because they are the same KIND of thing (pick a
   household member) and a second pill style would read as a different
   mechanism. None of them is `is-active`: nothing is preselected, for the same
   reason the income picker preselects nothing — an answer already filled in
   invites a tap that confirms a guess nobody made. */
.hub-whose-phone { margin-top: 0.85rem; }
.hub-whose-phone h3 {
  margin: 0 0 0.6rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-primary);
}

/* ── "What's new" (RAP-235 close-out) ─────────────────────────────────────
   A solid card, unlike .hub-prompt / .hub-install, which are dashed because
   they are asides the client may ignore. This one is a record of what we told
   them, so it reads as content. Every colour is a token, so all seven RAP-179
   schemes keep their own contrast. */
.hub-feed-title {
  margin: 0 0 0.85rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-secondary);
}
.hub-feed-list { list-style: none; margin: 0; padding: 0; }
.hub-feed-item {
  display: flex;
  gap: 0.6rem;
  padding: 0.7rem 0;
  border-top: 1px solid var(--border);
}
.hub-feed-item:first-child { border-top: none; padding-top: 0; }
.hub-feed-item:last-child { padding-bottom: 0; }
/* The unread marker. Decorative only (aria-hidden in the markup): items are
   newest-first, and a "New" label would be client-facing copy nobody approved.
   It keeps its 0.55rem box when read so the two states line up on the same
   left edge — a marker that collapsed would reflow the whole list as items
   are read. */
.hub-feed-dot {
  flex: 0 0 auto;
  width: 0.55rem; height: 0.55rem;
  margin-top: 0.42rem;
  border-radius: 50%;
  background: transparent;
}
/* --accent (#b22222) measures 2.76:1 on the hub's dark card, under the 3:1
   WCAG 1.4.11 floor for a graphical object — the same family of finding as
   accent-as-text failing AA on the dark schemes. --badge-coral-fg is the
   polarity-aware token the app already uses for red badges and clears the
   floor on both client palettes. */
.hub-feed-item.is-new .hub-feed-dot { background: var(--badge-coral-fg); }
.hub-feed-body { min-width: 0; }
.hub-feed-text {
  margin: 0;
  color: var(--text-primary);
  line-height: 1.45;
  /* The approved bodies carry a portal URL, which has no spaces and would
     otherwise push the card wider than a 375px phone. */
  overflow-wrap: anywhere;
}
/* --text-secondary rather than --text-muted: muted measures 2.96:1 on the hub's
   dark card (the same number RAP-200 recorded for it), which is under AA for
   body text. This is a client-facing surface, so it takes the token that
   passes. */
.hub-feed-when {
  margin: 0.3rem 0 0;
  font-size: 0.8rem;
  color: var(--text-secondary);
}
/* --accent as TEXT fails AA on the dark schemes (see CLAUDE.md's RAP-179 note
   and the accent-fails-dark-schemes finding), so the link is body text with an
   underline and takes the accent only on hover, where it is a state change
   rather than the only signal. */
.hub-feed-link { color: var(--text-primary); text-decoration: underline; }
.hub-feed-link:hover { color: var(--accent); }

/* Read-only criteria panel. `dialog` semantics without a <dialog>, because the
   shell has no modal infrastructure and this must work on an old iOS webview. */
.hub-review {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.45);
}
.hub-review-panel {
  width: 100%; max-width: 26rem; max-height: 85vh; overflow-y: auto;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 12px);
  padding: 1.25rem;
  box-shadow: var(--shadow-lg);
}
.hub-review-panel h2 { margin: 0 0 1rem; font-size: 1.05rem; }
.hub-criteria { margin: 0 0 1rem; }
.hub-criteria-row {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}
.hub-criteria-row dt { color: var(--text-secondary); font-size: 0.88rem; }
.hub-criteria-row dd { margin: 0; font-weight: 600; text-align: right; }
.hub-review-note {
  margin: 0 0 1rem; color: var(--text-muted);
  font-size: 0.85rem; line-height: 1.45;
}

/* RAP-235 — staff hub-stage controls + document reassign. */
.hub-stages-body { padding: 1rem 1.25rem; }
.hub-stage-list { list-style: none; margin: 0.75rem 0 0; padding: 0; display: grid; gap: 0.75rem; }
.hub-stage-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.35rem 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border);
}
.hub-stage-row:last-child { border-bottom: none; padding-bottom: 0; }
.hub-stage-main { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.hub-stage-name { font-weight: 600; font-size: 0.92rem; }
.hub-stage-meta { grid-column: 1 / -1; }
.hub-stage-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; justify-self: end; }
/* Three states, three readings. "Unlocked by staff" must not look like
   "Unlocked" — the difference is the whole reason the override is a table. */
.chip.chip-hub-auto   { color: var(--success); border-color: var(--success); }
.chip.chip-hub-manual { color: var(--accent);  border-color: var(--accent); }
.chip.chip-hub-locked { color: var(--text-secondary); }
.doc-slot-reassign {
  background: none; border: none; cursor: pointer; padding: 0.15rem 0.3rem;
  color: var(--text-secondary); font-size: 0.95rem; line-height: 1;
}
.doc-slot-reassign:hover { color: var(--text-primary); }

/* RAP-235 — Settings: required-document lists per income type. */
.doc-req-body { padding: 1rem 1.25rem; }
.doc-req-type { margin-top: 1.25rem; }
.doc-req-type:first-child { margin-top: 0.5rem; }
.doc-req-type h3 {
  margin: 0 0 0.5rem; font-size: 0.95rem;
  display: flex; align-items: baseline; gap: 0.5rem;
}
/* Wraps rather than scrolls — eleven slots at 375px cannot share a row, and a
   horizontally scrolling control list is the pattern RAP-200 removed. */
.doc-req-slots { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; }
.doc-req-slot {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.85rem; color: var(--text-secondary); cursor: pointer;
}
.doc-req-slot input { margin: 0; cursor: pointer; }
.doc-req-actions {
  margin-top: 0.6rem; display: flex; align-items: center; gap: 0.75rem;
}
