/* =============================================================================
   Segmic CRM — Design System v2 (Analytica CRM until 2026-09-11)
   -----------------------------------------------------------------------------
   A single, self-contained stylesheet. No external fonts, no CDN, no build step.
   Organised as: tokens → base → primitives → shell → layout → data → overlays →
   page modules → states → responsive → print/motion.

   Rules that hold everywhere in here:
   · Every colour is a token. Never a raw hex outside the token blocks.
   · Every surface works in light AND dark. Theme is user-selectable (see [data-theme]).
   · Spacing comes off a 4px grid; type comes off a 9-step scale.
   · Nothing may clip text or overlap: flex/grid children get `min-width:0`,
     long strings get `overflow-wrap`, wide content scrolls inside its own box.
   ========================================================================== */

/* =============================================================================
   01 · TOKENS
   ========================================================================== */
:root{
  /* — neutral ramp (light) — */
  --n0:#ffffff;  --n25:#fcfdfe; --n50:#f7f9fb; --n100:#eff2f6; --n200:#e4e8ef;
  /* --n500 is --muted, i.e. every .muted and .hint in the product: 158 var() sites and 467
     class="...muted..." uses in app.js. At #78839a it measured 3.81:1 on --surface and 3.61:1 on
     --surface-2 -- under the 4.5:1 AA floor for body text, app-wide. #68738a is the same hue ~6%
     darker: 4.76:1 and 4.51:1. Dark mode was already fine (5.97:1) and keeps its own value below. */
  --n300:#d3d9e3; --n400:#a8b1c1; --n500:#646e84; --n600:#586274; --n700:#3f4859;
  --n800:#28303f; --n900:#161c28; --n950:#0b0f17;

  /* — brand — */
  /* The Segmic Engineering logo (2026-09-11): the mark's bright blue is --brand-400, the wordmark's
     navy is --brand-800/900. The ACCENT is a darker blue than the mark on purpose — the mark's own
     #00a3e0 is 2.9:1 on white, well under the 4.5:1 that button labels and link text need, so the
     mark's blue is for graphics and gradients and --brand-500 (5.1:1) carries the text. */
  --brand-50:#eaf6fc; --brand-100:#d2ecf8; --brand-200:#a8daf2; --brand-300:#5fc0ea;
  --brand-400:#00a3e0; --brand-500:#0b74ad; --brand-600:#0a6a9e; --brand-700:#0f4f75;
  --brand-800:#0d3349; --brand-900:#0b2b3b;

  /* — semantic surfaces — */
  --page:var(--n50);
  --surface:var(--n0);          /* cards, panels, menus */
  --surface-2:var(--n50);       /* inset wells, table heads, subtle fills */
  --surface-3:var(--n100);      /* hover fills, tracks */
  --panel:var(--n0);            /* legacy alias — same as --surface */
  --sidebar-bg:color-mix(in srgb, var(--n0) 78%, transparent);
  --topbar-bg:color-mix(in srgb, var(--n0) 82%, transparent);
  --scrim:rgba(11,15,23,.42);

  /* — semantic ink — */
  --ink:var(--n900);            /* primary text */
  --ink2:var(--n700);           /* secondary text */
  --muted:var(--n500);          /* tertiary / meta */
  --ink-invert:#ffffff;

  /* — lines — */
  --line:var(--n200);
  --line-2:var(--n300);         /* stronger divider / input border */
  --line-soft:color-mix(in srgb, var(--n200) 60%, transparent);

  /* — accent — */
  --accent:var(--brand-500);
  --accent-dk:var(--brand-600);
  --accent-soft:var(--brand-50);
  --accent-soft-2:var(--brand-100);
  --tint-ink:var(--brand-700);
  --accent-ring:color-mix(in srgb, var(--accent) 26%, transparent);

  /* — status — */
  /* WCAG pass, measured (2026-09-10): each status ink is the LIGHTEST shade of its hue that clears
     4.5:1 on its own soft fill AND on --surface / --surface-2, because these colour 12px chip text
     (not "large text", so 3:1 does not apply). Before: good 3.03, warning 3.15, serious 3.39,
     critical 4.22 on their soft fills. After: 4.60 / 4.56 / 4.56 / 4.53. Dark mode was already
     above 5.9 everywhere and keeps its own values below. --brand-500 and --n500 moved by the same
     method: accent on --surface-2 was 4.43, muted on --surface-3 was 4.24; both now ≥ 4.55. */
  --good:#0e7f3f;   --good-soft:#e6f7ee;
  --warning:#a16000; --warning-soft:#fdf3e2;
  --serious:#bc4700; --serious-soft:#fdeee3;
  --critical:#d02b1f; --critical-soft:#fdecea;
  /* Lead-status tones. Their own tokens rather than borrowed semantic ones: "attempted" is orange
     because it is the second step of the pipeline, not because it is a warning, and won must stay
     the strongest green while contacted sits visibly lighter beneath it. */
  /* Measured as .status-select actually renders them -- color-mix(dot 78%, ink) on
     color-mix(dot 14%, surface), at 12.5px/620, which is NOT WCAG "large text" so 4.5:1 applies.
     #c9a100 rendered 3.33:1 and #4caf50 rendered 3.66:1. Darkened along the same hue until the
     rendered pair clears the floor: 4.61:1 and 4.53:1. --st-attempt already passed at 4.53:1. */
  --st-new:#a08000;      --st-new-soft:#fbf5d9;
  --st-attempt:#e05500;  --st-attempt-soft:#fdeee3;
  --st-contacted:#419645; --st-contacted-soft:#e9f7ea;
  --info:var(--accent); --info-soft:var(--accent-soft);

  /* — sequential ramp (magnitude only, single hue) — */
  /* single-hue magnitude ramp, on the brand teal so pipeline bars and the accent agree */
  --seq-1:#d8e9ef; --seq-2:#b3d3de; --seq-3:#8cbccb; --seq-4:#5fa3b8;
  --seq-5:#2f7d95; --seq-6:#26647a; --seq-7:#1e4f60;

  /* — elevation — */
  --shadow-1:0 1px 2px rgba(16,24,40,.05), 0 1px 3px rgba(16,24,40,.05);
  --shadow-2:0 2px 4px rgba(16,24,40,.05), 0 8px 20px -6px rgba(16,24,40,.12);
  --shadow-3:0 12px 24px -8px rgba(16,24,40,.16), 0 32px 64px -16px rgba(16,24,40,.24);
  --shadow-inset:inset 0 1px 0 rgba(255,255,255,.5);
  --ring-focus:0 0 0 3px var(--accent-ring);

  /* — radius — */
  --r-xs:6px; --r-sm:8px; --r-md:11px; --r-lg:15px; --r-xl:20px; --r-full:999px;
  --radius-sm:var(--r-sm); --radius-md:var(--r-md); --radius-lg:var(--r-lg); /* legacy aliases */

  /* — spacing (4px grid) — */
  /* Compact pass (2026-08-15): the top steps were carrying most of the page's excess whitespace,
     so they came down a notch while the small steps (control padding) stayed put. Data density up,
     tap targets untouched. */
  --s1:4px; --s2:8px; --s3:12px; --s4:14px; --s5:18px; --s6:22px;
  --s7:26px; --s8:34px; --s9:42px; --s10:56px;

  /* — type scale — */
  --fs-2xs:10.5px; --fs-xs:11.5px; --fs-sm:12.5px; --fs-base:13px; --fs-md:14px;
  --fs-lg:15.5px; --fs-xl:18px; --fs-2xl:22px; --fs-3xl:27px;
  --fs-label:12.5px; --fs-hint:12px;  /* legacy aliases */
  --lh-tight:1.25; --lh-snug:1.38; --lh:1.5;

  /* — motion — */
  --t-fast:110ms; --t:170ms; --t-slow:260ms;
  --ease:cubic-bezier(.2,.8,.2,1);
  --ease-spring:cubic-bezier(.34,1.28,.64,1);

  /* — shell metrics — */
  --topbar-h:52px;
  --side-w:236px;
  --rail-w:64px;
  --content-max:1440px;

  /* — z-index scale — */
  --z-sticky:20; --z-side:45; --z-topbar:50; --z-pop:60; --z-scrim:55;
  --z-dialog:70; --z-toast:80; --z-progress:90;

  color-scheme:light;
}

/* Dark tokens. Applied by OS preference unless the user has pinned light, and
   always when the user has pinned dark — so the in-app theme switcher wins over
   the OS in both directions. The ramp is written twice (media query + attribute
   selector) because CSS has no way to alias one custom-property block to two
   selectors without a preprocessor, and this file stays build-step-free. */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .nav-group[data-group="marketing"]{--grp:#a78bfa}
  :root:not([data-theme="light"]) .nav-group[data-group="sources"]{--grp:#2dd4bf}
  :root:not([data-theme="light"]){
    --n0:#0e1219;  --n25:#11161f; --n50:#141a24; --n100:#1a212d; --n200:#252e3c;
    --n300:#333e4f; --n400:#5d6a7e; --n500:#8b96a8; --n600:#a8b2c2; --n700:#c3cbd7;
    --n800:#dde2ea; --n900:#eef1f6; --n950:#f8fafc;

    --page:#0a0e15;
    --surface:#12181f;
    --surface-2:#161d26;
    --surface-3:#1d2531;
    --panel:#12181f;
    --sidebar-bg:color-mix(in srgb, #0d131a 76%, transparent);
    --topbar-bg:color-mix(in srgb, #0d131a 82%, transparent);
    --scrim:rgba(0,0,0,.6);

    --ink:#eef1f6; --ink2:#c3cbd7; --muted:#8b96a8; --ink-invert:#0a0e15;

    --line:#232c3a; --line-2:#2f3a4a;
    --line-soft:color-mix(in srgb, #232c3a 70%, transparent);

    --accent:#4dbcec; --accent-dk:#7fd0f3;   /* the logo blue, lightened for dark-surface contrast (7.9:1 on the page) */
    --accent-soft:#0e2a3a; --accent-soft-2:#123a4f;
    --tint-ink:#a6ddf6;
    --accent-ring:color-mix(in srgb, var(--accent) 34%, transparent);

    --good:#3ecf8e;   --good-soft:#10291f;
    --warning:#f2b544; --warning-soft:#2b2312;
    --serious:#fb8b4c; --serious-soft:#2d1d13;
    --critical:#ff6b5e; --critical-soft:#31171a;
    --st-new:#f2cf4a;      --st-new-soft:#2b2712;
    --st-attempt:#fb8b4c;  --st-attempt-soft:#2d1d13;
    --st-contacted:#6fcf7f; --st-contacted-soft:#132a18;
    --info:var(--accent); --info-soft:var(--accent-soft);

    --seq-1:#12303a; --seq-2:#17414f; --seq-3:#1f5a6d; --seq-4:#2b788f;
    --seq-5:#3f9bb4; --seq-6:#63b6cb; --seq-7:#92d0df;

    --shadow-1:0 1px 2px rgba(0,0,0,.4);
    --shadow-2:0 2px 6px rgba(0,0,0,.42), 0 10px 26px -8px rgba(0,0,0,.5);
    --shadow-3:0 18px 40px -10px rgba(0,0,0,.6), 0 40px 80px -20px rgba(0,0,0,.7);
    --shadow-inset:inset 0 1px 0 rgba(255,255,255,.06);

    color-scheme:dark;
  }
}
:root[data-theme="dark"] .nav-group[data-group="marketing"]{--grp:#a78bfa}
:root[data-theme="dark"] .nav-group[data-group="sources"]{--grp:#2dd4bf}
:root[data-theme="dark"]{
  --n0:#0e1219;  --n25:#11161f; --n50:#141a24; --n100:#1a212d; --n200:#252e3c;
  --n300:#333e4f; --n400:#5d6a7e; --n500:#8b96a8; --n600:#a8b2c2; --n700:#c3cbd7;
  --n800:#dde2ea; --n900:#eef1f6; --n950:#f8fafc;

  --page:#0a0e15;
  --surface:#12181f;
  --surface-2:#161d26;
  --surface-3:#1d2531;
  --panel:#12181f;
  --sidebar-bg:color-mix(in srgb, #0d131a 76%, transparent);
  --topbar-bg:color-mix(in srgb, #0d131a 82%, transparent);
  --scrim:rgba(0,0,0,.6);

  --ink:#eef1f6; --ink2:#c3cbd7; --muted:#8b96a8; --ink-invert:#0a0e15;

  --line:#232c3a; --line-2:#2f3a4a;
  --line-soft:color-mix(in srgb, #232c3a 70%, transparent);

  --accent:#4dbcec; --accent-dk:#7fd0f3;   /* the logo blue, lightened for dark-surface contrast (7.9:1 on the page) */
  --accent-soft:#0e2a3a; --accent-soft-2:#123a4f;
  --tint-ink:#a6ddf6;
  --accent-ring:color-mix(in srgb, var(--accent) 34%, transparent);

  --good:#3ecf8e;   --good-soft:#10291f;
  --warning:#f2b544; --warning-soft:#2b2312;
  --serious:#fb8b4c; --serious-soft:#2d1d13;
  --critical:#ff6b5e; --critical-soft:#31171a;
  --st-new:#f2cf4a;      --st-new-soft:#2b2712;
  --st-attempt:#fb8b4c;  --st-attempt-soft:#2d1d13;
  --st-contacted:#6fcf7f; --st-contacted-soft:#132a18;
  --info:var(--accent); --info-soft:var(--accent-soft);

  --seq-1:#12303a; --seq-2:#17414f; --seq-3:#1f5a6d; --seq-4:#2b788f;
  --seq-5:#3f9bb4; --seq-6:#63b6cb; --seq-7:#92d0df;

  --shadow-1:0 1px 2px rgba(0,0,0,.4);
  --shadow-2:0 2px 6px rgba(0,0,0,.42), 0 10px 26px -8px rgba(0,0,0,.5);
  --shadow-3:0 18px 40px -10px rgba(0,0,0,.6), 0 40px 80px -20px rgba(0,0,0,.7);
  --shadow-inset:inset 0 1px 0 rgba(255,255,255,.06);

  color-scheme:dark;
}

/* =============================================================================
   02 · BASE
   ========================================================================== */
*{box-sizing:border-box;margin:0;padding:0}
*,*::before,*::after{min-width:0}
[hidden]{display:none!important}

html{
  scrollbar-width:thin;
  scrollbar-color:color-mix(in srgb, var(--ink) 22%, transparent) transparent;
  -webkit-text-size-adjust:100%;
}
::-webkit-scrollbar{width:12px;height:12px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{
  background-color:color-mix(in srgb, var(--ink) 18%, transparent);
  border-radius:var(--r-full);border:3px solid transparent;background-clip:content-box;
}
::-webkit-scrollbar-thumb:hover{background-color:color-mix(in srgb, var(--ink) 30%, transparent)}
::-webkit-scrollbar-corner{background:transparent}

/* Best available locally-installed UI face, in preference order, then the platform
   default. No webfont is fetched — UIUX.md forbids external assets — so this picks the
   nicest thing already on the machine rather than shipping a binary. */
:root{
  --font-ui:"Inter var","Inter","SF Pro Text",ui-sans-serif,-apple-system,BlinkMacSystemFont,
            "Segoe UI Variable Text","Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
body{
  font:var(--fs-base)/var(--lh) var(--font-ui);
  font-optical-sizing:auto;text-rendering:optimizeLegibility;
  background:var(--page);color:var(--ink);
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-wrap:break-word;
}
h1,h2,h3,h4{font-weight:640;letter-spacing:-.018em;line-height:var(--lh-tight);color:var(--ink)}
/* Numbers that get compared down a column must not wobble: lining tabular figures
   everywhere a figure appears in a table, tile, meter or money value. */
table,.tile .n,.tk-n,.coll-total,.bar-row .val,.score,.cm-v,.board-col-count,.board-col-sum,
.nav-badge,.notif-pop-count,.cell-age,.target-kpis,input[type=number]{
  font-variant-numeric:tabular-nums lining-nums;
  font-feature-settings:"tnum" 1,"lnum" 1;
}
h1{font-size:var(--fs-2xl);letter-spacing:-.028em}
h2{font-size:var(--fs-xl);letter-spacing:-.024em}
h3{font-size:var(--fs-md);letter-spacing:-.014em}
h4{font-size:var(--fs-base);letter-spacing:-.008em}
p{line-height:var(--lh)}
a{color:var(--accent);text-underline-offset:2px;transition:color var(--t-fast) var(--ease)}
a:hover{color:var(--accent-dk)}
code,kbd,samp,.mono{font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace}
::selection{background:color-mix(in srgb, var(--accent) 26%, transparent)}
:where(img,svg,video){max-width:100%}

/* one focus treatment for the whole app */
:where(button,a,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:2px solid var(--accent);outline-offset:2px;box-shadow:var(--ring-focus);border-radius:var(--r-xs);
}

/* — text helpers — */
.muted{color:var(--muted);font-size:var(--fs-sm)}
.mono{font-size:var(--fs-hint);word-break:break-word;overflow-wrap:anywhere}
.err{color:var(--critical);font-size:var(--fs-base);margin-top:var(--s2);min-height:18px}
.hint{font-size:var(--fs-hint);color:var(--muted);line-height:var(--lh-snug)}
/* A hint inside a <label> inherited the label's 520 weight, so the same sentence read heavier there
   than the identical <p class="hint"> two rows below it — 27 of them across the app, and the two
   sitting side by side on My profile is where it shows. One weight for a hint, wherever it sits. */
label>.hint{font-weight:400}
.nowrap{white-space:nowrap}
.warn-line{
  display:flex;align-items:flex-start;gap:var(--s2);
  color:var(--ink2);font-weight:520;font-size:var(--fs-base);margin:var(--s2) 0;
  background:var(--serious-soft);border:1px solid color-mix(in srgb, var(--serious) 26%, transparent);
  border-radius:var(--r-sm);padding:var(--s2) var(--s3);
}

/* =============================================================================
   03 · BUTTONS
   ========================================================================== */
button,a.ghost,a.small,.btn{
  --btn-bg:var(--accent); --btn-ink:#fff;
  font:inherit;font-size:var(--fs-base);font-weight:560;line-height:1.2;
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);
  cursor:pointer;border:1px solid transparent;border-radius:var(--r-sm);
  padding:7px 14px;min-height:34px;
  background:var(--btn-bg);color:var(--btn-ink);
  box-shadow:var(--shadow-1);
  text-decoration:none;white-space:nowrap;
  transition:background var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease),
             color var(--t-fast) var(--ease),
             box-shadow var(--t-fast) var(--ease),transform var(--t-fast) var(--ease),opacity var(--t-fast);
}
button:hover,a.ghost:hover,a.small:hover,.btn:hover{background:var(--accent-dk)}
button:active,a.ghost:active,a.small:active,.btn:active{transform:translateY(.5px) scale(.985)}
button:disabled,.btn:disabled{opacity:.5;cursor:not-allowed;transform:none}

button.ghost,a.ghost,.btn.ghost{
  background:var(--surface);color:var(--ink2);border-color:var(--line-2);box-shadow:var(--shadow-1);font-weight:520;
}
button.ghost:hover,a.ghost:hover,.btn.ghost:hover{background:var(--surface-3);color:var(--ink);border-color:var(--line-2)}

button.subtle{background:transparent;color:var(--ink2);border-color:transparent;box-shadow:none;font-weight:520}
button.subtle:hover{background:color-mix(in srgb, var(--ink) 7%, transparent);color:var(--ink)}

button.small,a.small,.btn.small{padding:4px 10px;min-height:28px;font-size:var(--fs-sm);border-radius:var(--r-xs)}
button.tiny{padding:2px 8px;min-height:24px;font-size:var(--fs-xs);border-radius:var(--r-xs)}

button.danger,.btn.danger{background:transparent;color:var(--critical);border-color:color-mix(in srgb, var(--critical) 40%, var(--line));box-shadow:none;font-weight:520}
button.danger:hover{background:var(--critical-soft);border-color:var(--critical);color:var(--critical)}
button.small.ghost.danger{color:var(--critical);background:var(--surface)}
button.small.ghost.danger:hover{background:var(--critical-soft)}

button.icon-btn,.icon-btn{
  min-width:32px;padding:0 var(--s2);text-align:center;font-weight:600;font-size:var(--fs-md);line-height:1;
}
.icon-btn-dots{font-size:18px;letter-spacing:.5px}

/* =============================================================================
   04 · FORM CONTROLS
   ========================================================================== */
label{display:block;font-size:var(--fs-label);font-weight:520;color:var(--ink2);margin-bottom:var(--s3)}
label input,label select,label textarea{margin-top:5px}
label.inline{margin:0;display:flex;flex-direction:column;gap:2px;font-size:var(--fs-xs);color:var(--muted)}
label.inline select,label.inline input{width:auto;min-width:130px;margin-top:0}
label.ac-anchor{position:relative}

input,select,textarea{
  /* font:inherit pulls the wrapping label's 520 weight into the field, so everything you TYPED
     rendered semi-bold. What you type is content, not a heading — normal weight, everywhere. */
  font:inherit;font-weight:400;font-size:var(--fs-base);color:var(--ink);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
  padding:7px 11px;width:100%;min-height:34px;
  transition:border-color var(--t-fast) var(--ease),box-shadow var(--t-fast) var(--ease),background var(--t-fast);
}
input::placeholder,textarea::placeholder{color:var(--n400)}
input:hover:not(:disabled),select:hover:not(:disabled),textarea:hover:not(:disabled){border-color:color-mix(in srgb, var(--ink) 24%, var(--line-2))}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:var(--ring-focus)}
input:disabled,select:disabled,textarea:disabled{background:var(--surface-2);color:var(--muted);cursor:not-allowed}
input[readonly]{background:var(--surface-2);color:var(--ink2)}
textarea{min-height:96px;resize:vertical;line-height:var(--lh)}
input[type=search]{appearance:none}
input[type=number]{font-variant-numeric:tabular-nums}
input[type=range]{width:100%;accent-color:var(--accent);padding:0;min-height:0;border:0;background:transparent}
input[type=color]{padding:2px;min-height:34px;cursor:pointer}
input[type=file]{padding:6px 10px;background:var(--surface-2)}

select{
  appearance:none;-webkit-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2378839a' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 9px center;background-size:14px;padding-right:30px;
}
input[type=checkbox],input[type=radio]{
  /* min-width + flex-shrink:0 stop a checkbox being squeezed below its own size
     when it sits in a tight table header or flex row (the Leads select-all was 13px). */
  width:16px;height:16px;min-width:16px;min-height:16px;flex-shrink:0;
  accent-color:var(--accent);cursor:pointer;padding:0;
  transition:transform var(--t-fast) var(--ease-spring);
}
input[type=checkbox]:active,input[type=radio]:active{transform:scale(.9)}

.formgrid{display:grid;grid-template-columns:1fr 1fr;gap:0 var(--s4)}
.formgrid .full{grid-column:1/-1}
.q-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4)}
.chk{display:flex;align-items:center;gap:var(--s2);font-size:var(--fs-base);color:var(--ink);margin:var(--s2) 0;cursor:pointer}
.chk input{width:auto;margin:0;flex-shrink:0}
.chkrow{display:flex;flex-wrap:wrap;gap:var(--s1) var(--s4);margin-top:var(--s1)}
.chkrow .chk{margin:0}

/* password reveal */
/* The eye sits BESIDE the field, not over it (owner, 2026-09-11: "I can't reveal my password in sign-in
   page"). Overlaid on the input's right edge it shared the exact spot Safari and Chrome draw their own
   password / autofill buttons, so the click landed on the browser's control and never reached ours.
   A flex row with its own bordered box cannot be covered by anything the browser paints inside the input. */
.pw-wrap{display:flex;align-items:stretch;gap:6px;margin-top:5px}
.pw-wrap input{margin-top:0!important;flex:1;min-width:0;width:auto}
.pw-eye{
  flex:0 0 auto;width:40px;min-height:0;padding:0;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);box-shadow:none;color:var(--muted);
  cursor:pointer;font-size:15px;line-height:1;
  display:inline-flex;align-items:center;justify-content:center}
.pw-eye:hover{color:var(--ink);background:var(--surface-2)}
.pw-eye.on{color:var(--accent)}
/* Two glyphs, one shown at a time: an open eye means "click to reveal", the struck-through eye means
   "it is visible now, click to hide". A single icon cannot say which state you are in. */
.pw-eye svg{width:17px;height:17px;display:block}
.pw-eye .pw-eye-off{display:none}
.pw-eye.on .pw-eye-on{display:none}
.pw-eye.on .pw-eye-off{display:block}

/* autocomplete */
.ac-box{
  position:absolute;top:100%;left:0;right:0;z-index:var(--z-pop);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  margin-top:5px;max-height:240px;overflow-y:auto;box-shadow:var(--shadow-2);padding:var(--s1);
}
.ac-item{padding:7px 10px;font-size:var(--fs-base);color:var(--ink);cursor:pointer;border-radius:var(--r-xs)}
.ac-item:hover,.ac-item.ac-active{background:var(--accent-soft);color:var(--tint-ink)}

/* on/off switch */
.switch{position:relative;display:inline-flex;align-items:center;width:40px;height:23px;cursor:pointer;margin-bottom:0;flex-shrink:0}
.switch input{position:absolute;opacity:0;width:0;height:0}
.switch span{position:absolute;inset:0;background:var(--n300);border-radius:var(--r-full);transition:background var(--t) var(--ease)}
.switch span::before{
  content:"";position:absolute;left:3px;top:3px;width:17px;height:17px;background:#fff;border-radius:50%;
  transition:transform var(--t) var(--ease-spring);box-shadow:0 1px 3px rgba(0,0,0,.3);
}
.switch input:checked+span{background:var(--good)}
.switch input:checked+span::before{transform:translateX(17px)}
.switch input:focus-visible+span{box-shadow:var(--ring-focus)}
.switch-row{display:flex;align-items:center;gap:var(--s3);font-size:var(--fs-base);color:var(--ink);margin:var(--s3) 0;cursor:pointer}

/* segmented controls */
.set-nav,.seg-inline,.net-seg{
  display:inline-flex;gap:2px;padding:3px;background:var(--surface-3);
  border:1px solid var(--line);border-radius:var(--r-md);
}
/* inline-flex, not flex: a section-tab strip hugs its tabs instead of stretching
   the full content width (which read as an empty rail beside the last tab). */
/* Settings uses a grouped vertical rail (see .set-layout) — ten flat tabs in a scrolling
   strip gave no sense of what belonged together. Other pages keep the horizontal strip:
   it stays on ONE line and scrolls if it doesn't fit. */
.set-nav{
  display:flex;flex-wrap:nowrap;margin:0 0 var(--s5);border-radius:var(--r-full);
  max-width:100%;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
}
.set-nav::-webkit-scrollbar{display:none}
.set-nav .set-pill{flex:0 0 auto}
.set-nav{scroll-behavior:smooth}
.seg-inline{border-radius:var(--r-full)}
.set-pill,.net-seg button{
  background:transparent;color:var(--ink2);border:0;border-radius:var(--r-sm);
  padding:6px 15px;font-size:var(--fs-base);font-weight:520;box-shadow:none;min-height:30px;
  transition:background var(--t-fast),color var(--t-fast);
}
.set-nav .set-pill{border-radius:var(--r-full)}
.seg-inline .set-pill{padding:5px 14px;border-radius:var(--r-full)}
.set-pill:hover,.net-seg button:hover{background:color-mix(in srgb, var(--ink) 6%, transparent);color:var(--ink)}
.set-pill.on,.net-seg button.on{background:var(--surface);color:var(--ink);font-weight:620;box-shadow:var(--shadow-1)}
.set-intro{font-size:var(--fs-base);color:var(--ink2);margin:calc(var(--s1) * -1) 0 var(--s4);max-width:78ch}
.set-sec{display:block}

/* multi-select dropdown */
.dd{position:relative;display:inline-block}
.dd summary{
  list-style:none;cursor:pointer;padding:7px 12px;border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--surface);font-size:var(--fs-base);color:var(--ink);user-select:none;white-space:nowrap;
  display:inline-flex;align-items:center;gap:var(--s2);min-height:34px;
}
.dd summary::-webkit-details-marker{display:none}
.dd summary::after{content:"▾";color:var(--muted);font-size:11px}
.dd summary:hover{border-color:color-mix(in srgb, var(--ink) 24%, var(--line-2))}
.dd[open] summary{border-color:var(--accent);box-shadow:var(--ring-focus)}
.dd-panel{
  position:absolute;top:calc(100% + 4px);left:0;z-index:var(--z-pop);min-width:210px;max-height:290px;
  overflow-y:auto;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:var(--shadow-2);padding:var(--s1);
}
.dd-opt{display:flex;align-items:center;gap:var(--s2);padding:6px 9px;margin:0;font-size:var(--fs-base);color:var(--ink);border-radius:var(--r-xs);cursor:pointer;user-select:none}
.dd-opt:hover{background:var(--surface-3)}

/* choice chips */
.sig-chips{display:flex;flex-wrap:wrap;gap:var(--s2)}
.sig-chip{
  position:relative;display:inline-flex;align-items:center;gap:var(--s2);margin:0;padding:6px 14px;
  font-size:var(--fs-sm);font-weight:520;color:var(--ink2);background:var(--surface);
  border:1px solid var(--line-2);border-radius:var(--r-full);cursor:pointer;user-select:none;
  transition:background var(--t-fast),border-color var(--t-fast),color var(--t-fast);
}
.sig-chip input{position:absolute;opacity:0;pointer-events:none}
.sig-chip:hover{border-color:color-mix(in srgb, var(--accent) 50%, var(--line-2));color:var(--ink)}
.sig-chip:has(input:checked){background:var(--accent-soft);border-color:var(--accent);color:var(--tint-ink);font-weight:600}
.sig-chip:has(input:focus-visible){outline:2px solid var(--accent);outline-offset:2px}

/* =============================================================================
   05 · APP SHELL — topbar + sidebar + content
   ========================================================================== */
#app{
  display:grid;
  grid-template-columns:var(--side-w) minmax(0,1fr);
  grid-template-rows:var(--topbar-h) minmax(0,1fr);
  grid-template-areas:"topbar topbar" "side main";
  min-height:100vh;min-height:100dvh;
  transition:grid-template-columns var(--t) var(--ease);
}
#app.rail{--side-w:var(--rail-w)}

/* — topbar — */
.topbar{
  grid-area:topbar;position:sticky;top:0;z-index:var(--z-topbar);
  display:flex;align-items:center;gap:var(--s3);
  padding:0 var(--s4) 0 var(--s3);
  background:var(--topbar-bg);
  backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);
  border-bottom:1px solid var(--line);
}
.tb-brand{
  display:inline-flex;align-items:center;gap:var(--s3);text-decoration:none;color:var(--ink);
  padding:var(--s1) var(--s2);border-radius:var(--r-sm);flex-shrink:0;
}
.tb-brand:hover{background:color-mix(in srgb, var(--ink) 5%, transparent);color:var(--ink)}
/* The square icon beside the logo went on 2026-09-11 (owner: "remove the icon") — the Segmic mark is
   the brand mark now, and two marks side by side read as two brands. */
/* The wordmark and the product name are two lines of one lockup. `line-height:1.05` squeezed them
   together and the PNG was being squashed by the flex row it sat in — `object-fit:contain` and an
   explicit `flex:0 0 auto` keep its aspect ratio whatever the row does, and there is now real space
   between the logo and "CRM" instead of them touching. */
/* The Segmic lockup is two lines tall (mark + SEGMIC / ENGINEERING), not the one-line Analytica
   wordmark these sizes were set for — at 18px it was an unreadable smudge. 26px + a 2px gap + the
   10px caption still sits inside the 52px bar. */
.tb-word{display:flex;flex-direction:column;gap:2px;line-height:1.15;min-width:0}
/* align-self, not just width:auto — .tb-word is a flex COLUMN, so its default stretch was pulling
   the image to the column's width and squashing the wordmark. That was the compression. */
.tb-logo{height:26px;width:auto;max-width:none;flex:0 0 auto;align-self:flex-start;object-fit:contain;display:block}
:root[data-theme="dark"] .tb-logo,:root[data-theme="dark"] .login-logo{filter:brightness(0) invert(1) opacity(.92)}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .tb-logo,:root:not([data-theme="light"]) .login-logo{filter:brightness(0) invert(1) opacity(.92)}
}
.login-logo{height:52px;width:auto;display:block;margin-bottom:var(--s2)}
.sr-title{font-size:var(--fs-xs);font-weight:660;color:var(--accent);letter-spacing:.13em;text-transform:uppercase;margin-bottom:var(--s2)}
.tb-word b{font-size:var(--fs-md);font-weight:660;letter-spacing:-.02em}
.tb-word span{font-size:var(--fs-2xs);font-weight:640;letter-spacing:.13em;text-transform:uppercase;color:var(--muted);line-height:1}

.tb-divider{width:1px;height:24px;background:var(--line);flex-shrink:0}
.tb-spacer{flex:1;min-width:var(--s2)}

.tb-icon{
  background:transparent;color:var(--ink2);border:1px solid transparent;box-shadow:none;
  /* Circles, not squares (owner, 2026-08-25): the user chip beside them is a pill and the bug FAB
     is round, so the square-cornered cluster was the odd one out. Same 34px footprint. */
  width:34px;height:34px;min-height:34px;padding:0;border-radius:var(--r-full);flex-shrink:0;
  display:inline-flex;align-items:center;justify-content:center;position:relative;
}
.tb-icon:hover{background:color-mix(in srgb, var(--ink) 7%, transparent);color:var(--ink)}
.tb-icon svg{display:block}
.tb-icon .nav-badge{position:absolute;margin:0}   /* size and offset: see the bell badge rule below */

kbd{
  font-family:inherit;font-size:var(--fs-2xs);font-weight:600;color:var(--muted);
  background:var(--surface);border:1px solid var(--line-2);border-bottom-width:2px;
  border-radius:5px;padding:1px 5px;line-height:1.5;white-space:nowrap;
}

/* user menu */
.usermenu{position:relative;flex-shrink:0}
.tb-user{
  display:inline-flex;align-items:center;gap:var(--s2);background:transparent;color:var(--ink);
  border:1px solid transparent;box-shadow:none;padding:3px var(--s2) 3px 3px;border-radius:var(--r-full);height:36px;
}
.tb-user:hover{background:color-mix(in srgb, var(--ink) 7%, transparent)}
.tb-user[aria-expanded="true"]{background:color-mix(in srgb, var(--ink) 9%, transparent)}
.avatar{
  width:28px;height:28px;border-radius:50%;flex-shrink:0;
  display:inline-flex;align-items:center;justify-content:center;
  background:linear-gradient(145deg, var(--brand-400), var(--brand-700));
  color:#fff;font-size:var(--fs-xs);font-weight:700;letter-spacing:.02em;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.25);
}
.tb-user .tb-username{font-size:var(--fs-sm);font-weight:560;max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tb-user svg{color:var(--muted);flex-shrink:0}

.pop{
  position:absolute;top:calc(100% + 6px);right:0;z-index:var(--z-pop);min-width:230px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:var(--shadow-3);padding:var(--s1);
  /* The menu grows with the number of remembered accounts, so it has to be able to scroll. Its two
     siblings (.popmenu, .notif-pop-body) already capped themselves; this one overflowed a short
     window with no way to reach Sign out. */
  max-height:min(80vh, 560px);overflow-y:auto;overscroll-behavior:contain;
  animation:popIn var(--t) var(--ease) both;
}
@keyframes popIn{from{opacity:0;transform:translateY(-6px) scale(.98)}to{opacity:1;transform:none}}
.pop-head{padding:var(--s3) var(--s3) var(--s2);border-bottom:1px solid var(--line);margin-bottom:var(--s1)}
.pop-head .welcome{display:block;font-size:var(--fs-base);font-weight:620;color:var(--ink);margin:0}
.pop-head .pop-sub{font-size:var(--fs-hint);color:var(--muted);margin-top:1px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pop-item{
  display:flex;align-items:center;gap:var(--s2);width:100%;text-align:left;
  background:transparent;color:var(--ink2);border:0;box-shadow:none;font-weight:500;
  padding:7px var(--s3);border-radius:var(--r-xs);font-size:var(--fs-base);min-height:32px;justify-content:flex-start;
  text-decoration:none;
}
.pop-item:hover{background:var(--surface-3);color:var(--ink)}
.pop-item svg{color:var(--muted);flex-shrink:0}
/* Sign out is the one destructive item in this menu and now says so on its own terms rather than by
   accident: its red used to survive only because button.danger:hover happened to out-specify
   .pop-item:hover, so reordering two rules would have quietly turned it grey. */
.pop-item.danger,.pop-item.danger:hover{color:var(--critical);border:0;font-weight:620}
.pop-item.danger svg{color:var(--critical)}
.pop-item.danger:hover{background:var(--critical-soft)}
.pop-item.is-busy{opacity:.6;pointer-events:none}
.pop-sep{height:1px;background:var(--line);margin:var(--s1) 0}

/* ---- switch account rows (user menu) ----
   A two-line row: who, and what they are. The avatar is a plain initials disc rather than an icon so
   the eye can tell two remembered accounts apart at a glance. min-height is taller than a plain
   .pop-item because the row carries a subtitle; nothing changes size on hover, per the house rule. */
.switch-acct{min-height:42px;align-items:center}
.switch-av{
  flex:0 0 24px;width:24px;height:24px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-3);color:var(--ink2);
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.02em;
}
.switch-av{transition:background var(--t) var(--ease),color var(--t) var(--ease)}
.switch-acct:hover .switch-av{background:var(--accent);color:#fff}
.switch-who{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.switch-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.switch-sub{font-size:var(--fs-2xs);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.switch-acct.is-busy{opacity:.6;pointer-events:none}

/* ---- calendars card (Meetings) ----
   One row per connected mailbox: who it is on the left, what you can do with it on the right. Wraps
   to stacked on narrow screens rather than letting the buttons squeeze the address to nothing. */
.cal-list{display:flex;flex-direction:column}
.cal-row{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;
  padding:var(--s3) 0;border-top:1px solid var(--line);
}
.cal-row:first-child{border-top:0}
.cal-who{min-width:0;display:flex;flex-direction:column;gap:2px}
.cal-who>strong{display:inline-flex;align-items:center;gap:var(--s2);flex-wrap:wrap}
.cal-acts{display:flex;gap:var(--s2);flex-shrink:0}

/* ---- notification bubble (anchored to the topbar bell) ----
   A "cloud" popover: rounded panel with a pointer aimed at the bell, scaling up
   from that corner so it reads as coming out of the icon rather than appearing
   over it. */
.notif-anchor{position:relative;flex-shrink:0}
.notif-pop{
  position:absolute;top:calc(100% + 11px);right:calc(var(--s2) * -1);z-index:var(--z-pop);
  width:min(392px,calc(100vw - 24px));
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-3);overflow:hidden;
  display:flex;flex-direction:column;max-height:min(560px,70vh);
  transform-origin:top right;
  animation:notifPopIn var(--t-slow) var(--ease-spring) both;
}
@keyframes notifPopIn{
  from{opacity:0;transform:translateY(-10px) scale(.9)}
  60%{opacity:1}
  to{opacity:1;transform:none}
}
/* the pointer — two stacked squares so the 1px border reads continuously */
.notif-pop::before,.notif-pop::after{
  content:"";position:absolute;top:-7px;right:26px;width:12px;height:12px;
  background:var(--surface);transform:rotate(45deg);
}
.notif-pop::before{border:1px solid var(--line);border-right:0;border-bottom:0}
.notif-pop::after{top:-5px}
.notif-pop-head{
  display:flex;align-items:center;gap:var(--s2);padding:var(--s3) var(--s4);
  border-bottom:1px solid var(--line);background:var(--brand-800);color:#fff;flex-shrink:0;
}
.notif-pop-head h4{font-size:var(--fs-md);font-weight:640;flex:1;min-width:0;color:#fff}
.notif-pop-head .muted{color:rgba(255,255,255,.75)}
/* The bubble's pointer is drawn under the head, so it takes the head's colour or the arrow stops
   matching the bubble it points out of. */
.notif-pop::before,.notif-pop::after{background:var(--brand-800)}
.notif-pop-count{
  font-size:var(--fs-2xs);font-weight:700;color:var(--brand-800);background:#fff;
  border-radius:var(--r-full);padding:2px 8px;font-variant-numeric:tabular-nums;
}
.notif-pop-body{overflow-y:auto;padding:var(--s2);display:flex;flex-direction:column;gap:6px;min-height:0}
/* compact variant of .notif-item for inside the bubble */
.notif-pop-body .notif-item{padding:10px var(--s3);border-radius:var(--r-sm);box-shadow:none;gap:var(--s2)}
.notif-pop-body .notif-item:hover{transform:none;background:var(--surface-2)}
.notif-pop-body .notif-text{font-size:var(--fs-sm);line-height:var(--lh-snug)}
.notif-pop-body .notif-go{font-size:var(--fs-xs)}
/* dismissible rows: the ✕ sits beside the link, not inside it */
/* wrap so the per-message jumps drop onto their own line under the row rather than squeezing in
   beside it — the existing two children still fit on one line, so nothing else moves. */
.notif-row{display:flex;align-items:stretch;gap:6px;min-width:0;flex-wrap:wrap}
.notif-row>.notif-item{flex:1;min-width:0}
.notif-row>.notif-acts{flex-basis:100%}
.notif-x{
  flex-shrink:0;width:28px;min-height:0;padding:0;border:1px solid var(--line);
  background:var(--surface);color:var(--muted);border-radius:var(--r-sm);
  font-size:11px;box-shadow:none;
}
.notif-x:hover{background:var(--critical-soft);color:var(--critical);border-color:color-mix(in srgb,var(--critical) 30%,transparent)}
.notif-list .notif-row{margin-bottom:var(--s2)}
.notif-clear{
  background:transparent;border:0;box-shadow:none;color:var(--accent);
  font-size:var(--fs-xs);font-weight:620;padding:3px 8px;min-height:0;flex-shrink:0;
}
.notif-clear:hover{background:var(--accent-soft);color:var(--tint-ink)}
/* less-urgent group — present but folded away */
.notif-minor{margin-top:var(--s2);border-top:1px solid var(--line);padding-top:var(--s2)}
.notif-minor>summary{
  list-style:none;cursor:pointer;font-size:var(--fs-xs);font-weight:620;color:var(--muted);
  display:inline-flex;align-items:center;gap:5px;padding:2px 4px;user-select:none;
}
.notif-minor>summary::-webkit-details-marker{display:none}
.notif-minor>summary::before{content:"▸";transition:transform var(--t) var(--ease)}
.notif-minor[open]>summary::before{transform:rotate(90deg)}
.notif-minor>summary:hover{color:var(--ink2)}
.notif-minor .notif-row{margin-top:6px}
.notif-minor.page{margin-top:var(--s4)}
.notif-pop-empty{
  padding:var(--s7) var(--s4);text-align:center;color:var(--muted);font-size:var(--fs-base);
  display:flex;flex-direction:column;align-items:center;gap:var(--s2);
}
.notif-pop-empty .empty-ic{
  width:40px;height:40px;border-radius:var(--r-md);display:inline-flex;align-items:center;justify-content:center;
  background:var(--good-soft);color:var(--good);
}
.notif-pop-foot{
  padding:var(--s2) var(--s4);border-top:1px solid var(--line);background:var(--surface-2);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);flex-shrink:0;
}
@media (max-width:560px){
  .notif-pop{position:fixed;top:calc(var(--topbar-h) + 8px);right:var(--s3);left:var(--s3);width:auto}
  .notif-pop::before,.notif-pop::after{display:none}
}
.pop-label{font-size:var(--fs-2xs);font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);padding:var(--s2) var(--s3) var(--s1)}

/* — sidebar — */
aside{
  grid-area:side;position:sticky;top:var(--topbar-h);
  height:calc(100vh - var(--topbar-h));height:calc(100dvh - var(--topbar-h));
  display:flex;flex-direction:column;gap:var(--s2);
  padding:var(--s3) var(--s2) var(--s2);
  background:var(--sidebar-bg);
  backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);
  border-right:1px solid var(--line);
  z-index:var(--z-side);
}
.brand{display:none} /* brand now lives in the topbar */

aside nav{display:flex;flex-direction:column;gap:0;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding-bottom:var(--s2)}
aside nav::-webkit-scrollbar{width:8px}
aside nav::-webkit-scrollbar-thumb{background:transparent}
aside nav:hover::-webkit-scrollbar-thumb{background:color-mix(in srgb, var(--ink) 16%, transparent)}

.nav-group{display:flex;flex-direction:column}
.nav-group-h{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);width:100%;
  background:transparent;color:var(--muted);border:0;box-shadow:none;border-radius:var(--r-xs);
  padding:var(--s4) var(--s3) var(--s1);font-size:var(--fs-2xs);font-weight:700;
  text-transform:uppercase;letter-spacing:.08em;min-height:0;
}
.nav-group-h:hover{background:transparent;color:var(--ink2)}
.nav-group-h>span{display:inline-flex;align-items:center;gap:var(--s2);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav-group-chev{transition:transform var(--t) var(--ease);flex-shrink:0;opacity:.55}
.nav-group.collapsed .nav-group-chev{transform:rotate(-90deg)}
.nav-group-links{
  display:flex;flex-direction:column;gap:1px;margin-bottom:var(--s1);
  max-height:280px;opacity:1;overflow:hidden;
  transition:max-height var(--t-slow) var(--ease),opacity var(--t) var(--ease),margin-bottom var(--t);
}
.nav-group.collapsed .nav-group-links{max-height:0;opacity:0;margin-bottom:0}

aside nav a{
  position:relative;display:flex;align-items:center;gap:var(--s2);
  color:var(--ink2);text-decoration:none;
  padding:7px var(--s3);margin:0 var(--s1);border-radius:var(--r-sm);
  font-weight:500;font-size:var(--fs-base);min-width:0;
  transition:background var(--t-fast),color var(--t-fast);
}
aside nav a>span:not(.nav-badge):not(.tag){min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
aside nav a:hover{background:color-mix(in srgb, var(--ink) 6%, transparent);color:var(--ink)}
/* A hue per sidebar group, so the sections are distinguishable at a glance. It is
   deliberately applied in only two places — the group's label and the active item's
   indicator bar — so colour keeps meaning "which area am I in" and never competes with
   the status palette (green/amber/red) that means "how is this doing". */
.nav-group{--grp:var(--accent)}
.nav-group[data-group="pipeline"]{--grp:var(--brand-500)}
.nav-group[data-group="marketing"]{--grp:#7c5cf0}
.nav-group[data-group="sources"]{--grp:#0d9488}
.nav-group-h>span{color:color-mix(in srgb, var(--grp) 62%, var(--muted))}
aside nav a.active{background:color-mix(in srgb, var(--grp) 11%, transparent);color:color-mix(in srgb, var(--grp) 78%, var(--ink));font-weight:640}
aside nav a.active .nav-ic{opacity:1;color:var(--grp)}
aside nav a.active::before{
  content:"";position:absolute;left:calc(var(--s1) * -1);top:50%;transform:translateY(-50%);
  width:3px;height:18px;border-radius:0 3px 3px 0;background:var(--grp);
}
.nav-ic{flex-shrink:0;opacity:.7}
.nav-badge{
  display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;
  border-radius:var(--r-full);background:var(--critical);color:#fff;
  font-size:var(--fs-2xs);font-weight:700;margin-left:auto;flex-shrink:0;font-variant-numeric:tabular-nums;
}
aside nav a .tag{margin-left:auto;flex-shrink:0}

/* sidebar footer */
.side-foot{display:flex;align-items:center;gap:var(--s2);padding:var(--s2) var(--s1) 0;border-top:1px solid var(--line);flex-shrink:0}
.side-foot .rail-btn{
  background:transparent;color:var(--muted);border:0;box-shadow:none;font-size:var(--fs-sm);font-weight:500;
  justify-content:flex-start;flex:1;padding:6px var(--s2);min-height:32px;
}
.side-foot .rail-btn:hover{background:color-mix(in srgb, var(--ink) 6%, transparent);color:var(--ink)}
.side-foot .rail-btn svg{transition:transform var(--t) var(--ease)}
#app.rail .side-foot .rail-btn svg{transform:rotate(180deg)}
/* legacy user bar — user now lives in the topbar menu */
.userbar{display:none}

/* — rail (collapsed) sidebar — */
#app.rail aside{padding-left:var(--s2);padding-right:var(--s2)}
#app.rail aside nav a{justify-content:center;padding:9px 0;margin:0 2px}
#app.rail aside nav a>span:not(.nav-badge),#app.rail aside nav a .tag{display:none}
#app.rail aside nav a .nav-badge{
  display:inline-flex;position:absolute;top:2px;right:6px;min-width:15px;height:15px;padding:0 3px;
  font-size:9px;margin:0;
}
#app.rail .nav-ic{width:18px;height:18px}
#app.rail .nav-group-h{justify-content:center;padding:var(--s3) 0 var(--s1);pointer-events:none}
#app.rail .nav-group-h>span{display:none}
#app.rail .nav-group-h::after{content:"";display:block;width:18px;height:1px;background:var(--line-2)}
#app.rail .nav-group-chev{display:none}
#app.rail .nav-group.collapsed .nav-group-links{max-height:none;opacity:1;margin-bottom:var(--s1)}
#app.rail .side-foot .rail-btn{justify-content:center}
#app.rail .side-foot .rail-btn span{display:none}
/* hover label for rail items */
#app.rail aside nav a::after{
  content:attr(data-label);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%) scale(.94);
  background:var(--n900);color:var(--n0);font-size:var(--fs-sm);font-weight:520;white-space:nowrap;
  padding:5px 10px;border-radius:var(--r-xs);box-shadow:var(--shadow-2);
  opacity:0;pointer-events:none;transition:opacity var(--t-fast),transform var(--t-fast) var(--ease);z-index:var(--z-pop);
}
#app.rail aside nav a:hover::after,#app.rail aside nav a:focus-visible::after{opacity:1;transform:translateY(-50%) scale(1)}
:root[data-theme="dark"] #app.rail aside nav a::after{background:var(--surface-3);color:var(--ink);border:1px solid var(--line)}

/* — main content — */
main{
  grid-area:main;min-width:0;
  padding:var(--s6) var(--s7) var(--s9);
  width:100%;max-width:var(--content-max);
  margin:0 auto;
}
.nav-toggle{display:none}

/* =============================================================================
   06 · PAGE HEADER & BREADCRUMBS
   ========================================================================== */
.crumbs{
  display:flex;align-items:center;gap:var(--s1);min-width:0;flex:1;
  font-size:var(--fs-sm);color:var(--muted);overflow:hidden;
}
.crumbs a{color:var(--muted);text-decoration:none;white-space:nowrap;border-radius:var(--r-xs);padding:2px 4px}
.crumbs a:hover{color:var(--ink);background:color-mix(in srgb, var(--ink) 6%, transparent)}
.crumbs .sep{color:var(--n400);flex-shrink:0}
.crumbs .cur{color:var(--ink);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:2px 0}

.pagehead{
  display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s4);
  margin-bottom:var(--s5);flex-wrap:wrap;
}
.pagehead>h2,.pagehead>h3{min-width:0}
.pagehead h2{font-size:var(--fs-2xl);font-weight:660;letter-spacing:-.026em;line-height:1.2}
.pagehead h3{font-size:var(--fs-lg);font-weight:640}
/* flex:1 — the actions row may spend the header's spare middle width, which is what lets the grown
   search bar AND every button share ONE line (owner, 2026-09-09) instead of wrapping while half the
   header sits empty. The title keeps its intrinsic width beside it. */
.pagehead .actions{display:flex;gap:var(--s3);flex-wrap:wrap;align-items:center;min-width:0;flex:1;justify-content:flex-end}
/* Related actions sit together with a hairline seam, so a long toolbar reads as a few
   groups (view · data in-out · create) instead of one undifferentiated row of buttons. */
.btn-group{display:inline-flex;align-items:center;gap:0}
.btn-group>button,.btn-group>a{border-radius:0;margin:0}
.btn-group>:first-child{border-top-left-radius:var(--r-sm);border-bottom-left-radius:var(--r-sm)}
.btn-group>:last-child{border-top-right-radius:var(--r-sm);border-bottom-right-radius:var(--r-sm)}
.btn-group>:not(:first-child){margin-left:-1px}
.btn-group>button:hover,.btn-group>a:hover{position:relative;z-index:1}
.pagehead .actions select,.pagehead .actions input{width:auto;min-width:120px}
.pagehead .actions label.inline{align-self:center}
/* a subtitle line directly under the page title */
.pagehead .subtitle{font-size:var(--fs-base);color:var(--muted);font-weight:450;margin-top:5px;max-width:80ch}
/* A <select> that has to read as one word INSIDE a sentence — the Newsletter page's send day sits in
   "send weekly, every [Wednesday]." rather than in a settings card (owner, 2026-09-09). So: no box,
   no fixed width, inherits the sentence's size and colour, and only shows its edges on hover and
   focus, which is what tells a reader it is editable at all. Underlined at rest for the same reason:
   a control that looks exactly like prose is a control nobody finds. */
.inline-sel{
  appearance:none;-webkit-appearance:none;
  display:inline;width:auto;min-width:0;min-height:0;height:auto;
  margin:0;padding:0 18px 0 2px;border:0;border-radius:var(--r-xs);
  background:transparent;box-shadow:none;
  font:inherit;color:var(--ink);font-weight:600;
  text-decoration:underline;text-decoration-style:dotted;text-underline-offset:3px;
  cursor:pointer;
  /* The chevron, drawn rather than an extra element — currentColor so it follows the theme. */
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-size:5px 5px,5px 5px;
  background-position:right 7px center,right 3px center;
  background-repeat:no-repeat;
}
.inline-sel:hover{color:var(--accent);text-decoration-style:solid}
.inline-sel:focus-visible{outline:2px solid var(--accent);outline-offset:2px;text-decoration:none}
/* The OPTIONS render in the OS menu, which does not inherit the transparent background — without
   this the list is unreadable in dark mode on Chrome/Linux. */
.inline-sel option{background:var(--surface);color:var(--ink);font-weight:450}
/* The card's header strip, in the house dark teal-green (2026-08-22). Six of its ten uses sit
   directly above a table whose own header row is already brand-800 — white strip, dark band, rows —
   so one rule collapses that into a single header block. Negative margins pull it out to the card's
   edges (the card pads by --s5) and the top corners follow the card's radius, or it reads as a dark
   rectangle floating inside a white one. Everything inside is inverted: muted sub-lines, links and
   the small ghost buttons some heads carry all assume a light ground. */
.tablehead{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;
  background:var(--brand-800);color:#fff;
  padding:10px var(--s5);
  margin:calc(var(--s5) * -1) calc(var(--s5) * -1) var(--s4);
  border-radius:calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0;
}
.tablehead h3{margin-bottom:0;color:#fff}
.tablehead .muted,.tablehead .metric-period{color:rgba(255,255,255,.75)}
.tablehead a{color:#fff;text-decoration:underline}
/* A switch-row inherits --ink, which on this dark band is near-black in light mode — the Newsletter
   page's "Suggest weekly" label was unreadable there (audit, 2026-09-10). White, like every other
   word on the band. */
.tablehead .switch-row{color:#fff}
/* The controls a few heads carry — Events' month nav, "Show all leads", the Table/Board toggle. */
/* The seg's own shell is styled for a light card (--surface-3 + --line). Left alone on this dark
   band it put the buttons' white text on a near-white ground, so the UNSELECTED tab was the one
   nobody could read. Invert the shell too, not just the buttons. */
.tablehead .net-seg,.tablehead .seg-inline{background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.28)}
.tablehead button.small,.tablehead .btn-group button,.tablehead .net-seg button{
  background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.28);color:#fff;
}
.tablehead button.small:hover,.tablehead .btn-group button:hover,.tablehead .net-seg button:hover{
  background:rgba(255,255,255,.22);border-color:rgba(255,255,255,.4);color:#fff;
}
.tablehead .net-seg button.on{background:#fff;color:var(--brand-800);border-color:#fff}
/* Groups a run of tiles under a labelled band, so eight KPIs read as two related
   sets instead of one undifferentiated wall. */
.section-head{
  font-size:var(--fs-sm);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin:var(--s6) 0 var(--s3);display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;
}
.section-head .muted{font-size:var(--fs-sm);font-weight:500;letter-spacing:0;text-transform:none}
.section-head:first-child{margin-top:0}
/* NOTE: `.head-row` is always a <tr> in this app (Events, Tenders, Business
   Network header rows). It must NOT be given a display/layout of its own —
   doing so detaches the header cells from the body columns and the column
   labels stop lining up with their data. Style the <th>s, never the row. */

/* =============================================================================
   07 · SURFACES — cards, tiles, KPI
   ========================================================================== */
.card{
  /* A whisper of a gradient — the surface reads as material, not a flat fill. Both themes: it mixes
     from the theme's own surface-2, so dark mode gets the same treatment without a second ramp. */
  background:linear-gradient(180deg, color-mix(in srgb, var(--surface-2) 38%, var(--surface)), var(--surface) 52px);
  border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s5);margin-bottom:var(--s5);box-shadow:var(--shadow-1);
}
.card h3{font-size:var(--fs-md);margin-bottom:var(--s4);font-weight:640;letter-spacing:-.012em}
/* No negative margin on a paragraph following a card heading: several headings carry
   their own inline margin-bottom, and pulling the paragraph up then overlapped them
   (Collections). Let normal flow handle the gap. */
.card>h3+p.muted{margin-bottom:var(--s4)}
.card.banner{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s4);flex-wrap:wrap;
  background:var(--surface-2);border-color:var(--line);
}
.card.banner p{margin:0}
/* parked-feature notice: informational, not alarming */
.card.banner.parked{
  border-color:color-mix(in srgb,var(--warning) 28%,var(--line));
  background:color-mix(in srgb,var(--warning) 7%,var(--surface));
}
.card.banner.parked>div{display:flex;flex-direction:column;gap:3px;min-width:0}
.card.banner.parked .muted{font-size:var(--fs-sm)}
/* icon + label buttons: keep the glyph optically aligned with the text */
button>svg,.btn>svg{width:15px;height:15px;flex-shrink:0}
.icon-btn>svg{width:16px;height:16px}
.grid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:var(--s5);align-items:start}
.grid2>.card{margin-bottom:0}

/* KPI tiles */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));gap:var(--s3);margin-bottom:var(--s5)}
.tile{
  position:relative;overflow:hidden;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s4) var(--s4) var(--s4) var(--s5);box-shadow:var(--shadow-1);min-width:0;
}
.tile{--tile:var(--accent)}
.tile::before{
  content:"";position:absolute;left:0;top:var(--s4);bottom:var(--s4);width:3px;
  border-radius:0 3px 3px 0;background:var(--tile);opacity:.85;
}
/* A toned tile carries a verdict — failed, overdue — so the number takes the tone too. */
.tile.toned .n{color:var(--tile)}
.tile .n{
  font-size:var(--fs-3xl);font-weight:680;letter-spacing:-.03em;line-height:1.1;
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere;
}
.tile .n .muted{font-size:var(--fs-lg);font-weight:600}
.tile .l{
  font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.07em;
  font-weight:640;margin-top:var(--s1);
}
.tile .sub{font-size:var(--fs-sm);color:var(--ink2);margin-top:6px}
/* The tile's own mark — a large, faint watermark in the bottom-right corner rather than a small
   badge. At 80% transparency it never competes with the number (still the first thing the eye lands
   on) but it is big enough to be recognised before the label is read, which is the whole job.
   Bottom-right, not top-right: the number and label keep their full width, so no label wraps to a
   second line just to make room for an ornament. */
.tile .tile-ic{
  position:absolute;bottom:9px;right:10px;
  display:inline-flex;align-items:center;justify-content:center;
  width:50px;height:50px;color:var(--muted);opacity:.1;pointer-events:none;
}
/* FULL SILHOUETTES, HALF AS PRESENT (owner, 2026-09-11: "icons should be more realistic and 50% more
   transparent"). The marks were thin line art with a faint fill; they are solid shapes now — the fill
   at full strength, the outline a step heavier — so a trophy reads as a trophy at a glance, and the
   whole mark sits at half its previous opacity (.1 / .11) so it stays a watermark under the number.
   CSS wins over the SVGs' own fill-opacity attributes, so every page's band changes at once. */
.tile .tile-ic svg{width:100%;height:100%;display:block;stroke-width:2.1}
.tile .tile-ic svg [fill-opacity]{fill-opacity:.92}
/* A toned tile is already a verdict, so its mark carries the verdict's colour — at the same
   transparency, so the band stays even. */
.tile.toned .tile-ic{color:var(--tile);opacity:.11}
.tile .sub{padding-right:58px}
a.tile{display:block;color:inherit;text-decoration:none;transition:border-color var(--t),box-shadow var(--t),transform var(--t)}
a.tile:hover,a.tile:focus-visible{border-color:color-mix(in srgb, var(--accent) 45%, var(--line));box-shadow:var(--shadow-2);transform:translateY(-2px)}
/* A metric tile that is also a filter. Same box as the rest of the band — it must not look like a
   different kind of thing — with a pressed state for the filter currently in force. */
button.tile{
  /* display:block on purpose — the global button rule is inline-flex/center, which laid the
     number, label and sub out side by side and overlapping. A metric tile must look identical
     whether it is a div, a link or a filter button. */
  display:block;text-align:left;font:inherit;font-weight:inherit;color:inherit;cursor:pointer;
  min-height:0;background:var(--surface);
  /* buttons are nowrap by default in this design system; a metric tile's sub-line has to wrap or it
     is clipped by the tile's own overflow:hidden at narrow widths */
  white-space:normal;
  transition:border-color var(--t),box-shadow var(--t),transform var(--t),background var(--t);
}
button.tile:hover{border-color:color-mix(in srgb,var(--tile) 45%,var(--line));box-shadow:var(--shadow-2);transform:translateY(-2px)}
button.tile:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.tile.on{
  border-color:color-mix(in srgb,var(--tile) 55%,var(--line));
  background:color-mix(in srgb,var(--tile) 7%,var(--surface));
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--tile) 22%,transparent),var(--shadow-1);
}
.tile.on::before{opacity:1;width:4px}
/* A tile that carries its own control: the switch or button hangs off the bottom of the same box.
   The wrapper is the grid item, so the band keeps its columns; the tile loses its bottom corners and
   the foot takes them, so the two read as ONE square rather than a tile with a thing under it.
   The hover lift is cancelled here — a tile rising away from its own foot looks broken. */
/* COMPACT METRICS (Leads, 2026-09-11): the same tiles at two-thirds size, under the collection cards.
   Number, label and the filter behaviour are unchanged; only the type scale, padding, gaps and the
   watermark shrink, so the strip reads as context beneath the collections rather than the page's
   headline. The "Contact data" head becomes a small inline label. */
.metrics-compact .tiles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--s2);margin-bottom:var(--s3)}
.metrics-compact .tile{padding:9px 12px 9px 14px;border-radius:var(--r-md)}
.metrics-compact .tile::before{top:9px;bottom:9px}
.metrics-compact .tile .n{font-size:var(--fs-xl)}
.metrics-compact .tile .n .muted{font-size:var(--fs-base)}
.metrics-compact .tile .l{font-size:var(--fs-2xs);margin-top:2px;letter-spacing:.06em}
.metrics-compact .tile .sub{font-size:var(--fs-2xs);margin-top:2px;padding-right:40px}
.metrics-compact .tile .tile-ic{width:32px;height:32px;bottom:6px;right:8px}
.metrics-compact .section-head{margin:0 0 var(--s2);font-size:var(--fs-2xs)}
.tile-wrap{display:flex;flex-direction:column;min-width:0}
.tile-wrap>.tile{flex:1;border-radius:var(--r-lg) var(--r-lg) 0 0;box-shadow:none}
.tile-wrap>button.tile:hover,.tile-wrap>button.tile:focus-visible{transform:none}
.tile-foot{
  /* wrap: the Approved foot carries a switch AND a Send now button, and a narrow grid column would
     otherwise push one of them out of the tile it belongs to. */
  display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  padding:8px var(--s3) 8px var(--s5);
  background:var(--surface);border:1px solid var(--line);border-top:1px solid var(--line-soft);
  border-radius:0 0 var(--r-lg) var(--r-lg);box-shadow:var(--shadow-1);
}
.tile-foot .switch-row{margin:0;gap:8px;font-size:var(--fs-sm)}
.tile-foot .small{margin:0}
/* the segmented period switch that sits in a section heading */
.section-head .metric-period{margin-left:auto;text-transform:none;letter-spacing:0;align-self:center}

/* "this goes somewhere" — shared by linked tiles and by metric tiles that drill into a detail page,
   so there is one affordance for it rather than two that look different. */
a.tile::after,button.tile.drill::after{
  content:"→";position:absolute;top:var(--s3);right:var(--s3);color:var(--muted);
  font-size:var(--fs-base);opacity:0;transform:translateX(-4px);transition:opacity var(--t),transform var(--t);
}
a.tile:hover::after,a.tile:focus-visible::after,
button.tile.drill:hover::after,button.tile.drill:focus-visible::after{opacity:1;transform:none}

.note-item{padding:var(--s2) 0;border-bottom:1px solid var(--line-soft)}
.note-item:last-child{border-bottom:none}
/* A note-item carrying a file: the name and its size on the left, the remove control pinned right.
   A long filename gets an ellipsis rather than pushing the button off the panel. */
.att-row{display:flex;align-items:center;gap:var(--s3)}
.att-name{flex:1;min-width:0}
.att-name>a{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kvrow{
  display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:var(--s2) var(--s3);
  padding:var(--s2) 0;border-bottom:1px solid var(--line-soft);font-size:var(--fs-base);
}
.kvrow:last-child{border-bottom:0}
.kvrow>strong{font-variant-numeric:tabular-nums}
/* Same treatment for a kvrow that navigates — the Signals list on the dashboard. */
button.kvrow-act{
  width:100%;font:inherit;color:inherit;background:transparent;border:0;border-bottom:1px solid var(--line-soft);
  text-align:left;cursor:pointer;transition:background var(--t) var(--ease);
}
button.kvrow-act:hover{background:var(--surface-2)}
button.kvrow-act:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

/* =============================================================================
   08 · BADGES, TAGS, SCORES
   ========================================================================== */
.badge{
  display:inline-flex;align-items:center;gap:5px;font-size:var(--fs-hint);font-weight:600;
  border-radius:var(--r-full);padding:2px 10px;border:1px solid var(--line);
  background:var(--surface);color:var(--ink2);box-shadow:none;white-space:nowrap;line-height:1.6;
}
.badge::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--muted);flex-shrink:0}
/* Lead statuses. The class name is the stored slug; keep these in step with STATUS_DOT in app.js
   so a dot means the same thing on a badge, a board column and the table's status select.
   `sequenced` is kept as an alias because the invite badge borrows it for its pending colour. */
.badge.new{background:var(--st-new-soft);border-color:transparent;color:color-mix(in srgb,var(--st-new) 62%,var(--ink))}
.badge.new::before{background:var(--st-new)}
.badge.not-contacted::before{background:var(--seq-4)}
.badge.attempted{background:var(--st-attempt-soft);border-color:transparent;color:color-mix(in srgb,var(--st-attempt) 70%,var(--ink))}
.badge.attempted::before{background:var(--st-attempt)}
/* `sequenced` is the invite badge's pending colour, not a lead status — it keeps warning. */
.badge.contacted{background:var(--st-contacted-soft);border-color:transparent;color:color-mix(in srgb,var(--st-contacted) 68%,var(--ink))}
.badge.contacted::before{background:var(--st-contacted)}
/* Written off, both of them: grey, and deliberately the SAME grey — the distinction between junk
   and not-interested is in the word, not the colour, and neither deserves a red alarm. */
.badge.not-interested{color:var(--muted)}
.badge.not-interested::before{background:var(--muted)}
.badge.won{background:var(--good-soft);color:color-mix(in srgb,var(--good) 72%,var(--ink));border-color:color-mix(in srgb,var(--good) 32%,transparent)}
.badge.won::before{background:var(--good)}
.badge.junk,.badge.lost{color:var(--muted)}
.badge.junk::before,.badge.lost::before{background:var(--muted)}
.badge.draft::before,.badge.skipped::before,.badge.idle::before{background:var(--muted)}
.badge.idle{color:var(--muted)}
.badge.approved{background:var(--warning-soft);border-color:transparent;color:color-mix(in srgb,var(--warning) 70%,var(--ink))}
.badge.approved::before{background:var(--warning)}
.badge.sent,.badge.done{background:var(--good-soft);border-color:transparent;color:color-mix(in srgb,var(--good) 70%,var(--ink))}
.badge.sent::before,.badge.done::before{background:var(--good)}
.badge.failed{background:var(--critical-soft);color:var(--critical);border-color:color-mix(in srgb,var(--critical) 30%,transparent)}
.badge.failed::before{background:var(--critical)}
.badge.running{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 72%,var(--ink));border-color:color-mix(in srgb,var(--warning) 30%,transparent)}
.badge.running::before{background:var(--warning);animation:pulseDot 1.4s ease-in-out infinite}
.badge.off{background:var(--critical-soft);color:var(--critical);border-color:color-mix(in srgb,var(--critical) 26%,transparent)}
.badge.off::before{background:var(--serious)}
@keyframes pulseDot{0%,100%{opacity:1}50%{opacity:.35}}

.tag{
  display:inline-flex;align-items:center;gap:5px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-full);padding:2px 10px;
  font-size:var(--fs-hint);font-weight:520;color:var(--ink2);margin:2px 3px 2px 0;
  box-shadow:none;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis;
  transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast);
}
.chips{display:flex;flex-wrap:wrap;gap:var(--s1)}
.tag.mini{font-size:var(--fs-xs);padding:2px 8px;cursor:pointer;user-select:none}
.tag.mini:hover{background:var(--accent-soft);color:var(--tint-ink);border-color:transparent}
/* A chip that is a link (a task's deal, for instance) keeps the chip's shape rather than picking up
   the underline and accent colour every other anchor gets. */
a.tag{color:var(--ink2);text-decoration:none}
a.tag:hover{color:var(--tint-ink)}
.tag.mini.static{cursor:default}
.tag.mini.static:hover{background:var(--surface-2);color:var(--ink2);border-color:var(--line)}
.tag.auto,.tag.auto-semi{background:var(--accent-soft);color:var(--tint-ink);border-color:transparent;font-weight:600}
.tag.auto-full{background:var(--good-soft);color:color-mix(in srgb,var(--good) 70%,var(--ink));border-color:transparent;font-weight:600}
.tag.auto-manual{background:var(--surface-2);color:var(--muted);border-color:var(--line);font-weight:600}
.tag.soon{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 72%,var(--ink));border-color:transparent;font-weight:600}
.tag.sev-high{background:var(--critical-soft);color:var(--critical);border-color:transparent;font-weight:600}
.tag.sev-medium{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 72%,var(--ink));border-color:transparent;font-weight:600}
.tag.net-goal,.tag.net-industry,.tag.net-role{background:var(--surface-2);color:var(--ink2)}
.tag.poweredby,.poweredby{font-size:var(--fs-2xs);color:var(--muted);letter-spacing:.02em}

.score{
  display:inline-flex;align-items:center;justify-content:center;min-width:36px;
  border-radius:var(--r-xs);padding:1px 7px;font-weight:660;font-size:var(--fs-sm);
  font-variant-numeric:tabular-nums;background:var(--accent-soft);color:var(--tint-ink);
}
.verify-ico{
  display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;margin-left:4px;
  font-weight:700;font-size:10px;border-radius:50%;flex-shrink:0;
}
.verify-ico.ok{color:var(--good);background:color-mix(in srgb, var(--good) 16%, transparent)}
.verify-ico.bad{color:var(--critical);background:color-mix(in srgb, var(--critical) 16%, transparent)}
.verify-ico.unk{color:var(--muted);background:color-mix(in srgb, var(--muted) 16%, transparent)}

/* inline status <select> styled as a status pill.
   ONE fixed height for every status, active or not — the pill's colour is the signal, not its
   size. The focus ring is keyboard-only (:focus-visible): after a mouse change the select keeps
   focus, and the old :focus ring sat there glowing and reading as a bigger pill. */
.status-select{
  --dot:var(--muted);width:auto;min-width:118px;border:1px solid transparent;border-radius:var(--r-full);
  padding:0 28px 0 13px;font-weight:620;font-size:var(--fs-sm);text-transform:capitalize;
  height:28px;min-height:28px;max-height:28px;line-height:26px;
  color:color-mix(in srgb, var(--dot) 78%, var(--ink));
  background-color:color-mix(in srgb, var(--dot) 14%, var(--surface));
  background-position:right 9px center;
}
.status-select:hover{background-color:color-mix(in srgb, var(--dot) 22%, var(--surface));border-color:transparent}
.status-select:focus{outline:none;border-color:transparent;box-shadow:none}
.status-select:focus-visible{border-color:var(--dot);box-shadow:0 0 0 3px color-mix(in srgb, var(--dot) 26%, transparent)}

/* =============================================================================
   09 · TABLES
   ========================================================================== */
.table-wrap{
  overflow-x:auto;overflow-y:visible;border-radius:var(--r-md);
  /* scroll-shadow affordance: a soft edge fades in on whichever side still has
     more columns to reach, and clears once you've scrolled all the way there. */
  background:
    linear-gradient(to right, var(--surface) 30%, rgba(255,255,255,0)) 0 0,
    linear-gradient(to left, var(--surface) 30%, rgba(255,255,255,0)) 100% 0,
    radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.13), rgba(0,0,0,0)) 0 0,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.13), rgba(0,0,0,0)) 100% 0;
  background-repeat:no-repeat;
  background-size:44px 100%, 44px 100%, 14px 100%, 14px 100%;
  background-attachment:local, local, scroll, scroll;
}
table,.data-table,.activity-table{width:100%;border-collapse:separate;border-spacing:0;font-size:var(--fs-base)}
/* ONE dark band per section (owner, 2026-09-11: "sections that have tables have two headers, which
   is not comfortable to look at"). The card's own head band is the heading; the column row is a
   QUIET label row everywhere — a light well, small muted uppercase type — so a table never stacks a
   second navy bar under the first. Standalone tables get the same quiet row: one rule for every
   table on the platform, which is how it stays one design. */
th,.data-table th,.activity-table th{
  position:sticky;top:0;z-index:var(--z-sticky);
  font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.07em;font-weight:700;
  color:var(--ink);text-align:left;padding:8px var(--s3);
  /* A label row, not a second heading: a faint brand-tinted well under dark text, closed by a two-pixel
     brand rule — the eye reads it as "the columns" without a second navy bar competing with the card's
     own head. Dark text on the tint is ~13:1, readable in both themes. */
  background:color-mix(in srgb, var(--accent) 7%, var(--surface));
  border-bottom:2px solid color-mix(in srgb, var(--accent) 45%, var(--line));
  white-space:nowrap;
}
/* The signature editor is somebody's own HTML being edited in place — a table THEY pasted must not
   be repainted in our brand colour. */
.sig-edit th{background:transparent;color:inherit;position:static;text-transform:none;letter-spacing:0}
/* rounded top corners on the first/last header cell of a card-hosted table */
.card .table-wrap th:first-child{border-top-left-radius:var(--r-sm)}
.card .table-wrap th:last-child{border-top-right-radius:var(--r-sm)}
td,.data-table td,.activity-table td{
  padding:8px var(--s3);border-bottom:1px solid var(--line-soft);vertical-align:top;
  transition:background var(--t-fast);overflow-wrap:anywhere;
}
tr:last-child td,.data-table tr:last-child td{border-bottom:0}
tbody tr:hover td,.data-table tr.dt-row:hover td{background:color-mix(in srgb, var(--accent) 5%, transparent)}
/* A row that navigates. The hover tint above already applies; this adds the two things that were
   missing — a pointer, and a focus ring for the keyboard path (role=button + Enter/Space). */
tr.row-act{cursor:pointer}
tr.row-act:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.activity-table{font-size:var(--fs-sm)}
.activity-table td{padding:8px var(--s3)}
.activity-table code,.wiz-body code{
  font-size:var(--fs-hint);background:var(--surface-3);border:1px solid var(--line);
  padding:1px 6px;border-radius:5px;white-space:nowrap;
}
.data-table th.sortable{cursor:pointer;user-select:none}
.data-table th.sortable:hover{color:var(--ink);text-decoration:underline}
/* The sorted column is named by its arrow — on a dark header an accent tint would be less legible,
   not more. */
.data-table th.sortable[aria-sort="ascending"],.data-table th.sortable[aria-sort="descending"]{color:var(--accent)}
.data-table th.sortable[aria-sort="ascending"]::after{content:" ↑"}
.data-table th.sortable[aria-sort="descending"]::after{content:" ↓"}
.data-table .filter-row th{position:static;background:var(--surface);padding-top:var(--s2);padding-bottom:var(--s2)}
.data-table .filter-row input{width:100%;padding:5px 9px;font-size:var(--fs-sm);font-weight:400;border-radius:var(--r-xs);min-height:28px}
.data-table .filter-row input.narrow{width:64px}
.data-table .cell-wrap{min-width:160px;max-width:300px;color:var(--ink2)}
.data-table .overdue,td .overdue{color:var(--critical);font-weight:620}
.data-table button.small{margin:2px 3px 0 0}
.data-table tr.lost-row td{opacity:.55}
.empty-cell{padding:0}
.cell-desc{
  font-size:var(--fs-sm);line-height:var(--lh-snug);margin-top:3px;max-width:340px;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.cell-contact{display:flex;align-items:center;gap:var(--s2);min-width:0}
/* Notes is now an editable cell holding a textarea, not clamped text — and no minimum width, which
   was the one thing still forcing the table wider than its container. */
#leadTable .cell-notes{font-size:var(--fs-sm);line-height:var(--lh-snug);overflow:hidden}
.dt-detail>td{background:var(--surface-2);padding:0}

/* selection / bulk bar */
.bulk-count{font-variant-numeric:tabular-nums}

/* avatars in tables */
.lead-avatar-ring{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:38px;height:38px;min-width:38px;border-radius:50%;padding:2px;flex-shrink:0;
  background:conic-gradient(var(--ring) calc(var(--pct)*1%), var(--line-2) 0);
}
.lead-avatar-ring img{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block;background:var(--surface)}
.lead-avatar-fallback{
  width:100%;height:100%;border-radius:50%;background:var(--surface-3);color:var(--ink2);
  align-items:center;justify-content:center;font-size:var(--fs-sm);font-weight:700;
}
.company-avatar{position:relative;display:inline-flex;width:38px;height:38px;min-width:38px;flex-shrink:0}
.company-logo{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;border-radius:50%;padding:2px;
  background:conic-gradient(from 200deg, color-mix(in srgb, var(--ring) 50%, transparent), var(--ring), color-mix(in srgb, var(--ring) 50%, transparent));
  box-shadow:var(--shadow-1);transition:transform var(--t) var(--ease);
}
.company-avatar:hover .company-logo{transform:scale(1.06)}
.company-logo img{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block;background:var(--surface)}
.company-logo-fallback{width:100%;height:100%;border-radius:50%;background:var(--surface);align-items:center;justify-content:center;font-size:15px}
.company-badge{
  position:absolute;min-width:16px;height:16px;padding:0 3px;border-radius:var(--r-full);
  font-size:8.5px;font-weight:800;line-height:1;display:inline-flex;align-items:center;justify-content:center;
  color:#fff;border:2px solid var(--surface);box-shadow:var(--shadow-1);
}
/* The employee count was a number nobody acted on. A buying signal on a lead is the opposite —
   it is the reason to pick this lead over the one under it, so it takes the badge.
   Signal yellow is its own colour, deliberately not the warning amber: a signal is an opportunity,
   not a problem, and it has to pop out of a scan of forty rows. */
.company-badge-signal{
  bottom:-4px;right:-6px;width:17px;height:17px;padding:0;
  background:linear-gradient(145deg,#ffdd55,#f5a524);
  color:#4a3200;border-color:var(--surface);
  box-shadow:0 0 0 1px rgba(245,165,36,.45),0 1px 3px rgba(245,165,36,.5);
}
.company-badge-signal svg{width:11px;height:11px}
.lead-name-link{
  cursor:pointer;text-decoration:underline;text-decoration-color:transparent;
  text-underline-offset:2px;transition:text-decoration-color var(--t);
}
.lead-name-link:hover,.lead-name-link:focus-visible{text-decoration-color:currentColor}

/* =============================================================================
   10 · FILTER BARS
   ========================================================================== */
/* align-items:center + one shared control height — mixed input/select/button heights made every
   filter row read as slightly broken, which is the kind of thing nobody reports but everyone feels. */
.filters,.filterbar{display:flex;gap:var(--s2);flex-wrap:wrap;margin:0 0 var(--s4);align-items:center}
.filters input,.filters select,.filterbar input,.filterbar select{width:auto;min-height:34px}
.filterbar input[type=search]{flex:1;min-width:220px}
.filterbar{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s3);box-shadow:var(--shadow-1);
}
.filterbar.plain{background:transparent;border:0;padding:0;box-shadow:none}

/* =============================================================================
   11 · KANBAN BOARDS
   ========================================================================== */
.board{display:flex;gap:var(--s3);overflow-x:auto;padding-bottom:var(--s2);align-items:flex-start}
.board-col{
  flex:0 0 274px;width:274px;background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-lg);display:flex;flex-direction:column;max-height:74vh;box-shadow:var(--shadow-1);
}
.board-col-head{
  position:sticky;top:0;z-index:1;
  background:color-mix(in srgb, var(--surface-2) 88%, transparent);
  backdrop-filter:blur(12px) saturate(160%);-webkit-backdrop-filter:blur(12px) saturate(160%);
  border-bottom:1px solid var(--line);border-radius:var(--r-lg) var(--r-lg) 0 0;padding:var(--s3);
}
.board-col-title{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);
  font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--ink2);
}
.board-col-count{
  font-size:var(--fs-2xs);font-weight:700;color:var(--muted);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-full);padding:1px 8px;font-variant-numeric:tabular-nums;
}
.board-col-sum{font-size:var(--fs-hint);color:var(--muted);margin-top:3px;font-variant-numeric:tabular-nums}
.board-col-body{padding:var(--s2);display:flex;flex-direction:column;gap:var(--s2);overflow-y:auto}
.board-card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s3);cursor:pointer;box-shadow:var(--shadow-1);
  transition:border-color var(--t),box-shadow var(--t),transform var(--t);
}
.board-card:hover{border-color:color-mix(in srgb, var(--accent) 40%, var(--line));box-shadow:var(--shadow-2);transform:translateY(-2px)}
.board-card:active{transform:scale(.985);box-shadow:var(--shadow-1)}
.board-card:focus-visible{outline:none;border-color:var(--accent);box-shadow:var(--ring-focus)}
.board-card-title{font-weight:640;font-size:var(--fs-base);color:var(--ink);line-height:var(--lh-tight)}
.board-card-sub{font-size:var(--fs-sm);color:var(--muted);margin-top:3px}
.board-card-meta{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);flex-wrap:wrap;margin-top:var(--s2)}
.board-card select.small{margin-top:var(--s2);padding:5px 9px;font-size:var(--fs-sm);border-radius:var(--r-xs);width:100%}
/* status select + the ⋯ actions chip share the card's last row */
.board-card-foot{display:flex;align-items:center;gap:var(--s2)}
.board-card-foot select.small{flex:1;min-width:0}
.board-card-foot .act-tool{margin-top:var(--s2);flex-shrink:0}
.board-empty{font-size:var(--fs-sm);color:var(--muted);padding:var(--s3) var(--s1);text-align:center}
.board-card[draggable]{cursor:grab}
.board-card.dragging{opacity:.4}
.board-col.drag-over{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft) inset}

/* =============================================================================
   12 · LISTS — notifications, tasks, connectors, collections
   ========================================================================== */
.notif-list{display:flex;flex-direction:column;gap:var(--s2);margin-bottom:var(--s4)}
.notif-item{
  display:flex;align-items:center;gap:var(--s3);background:var(--surface);
  border:1px solid var(--line);border-left-width:3px;border-left-color:var(--line);
  border-radius:var(--r-md);padding:var(--s3) var(--s4);text-decoration:none;color:var(--ink);
  box-shadow:var(--shadow-1);transition:border-color var(--t),box-shadow var(--t),transform var(--t);
}
.notif-item:hover{border-color:var(--line-2);box-shadow:var(--shadow-2);transform:translateX(2px);color:var(--ink)}
.notif-item.hot{border-left-color:var(--critical)}
.notif-item.warn{border-left-color:var(--serious)}
.notif-item.info{border-left-color:var(--accent)}
.notif-dot{font-size:11px;flex-shrink:0}
.notif-text{flex:1;font-size:var(--fs-base);min-width:0}
.notif-go{font-size:var(--fs-sm);color:var(--accent);font-weight:620;flex-shrink:0;white-space:nowrap}

.task-list{display:flex;flex-direction:column;gap:var(--s2)}
.task-item{
  display:flex;align-items:center;gap:var(--s3);background:var(--surface);
  border:1px solid var(--line);border-left-width:3px;border-left-color:var(--line);
  border-radius:var(--r-md);padding:var(--s3) var(--s4);box-shadow:var(--shadow-1);
}
.task-item.overdue{border-left-color:var(--critical)}
.task-item.today{border-left-color:var(--warning)}
.task-item.done{opacity:.55}
.task-item.done .task-title{text-decoration:line-through}
/* The done control. A button rather than an 18px checkbox: it writes to the server on press, so it
   should look and read like an action, and it has to be hittable on a phone. Fixed width so a row
   does not reflow when "Complete" becomes "Reopen". */
.task-done{display:inline-flex;align-items:center;gap:6px;flex-shrink:0;width:112px;justify-content:center;
  padding:7px 10px;font-size:var(--fs-sm);font-weight:600;border-radius:8px;cursor:pointer;
  border:1px solid var(--accent);background:var(--accent);color:#fff}
.task-done:hover{filter:brightness(1.08)}
.task-done svg{width:15px;height:15px;flex-shrink:0}
.task-done.is-done{background:transparent;color:var(--muted);border-color:var(--line)}
.task-done.is-done:hover{color:var(--ink);border-color:var(--muted);filter:none}
@media (max-width:640px){.task-done{width:auto}.task-done span{display:none}}
.task-main{flex:1;min-width:0}
.task-title{font-size:var(--fs-base);font-weight:600}
.task-meta{font-size:var(--fs-sm);color:var(--muted);margin-top:2px}
.task-meta .overdue{color:var(--critical);font-weight:620}

/* connector cards */
.conn-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(310px,1fr));gap:var(--s4)}
.conn-card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s4);display:flex;flex-direction:column;gap:var(--s2);box-shadow:var(--shadow-1);
  min-width:0;overflow-wrap:anywhere;/* a model id or a URL in a description wraps instead of leaving the card */
  transition:border-color var(--t),box-shadow var(--t),transform var(--t);
}
.conn-card:hover{box-shadow:var(--shadow-2);transform:translateY(-2px)}
.conn-card.on{border-color:color-mix(in srgb,var(--good) 42%,var(--line))}
/* The head is a grid, not one flex row. As a flex row the nowrap pricing chip and the nowrap status
   badge refused to shrink, so the only item that could give — the min-width:0 title — was squeezed to
   ~15px and the connector's own NAME painted straight across the chip and the badge, at every width.
   Rows are explicit instead: icon + name + status on row 1 (the name wraps inside its own track),
   pricing on row 2 under the name. Nothing can be crushed into anything else. */
.conn-head{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;column-gap:var(--s3);row-gap:var(--s1);min-width:0;
}
.conn-head .conn-icon{grid-column:1;grid-row:1}
.conn-head .conn-title{
  grid-column:2;grid-row:1;font-weight:640;font-size:var(--fs-md);
  display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;min-width:0;
}
/* "auto-login"/"Soon" wrap onto the next line of the title rather than being squashed to an
   unreadable 20px stub — they are labels, not filler. */
.conn-head .conn-title .tag{flex:none;margin:0}
.conn-head .badge{grid-column:3;grid-row:1;justify-self:end}
.conn-head .conn-price{grid-column:2/-1;grid-row:2;justify-self:start;margin:0}
.conn-icon{
  display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;
  border-radius:var(--r-sm);background:#fff;border:1px solid var(--line);
  flex-shrink:0;overflow:hidden;padding:5px;
}
.conn-icon .conn-logo{width:100%;height:100%;object-fit:contain;display:block}
.conn-desc{color:var(--ink2);font-size:var(--fs-base);flex:1;margin:0;line-height:var(--lh-snug)}
/* Why the connector badge says what it says. Sits directly under the badge and above the
   description, because it is the only line on the card that changes what you do next. .bad uses
   --warning so it is theme-aware; both classes are asserted to exist, since a class styled by
   nothing is how this app has shipped invisible features before. */
.conn-health{color:var(--ink2);font-size:var(--fs-sm);margin:0 0 2px;line-height:var(--lh-snug)}
.conn-health.bad{color:var(--warning);font-weight:500}
/* inline-block, not the .tag inline-flex: text-overflow does nothing to a flex container's anonymous
   text item, so a long price used to be sliced off mid-glyph. The full terms stay in the title. */
/* Wraps rather than truncating: "Free tier · paid from ~…" was cutting off the one fact — the price —
   that the badge exists to show (audit, 2026-09-10). */
.conn-price{display:inline-block;max-width:100%;white-space:normal;overflow-wrap:anywhere;cursor:help}
.conn-tags{display:flex;flex-wrap:wrap;gap:var(--s1);margin:2px 0}
.conn-foot{
  display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  padding-top:var(--s3);border-top:1px solid var(--line);margin-top:auto;
}
.conn-foot button{margin-top:0}
.conn-foot .switch{margin:0 0 0 auto}
.conn-search{margin:0 0 var(--s4);position:relative;max-width:420px}
.conn-search input{max-width:420px;width:100%}
.conn-suggest{
  position:absolute;left:0;right:0;top:calc(100% + 5px);z-index:var(--z-pop);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:var(--shadow-2);overflow:hidden;padding:var(--s1);
}
.conn-suggest-row{display:flex;align-items:center;gap:var(--s2);padding:7px var(--s2);border-radius:var(--r-xs);cursor:pointer;min-width:0}
.conn-suggest-row:hover,.conn-suggest-row.active{background:var(--accent-soft)}
.conn-suggest .conn-icon{width:26px;height:26px;border-radius:var(--r-xs);padding:3px}
.conn-suggest-name{font-weight:600;font-size:var(--fs-base);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.conn-suggest-via{font-size:var(--fs-xs);color:var(--muted);flex-shrink:0}
.conn-suggest-hint{font-size:var(--fs-2xs);color:var(--muted);text-transform:lowercase;flex-shrink:0}
.conn-suggest-hint.ok{color:var(--good)}

/* collection cards */
/* align-items:start is what actually saves the space (2026-09-09). A grid stretches every item to
   the tallest in its row by default, so with cards collapsed each one still grew to the height of
   the "All leads" card beside it and the page was exactly as long as before. Collapsing the content
   without this is a change nobody can see. */
.coll-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(248px,1fr));gap:var(--s3);align-items:start}
.coll-card{
  display:flex;flex-direction:column;gap:6px;min-height:196px;padding:var(--s4);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);cursor:pointer;
  box-shadow:var(--shadow-1);transition:border-color var(--t),box-shadow var(--t),transform var(--t);
}
/* COLLAPSED BY DEFAULT (owner, 2026-09-09), for everyone.
   Scoped to .coll-collapsible, which only the LEADS collection cards carry. The Exhibitions page
   reuses .coll-card for its own cards and they have no .coll-more — dropping min-height on the base
   class would have shortened those too, which is a change nobody asked for on a page nobody was
   looking at. The min-height exists to keep a row of expanded cards aligned; on a collapsed one it
   would reserve four fifths of the height the collapse is meant to save. */
.coll-card.coll-collapsible:not(.open){min-height:0}
.coll-card:not(.open) .coll-more{display:none}
/* TWO LINES, and that is the fix (2026-09-09). All five items shared one row and four of them were
   flex-shrink:0, so inside a 248px grid column the NAME — the thing you scan for — was left about
   four characters: "Trading & Distribution Sharjah" read as "Trad in…" over two lines. Whether it
   wrapped then decided the card's height, which is what made a row of collapsed cards ragged.
   Name and chevron on the first line, the three facts on the second. Every collapsed card is now
   the same height by construction, so the band reads as a list instead of a broken mosaic. */
.coll-sum{display:flex;flex-direction:column;gap:3px;min-width:0}
.coll-sum-top{display:flex;align-items:flex-start;gap:var(--s2);min-width:0}
/* ONE line with an ellipsis, not a two-line clamp: a uniform height is worth more here than the
   extra half of a long generated name, and the full name is on the element's title either way. */
.coll-sum-top .coll-name{
  flex:1;min-width:0;display:block;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* The facts, in a fixed order down the column so the eye can run straight down them.
   A FIXED LINE BOX, because the point of this row is that every card is the same height and the
   avatar stack is taller than the text: without it the two shared cards measured 78px against 75px
   and the band was ragged again, for three pixels. A fixed height means nothing inside can change
   the card's shape — which is the whole reason the previous layout failed. */
.coll-sum-facts{display:flex;align-items:center;gap:var(--s2);min-width:0;flex-wrap:nowrap;height:20px}
.coll-sum-facts>*{align-self:center}
.coll-sum-n,.coll-sum-pct{font-size:var(--fs-xs);white-space:nowrap;flex-shrink:0}
.coll-sum-n b,.coll-sum-pct b{font-weight:680;color:var(--ink)}
/* The percentage is pushed to the right edge: it is the one fact worth comparing BETWEEN cards, and
   comparing a column of numbers needs them on a common edge. */
.coll-sum-pct{margin-left:auto}
.coll-sum-pct.none{opacity:.6}
/* A focus naming a service that has since been renamed away. Amber, not red: drafting still works,
   it is just wider than the owner asked for, and that is a thing to fix rather than a failure. */
/* The stack is the only thing allowed to shrink — a face is still recognisable clipped, a truncated
   number is not. */
.coll-sum-who{display:flex;align-items:center;min-width:0;overflow:hidden;flex-shrink:1}
/* The faces must not drive the row's height. At their normal size the two shared cards measured
   82px against 75px for every other one — ragged again, for seven pixels nobody asked for. Sized
   down to the text's own line box here only; the full-size stack stays wherever else it is used. */
.coll-sum-who .av-face{width:18px;height:18px;font-size:9px;line-height:18px}
/* .av-stack sets align-self:flex-start for the cards it was written for; in a fixed-height row that
   pushes it against the top edge instead of onto the text's baseline. */
.coll-sum-who .av-stack{align-self:center;min-height:0}
.coll-sum-who .av-more{height:18px;line-height:18px;font-size:9px}
/* "private" answers the same question the faces do, so it sits in the same slot at the same size. */
.coll-sum-priv{font-size:var(--fs-2xs);white-space:nowrap}
/* Auto draft, on a collapsed card. Colour-only emphasis from the accent vars, never a hex literal
   (house rule), and NO size change — the same restraint the full control is under.
   `approved` wears the warning tone instead: that is the setting under which mail leaves the
   building with nobody reading it, and it should not look like the harmless one. */
/* mid-drag: the card travels with the cursor; the ghost the browser paints is enough feedback,
   the source dims so the origin reads as a gap being re-decided. */
.coll-card.dragging{opacity:.45}
/* ---------- the Auto draft dialog (owner, 2026-09-09: "enhance the popup") ---------- */
.adraft-wiz{max-width:560px}
/* Wider than the auto-draft popup: this one wraps a row of service chips, and at 560px a five-service
   list broke into a ragged three lines. */
.svcf-wiz{max-width:640px}
.adraft-grid{display:grid;grid-template-columns:auto 1fr;gap:var(--s4);align-items:start;margin-top:var(--s3)}
.adraft-grid select{width:100%;min-height:36px}
@media (max-width:560px){.adraft-grid{grid-template-columns:1fr}}
.adraft-lbl{display:block;font-weight:600;margin-bottom:6px}
.adraft-seg .set-pill{display:inline-flex;align-items:center;gap:6px}
.adraft-seg .set-pill svg{width:14px;height:14px}
/* The two decisions as card rows: a bordered box each, the whole row clickable, and the approve
   one turns AMBER when ticked — it is the line between a queue somebody reads and mail leaving
   unattended, and colour is how this app says "this one is armed". */
.adraft-opt{
  display:flex;gap:10px;align-items:flex-start;margin-top:var(--s3);
  padding:10px 12px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface-2);cursor:pointer;line-height:1.45;
}
.adraft-opt:hover{border-color:var(--line-2);background:var(--surface)}
.adraft-opt input{margin-top:2px;flex-shrink:0}
.adraft-opt.warnable:has(:checked){background:var(--warning-soft);border-color:var(--warning)}
/* A BUTTON in both states now (owner, 2026-09-09): off = ordinary and quiet, click arms it;
   on = GREEN, click stops it. Colour is the state carrier and the label stays the same size —
   the house rule against size-changing active states. */
.coll-auto-mini{
  flex-shrink:0;display:inline-flex;align-items:center;gap:3px;
  padding:0 6px;height:18px;min-height:0;min-width:0;margin:0;border-radius:var(--r-full);
  font:inherit;font-size:var(--fs-2xs);font-weight:600;line-height:16px;white-space:nowrap;
  cursor:pointer;box-shadow:none;appearance:none;
  background:var(--surface-2);color:var(--muted);border:1px solid var(--line);
}
/* How many services this collection pitches — a themed circle beside the bolt. Quiet grey when the
   collection may pitch everything, accent when it is narrowed, critical-tinted when the narrow list
   names a service that no longer exists. */
.coll-svc-n{
  flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;min-height:0;min-width:0;padding:0;margin:0;border-radius:var(--r-full);
  font:inherit;font-size:var(--fs-2xs);font-weight:700;line-height:1;appearance:none;box-shadow:none;
  background:var(--surface-2);color:var(--muted);border:1px solid var(--line);cursor:pointer;
}
span.coll-svc-n{cursor:default}
.coll-svc-n.on{background:var(--accent-soft);color:var(--tint-ink);border-color:var(--accent)}
.coll-svc-n.bad{background:var(--critical-soft);color:var(--critical);border-color:var(--critical)}
button.coll-svc-n:hover{border-color:var(--accent);color:var(--accent)}
.coll-svc-n:active{transform:none}
/* Off is icon-only, so it squares up: the name owns the row and most collections are off. */
.coll-auto-mini.off{width:18px;padding:0;justify-content:center}
.coll-auto-mini.off:hover{border-color:var(--accent);color:var(--accent);background:var(--surface)}
.coll-auto-mini.on{background:var(--good-soft);color:var(--good);border-color:var(--good)}
.coll-auto-mini.on.approved{background:var(--warning-soft);color:var(--warning);border-color:var(--warning)}
.coll-auto-mini:active{transform:none}
.coll-exp{
  flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;
  width:24px;height:24px;padding:0;margin:0;min-height:0;
  /* Nudged up so its optical centre sits on the name's cap height rather than its line box. */
  margin-top:-1px;
  background:transparent;border:0;border-radius:var(--r-sm);color:var(--muted);cursor:pointer;
  transition:transform var(--t),color var(--t),background var(--t);
}
.coll-exp:hover{color:var(--ink);background:var(--surface-2)}
.coll-exp svg{width:16px;height:16px}
.coll-card.open .coll-exp{transform:rotate(180deg)}
.coll-exp:focus-visible{outline:none;box-shadow:var(--ring-focus)}
.coll-card:hover{border-color:color-mix(in srgb, var(--accent) 40%, var(--line));box-shadow:var(--shadow-2);transform:translateY(-2px)}
.coll-card:focus-visible{outline:none;border-color:var(--accent);box-shadow:var(--ring-focus)}
.coll-card.active{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft-2)}
.coll-top{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--s2);min-width:0}
/* Titles are clamped to two lines so a long generated name can't push the metrics
   out of alignment across the grid; the full name is on the element's title. */
.coll-name{
  font-weight:680;font-size:var(--fs-md);color:var(--ink);line-height:var(--lh-tight);
  letter-spacing:-.015em;min-width:0;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.coll-updated{font-size:var(--fs-2xs);white-space:nowrap}
.coll-industry{align-self:flex-start;background:var(--accent-soft);color:var(--tint-ink);border-color:transparent;font-weight:600;white-space:nowrap;margin:0;flex-shrink:0}
.coll-metrics{display:flex;align-items:baseline;gap:7px;flex-wrap:wrap;margin-top:2px}
.coll-total{font-size:32px;font-weight:700;color:var(--ink);line-height:1;font-variant-numeric:tabular-nums;letter-spacing:-.03em}
.coll-count-label{font-size:var(--fs-hint)}
.coll-year{font-size:var(--fs-xs);white-space:nowrap;margin-left:auto}
/* Collection card body: headline count, then coverage meters, then quality.
   (Replaces a flat 2-column grid of nine label/value pairs, where a progress figure,
   three coverage ratios and two metadata strings all looked identical.) */
.coll-src{margin-left:auto;text-transform:lowercase}
.coll-meters{display:flex;flex-direction:column;gap:5px;margin-top:var(--s3)}
.coll-meter{
  /* 78px, not 60: the widest label in use is "Conferences" on the All-events card, and a truncated
     meter label ("Conferen…") is worse than a slightly narrower bar. */
  display:grid;grid-template-columns:78px minmax(0,1fr) auto;gap:var(--s2);align-items:center;
  font-size:var(--fs-xs);color:var(--ink2);
}
.coll-meter .cm-l{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.coll-meter .cm-bar{height:5px;border-radius:var(--r-full);background:var(--surface-3);overflow:hidden}
.coll-meter .cm-bar i{display:block;height:100%;border-radius:var(--r-full);background:var(--accent);transition:width var(--t-slow) var(--ease)}
.coll-meter.good .cm-bar i{background:var(--good)}
.coll-meter .cm-v{font-variant-numeric:tabular-nums;font-weight:620;color:var(--ink);white-space:nowrap}
.coll-meter .cm-v .muted{font-weight:500}
.coll-quality{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);flex-wrap:wrap;
  margin-top:var(--s3);padding-top:var(--s2);border-top:1px solid var(--line-soft);font-size:var(--fs-xs);
}
.coll-q{display:inline-flex;align-items:center;gap:5px;color:var(--ink2);min-width:0}
.coll-q.on{color:color-mix(in srgb,var(--warning) 74%,var(--ink));font-weight:620}
.coll-next{margin-top:auto;padding-top:var(--s2);border-top:1px solid var(--line);display:flex;flex-direction:column;gap:1px;min-width:0}
.coll-next-label{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.07em;color:var(--muted);font-weight:640}
.coll-next-val{display:flex;flex-direction:column;min-width:0}
.coll-next-name{font-size:var(--fs-sm);font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.coll-next-date{font-size:var(--fs-xs);color:var(--ink2);font-variant-numeric:tabular-nums}
/* Date above, icon row below: with five actions the two no longer compete for one
   line and the last icon stops wrapping onto a row of its own. */
.coll-foot{display:flex;flex-direction:column;align-items:flex-start;gap:6px;margin-top:var(--s2)}
/* The date and the avatar stack share one line and wrap onto two when there are many faces — the
   "shared by" credit this replaced lived in the icon row below, which is nowrap, so it was clipped. */
.coll-meta{display:flex;align-items:center;flex-wrap:wrap;gap:6px;width:100%;min-width:0}
/* wrap, don't spill: nowrap dates from when this row held 4 actions — at 8, a narrow card's row
   ran straight through the card border (owner's screenshot, 2026-08-25). Two tidy rows beat that. */
.coll-foot .coll-actions{width:100%;flex-wrap:wrap;row-gap:2px}
.coll-actions{display:flex;gap:var(--s1)}
.coll-actions .small{padding:3px 9px;font-size:var(--fs-xs)}
/* The All-leads card is the aggregate, not one collection among many — so it gets its own
   treatment instead of the dashed grey it had, which read as de-emphasised (the same look as an
   archived card). Brand-tinted, with a solid accent edge on the left. */
.coll-card.all{
  align-items:stretch;justify-content:flex-start;gap:var(--s1);
  /* At rest it is a card like the others, told apart by its accent bar. The deep fill below is the
     SELECTED look and applies only when it is the one filtering the table (owner, 2026-09-10: with the
     fill on permanently, picking a collection left two cards lit and "all leads are highlighted"). */
  border-left:4px solid var(--accent-dk);
  padding-left:calc(var(--s4) - 3px);
}
.coll-card.all:hover{border-color:color-mix(in srgb,var(--accent) 52%,var(--line));border-left-color:var(--accent)}
.coll-card.all.active{
  /* Deliberately deeper than the per-collection tints (which sit at 9% of their own hue): this is
     the aggregate, and at anything near their strength it read as just another collection. */
  background:linear-gradient(160deg,color-mix(in srgb,var(--accent) 24%,var(--surface)) 0%,color-mix(in srgb,var(--accent) 14%,var(--surface)) 70%);
  border-color:color-mix(in srgb,var(--accent) 55%,var(--line));
  box-shadow:0 0 0 2px var(--accent-soft-2),var(--shadow-2);
}
.coll-card.all .coll-name{color:var(--tint-ink)}
.coll-card.all .coll-total{color:var(--tint-ink)}
.coll-card.all>*{min-width:0}
/* The split-by-collection list is secondary to the summary above it, so it folds away — the card
   leads with what the numbers are, and the breakdown is there when you want to know where they sit. */
.coll-split{margin-top:var(--s2);border-top:1px solid var(--line);padding-top:var(--s2)}
.coll-split>summary{
  list-style:none;cursor:pointer;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);display:inline-flex;align-items:center;gap:5px;user-select:none;
}
.coll-split>summary::-webkit-details-marker{display:none}
.coll-split>summary::before{content:"▸";transition:transform var(--t) var(--ease)}
.coll-split[open]>summary::before{transform:rotate(90deg)}
.coll-split>summary:hover{color:var(--ink2)}
.coll-split .coll-breakdown{margin-top:var(--s1);padding-top:0;border-top:0}
/* a per-row signal count inside the All-leads breakdown, so the block reconciles with the metric band */
.coll-breakdown .cb-sig{
  font-style:normal;font-size:var(--fs-2xs);font-weight:640;color:var(--warning);
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
/* rows without a signal count still need the middle track to exist, or their number shifts left */
.coll-breakdown>div>b:nth-child(2){grid-column:3}
/* (dark-mode override removed: --tint-ink is now a light teal that reads fine on --accent-soft,
   so the "all leads" card uses the same tint ink as every other tinted surface) */
/* Every collection card carries its own hue, derived from its name (hueOf) — the same categorical
   colour family as the industry chips, at fixed saturation and lightness so nothing shouts and
   nothing collides with the status palette. The All-leads card keeps the brand accent because it is
   the aggregate, not one collection among many. */
/* Calmer cards (owner, 2026-08-25: "all are colored, looks bad and very colorful"). At rest a card
   is neutral — surface, hairline, ink title — and its collection hue survives ONLY as the 3px left
   edge, which keeps each card recognisable without turning the row into a rainbow. The full hue
   treatment (wash, coloured title, hue ring) now belongs to the SELECTED card alone, so colour
   means "this is the one filtering the table", and the neutral siblings still read as fully alive
   thanks to the shared hover lift. */
.coll-card.tinted{
  --coll-c:hsl(var(--coll) 52% 46%);
  border-left:3px solid var(--coll-c);
  padding-left:calc(var(--s4) - 2px);
}
.coll-card.tinted.active{
  background:linear-gradient(160deg,color-mix(in srgb,var(--coll-c) 10%,var(--surface)) 0%,var(--surface) 60%);
  border-color:color-mix(in srgb,var(--coll-c) 40%,var(--line));border-left-color:var(--coll-c);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--coll-c) 22%,transparent),var(--shadow-2);
}
.coll-card.tinted.active .coll-name{color:color-mix(in srgb,var(--coll-c) 72%,var(--ink))}
.coll-card.tinted.active .coll-industry{background:color-mix(in srgb,var(--coll-c) 14%,transparent);color:color-mix(in srgb,var(--coll-c) 78%,var(--ink))}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .coll-card.tinted{--coll-c:hsl(var(--coll) 58% 62%)}
  :root:not([data-theme="light"]) .coll-card.tinted .coll-name{color:color-mix(in srgb,var(--coll-c) 86%,var(--ink))}
}
:root[data-theme="dark"] .coll-card.tinted{--coll-c:hsl(var(--coll) 58% 62%)}
:root[data-theme="dark"] .coll-card.tinted .coll-name{color:color-mix(in srgb,var(--coll-c) 86%,var(--ink))}

/* Avatar stack — who owns a collection/lead magnet and who it's shared with, on both the Leads
   and Lead Magnets pages. Same per-person hue as the industry/collection tints (hueOf keyed on
   username) so one person is the same colour everywhere. Overlap via negative margin, a ring in
   the card's own background so the stack reads as depth rather than a flat row, and a slight gap
   on hover — never a size change (house rule: no scaling hover/active states). */
/* Overrides the generic `button{}` reset (03 · BUTTONS): that rule centers content, adds a
   34px min-height and a shadow meant for a solid CTA, and — inside a column flex parent like
   .lm-card — stretches to the full card width. None of that belongs on an avatar cluster. */
.av-stack{display:inline-flex;align-items:center;justify-content:flex-start;align-self:flex-start;
  width:auto;min-height:0;border:0;background:none;box-shadow:none;padding:2px;margin:0;cursor:pointer;flex-shrink:0}
/* button:hover/:active (03 · BUTTONS) paint an accent background and scale-down on press — neither
   belongs here, and a size change on :active is the exact thing the house "no scale" rule bans. */
.av-stack:hover,.av-stack:active{background:none}
.av-stack:active{transform:none}
.av-face{
  /* 40%, not the 46% the collection tints use: this is the only place the hue carries WHITE TEXT on
     top of it, and at 46% the yellow-green sixth of the wheel drops under 2.5:1 — a face you cannot
     read is not an identity. Dark mode lifts it instead, for the same reason in the other direction. */
  --av-c:hsl(var(--av) 52% 40%);
  display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:50%;flex-shrink:0;position:relative;
  /* An orb, not a flat disc (owner's reference, 2026-08-25): lit from the upper left, deepening
     toward the rim. Derived from --av-c with color-mix so the two theme blocks below keep owning
     the one lightness decision — and the readable middle stays exactly the contrast-checked base. */
  background:radial-gradient(120% 120% at 32% 22%,
    color-mix(in srgb, var(--av-c) 70%, #fff) 0%,
    var(--av-c) 55%,
    color-mix(in srgb, var(--av-c) 80%, #000) 100%);
  color:#fff;font-size:9px;font-weight:700;
  box-shadow:var(--shadow-1);transition:margin var(--t-fast);
}
/* The surface ring and the overlap belong to STACKS only — they are what keeps overlapping faces
   apart. A face on its own (task owner, bug reporter, KPI row) wears no white border (owner,
   2026-08-25). */
/* No ring anywhere (owner, 2026-08-25, second pass: "no white borders... everywhere anywhere").
   Overlap shrinks so the distinct per-person hues do the separating that the ring used to. */
.av-stack .av-face{margin-left:-5px}
.av-face:first-child{margin-left:0}
.av-stack:hover .av-face{margin-left:-3px}
.av-stack:hover .av-face:first-child{margin-left:0}
.av-face.av-more{background:var(--surface-3);color:var(--ink2);font-size:8px}
/* the +N chip is a count, not a person — flat on purpose so it never reads as a seventh face */
.av-stack:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:50%}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .av-face{--av-c:hsl(var(--av) 55% 54%)}
}
:root[data-theme="dark"] .av-face{--av-c:hsl(var(--av) 55% 54%)}
/* the read-only "who can see this" list a stack (or its +N chip) opens */
.av-list{display:flex;flex-direction:column;gap:8px}
.av-row{display:flex;align-items:center;gap:10px}
.av-row .av-face{margin-left:0;width:28px;height:28px;font-size:11px;border-width:0;box-shadow:none}
.av-row-name{flex:1;min-width:0}

/* archived collections — collapsed, and visibly de-emphasised when open */
.coll-archive{margin-top:var(--s4);border-top:1px solid var(--line);padding-top:var(--s3)}
.coll-archive>summary{
  list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:6px;
  font-size:var(--fs-sm);font-weight:620;color:var(--ink2);user-select:none;
}
.coll-archive>summary::-webkit-details-marker{display:none}
.coll-archive>summary::before{content:"▸";color:var(--muted);transition:transform var(--t) var(--ease)}
.coll-archive[open]>summary::before{transform:rotate(90deg)}
.coll-archive>.hint{margin:var(--s1) 0 var(--s3)}
.coll-archive .coll-card{opacity:.72;background:var(--surface-2)}
.coll-archive .coll-card:hover{opacity:1}

/* =============================================================================
   13 · CHARTS & PROGRESS
   ========================================================================== */
.bar-row{display:grid;grid-template-columns:minmax(104px,148px) 1fr 48px;gap:var(--s3);align-items:center;margin-bottom:var(--s2);font-size:var(--fs-base)}
.bar-row .name{color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bar-track{height:20px;border-radius:var(--r-xs);background:var(--surface-3);position:relative;overflow:hidden}
/* A default fill colour matters: only the Dashboard pipeline sets one inline (per
   stage). Performance's lead funnel sets none, so without this every one of its
   bars painted transparent — a chart of empty tracks. */
.bar-fill{height:100%;border-radius:var(--r-xs);min-width:3px;background:var(--accent);transition:width var(--t-slow) var(--ease)}
.bar-row .val{text-align:right;font-variant-numeric:tabular-nums;color:var(--ink);font-weight:620}
/* The dashboard's pipeline rows are buttons now — a count you can read but not follow is a poster,
   not a tool. Button defaults are reset so they still look exactly like the rows they replaced;
   only hover, focus and the cursor say they are live. */
button.bar-row{
  width:100%;text-align:left;font:inherit;color:inherit;
  background:transparent;border:0;padding:var(--s1) var(--s2);margin-left:calc(var(--s2) * -1);
  border-radius:var(--r-sm);cursor:pointer;
  transition:background var(--t) var(--ease);
}
button.bar-row:hover{background:var(--surface-2)}
button.bar-row:hover .bar-track{background:var(--line-2)}
button.bar-row:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.bar-foot{margin-top:var(--s3);display:flex;align-items:center;gap:2px;flex-wrap:wrap;font-size:var(--fs-sm)}
/* A count rendered as prose that turns out to be clickable. Underlined on hover only, so the row
   still reads as the quiet footnote it is. */
.linkish{
  background:none;border:0;padding:0;font:inherit;color:var(--muted);cursor:pointer;
  border-radius:var(--r-xs);
}
.linkish:hover{color:var(--ink2);text-decoration:underline}
.linkish:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* The line under the Dashboard's tiles naming what could not be shown for one person. Pulled up
   against the band it belongs to, so it reads as that band's footnote and not as a stray paragraph. */
.own-skip{margin:calc(-1 * var(--s2)) 0 var(--s4);font-size:var(--fs-sm)}

/* The revenue-target band and the monthly bar chart were styled here. Deals came back on 2026-08-19
   WITHOUT targets or quota attainment — money lives in the owner's CRM — so none of these classes has
   been emitted by app.js since. ~45 lines, no markup. */

.step-flow{display:flex;gap:var(--s1);flex-wrap:wrap;margin:var(--s3) 0 var(--s4);align-items:center}
.step-flow span{
  background:var(--accent-soft);color:var(--tint-ink);border-radius:var(--r-sm);
  padding:6px 12px;font-size:var(--fs-sm);font-weight:600;
}
.step-flow.small span{font-size:var(--fs-xs);padding:5px 10px}

/* =============================================================================
   14 · OVERLAYS — dialog, wizard, toast, palette, progress
   ========================================================================== */
dialog{
  border:1px solid var(--line);border-radius:var(--r-xl);background:var(--surface);color:var(--ink);
  padding:var(--s6);width:min(580px,94vw);box-shadow:var(--shadow-3);
  /* TRANSFORM-CENTRED, deliberately not inset:0 + margin:auto (owner's Safari, 2026-09-09, twice):
     the auto-margin resolution of an over-constrained fixed box is where his Safari collapsed every
     dialog to a zero-tall sliver, and neither a valid max-height fallback nor an explicit
     fit-content height cured it. translate(-50%,-50%) centring has behaved identically in every
     Safari since 9 — nothing version-sensitive is left in this geometry. The entrance animation
     carries the same translate in BOTH keyframes, because transform:none at rest would throw the
     centring away. max-height stays two declarations: plain vh floor first, dvh refinement after. */
  position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);margin:0;
  max-height:92vh;max-height:min(92vh,92dvh);overflow:auto;
  animation:dlgIn var(--t-slow) var(--ease) both;
}
dialog::backdrop{background:var(--scrim);backdrop-filter:blur(8px) saturate(130%);animation:fadeIn var(--t-slow) ease both}
@keyframes dlgIn{from{opacity:0;transform:translate(-50%,calc(-50% + 10px)) scale(.97)}to{opacity:1;transform:translate(-50%,-50%)}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}

/* content-heavy popups (the form builder) ask for more room via dialog(html,'wide');
   'mid' is the half-step for previews that want a 600px email to breathe without going full-wide */
dialog.wizard.wide{width:min(940px,96vw)}
dialog.wizard.mid{width:min(680px,95vw)}
dialog.wizard{width:min(560px,95vw);padding:0;overflow:hidden;max-height:92vh;max-height:min(92vh,92dvh);display:flex;flex-direction:column}
.wiz-top{
  display:flex;align-items:center;gap:var(--s3);padding:var(--s4) var(--s5);
  border-bottom:1px solid var(--line);background:var(--surface-2);flex-shrink:0;
}
.wiz-name{font-weight:660;font-size:var(--fs-md);letter-spacing:-.015em}
.wiz-progress{display:flex;align-items:center;gap:var(--s1);margin-top:6px}
.wiz-dot{width:7px;height:7px;border-radius:50%;background:var(--line-2);transition:background var(--t),box-shadow var(--t)}
.wiz-dot.cur{background:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.wiz-dot.done{background:var(--good)}
.wiz-count{font-size:var(--fs-xs);color:var(--muted);margin-left:var(--s1);font-variant-numeric:tabular-nums}
/* ONE dismiss control for every overlay (2026-08-22). It was declared twice with contradictory
   values — a round grey disc at one rule, a hairline square at the next — and rendered a 12px glyph in
   a 30px box at 3.5:1 contrast, which is the "close button" the owner asked to see redesigned.
   It inks itself from `currentColor`, so the same rule works on a white panel and on the dark
   teal-green header bands without a second variant, and the ::after gives it a 44px pointer target
   without a 44px visual box. Red on hover is gone: closing a panel is not destructive, and red is
   this app's delete colour. */
.wiz-x,.bp-x{
  display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;min-height:32px;padding:0;flex-shrink:0;align-self:flex-start;
  border:0;border-radius:var(--r-sm);box-shadow:none;position:relative;
  background:color-mix(in srgb, currentColor 9%, transparent);
  color:inherit;opacity:.75;
  transition:background var(--t) var(--ease),opacity var(--t) var(--ease);
}
.wiz-x::after,.bp-x::after{content:"";position:absolute;inset:-6px}
.wiz-x:hover,.bp-x:hover{background:color-mix(in srgb, currentColor 18%, transparent);opacity:1}
/* A ring that survives both grounds: the outline takes the button's own ink, the halo is its inverse. */
.wiz-x:focus-visible,.bp-x:focus-visible{
  outline:2px solid currentColor;outline-offset:2px;opacity:1;
  box-shadow:0 0 0 4px color-mix(in srgb, currentColor 22%, transparent);
}
.wiz-head{padding:var(--s4) var(--s5) 0;display:flex;align-items:flex-start;gap:var(--s3)}
.wiz-head h3{font-size:var(--fs-lg);margin:0;display:flex;align-items:center;gap:var(--s2);min-width:0}
/* title + subtitle stack: every dialog with a one-line explainer wraps them in .wh-txt */
.wh-txt{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
/* No `order` any more: the button is the last child now, so the DOM order and the visual order agree
   and it is the last thing in the tab sequence rather than the first. */
.wiz-head .wiz-x,.wiz-top .wiz-x{margin-left:auto}
.wiz-steptitle{font-size:var(--fs-lg);font-weight:640;padding:var(--s4) var(--s5) var(--s1);letter-spacing:-.018em}
/* flex-BASIS AUTO, not the flex:1 shorthand (owner's Safari 26.6.2, 2026-09-10, reproduced against
   the real browser): flex:1 means flex-basis 0%, and in an AUTO-HEIGHT column container Safari 26
   resolves a basis-0, min-height-0 child to ZERO content height — the whole dialog collapsed to an
   18px sliver while every other engine leniently sized the container from content. basis:auto sizes
   the body from its content; when max-height clamps the dialog, shrink:1 + min-height:0 still let it
   compress and scroll. Measured before/after in Safari itself: 560x18 became 560x234. */
.wiz-body{padding:var(--s1) var(--s5) var(--s3);overflow-y:auto;flex:1 1 auto;min-height:0}
dialog:not(.wizard) .wiz-body{padding:0;overflow:visible}
.wiz-body p{color:var(--ink2);font-size:var(--fs-base);margin-bottom:var(--s3)}
.wiz-body form label{margin-bottom:var(--s3)}
.wiz-body .hint{margin:6px 0 0}
.wiz-ol{padding-left:var(--s5);margin:0 0 var(--s3);color:var(--ink2);font-size:var(--fs-base)}
.wiz-ol li{margin-bottom:6px}
.wiz-ol b,.wiz-body b{color:var(--ink);font-weight:620}
.wiz-live{
  display:flex;align-items:center;gap:var(--s2);background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:11px var(--s3);
  font-size:var(--fs-base);color:var(--ink2);margin-top:var(--s2);
}
/* The confirmation that survives the step it was earned on: which instance answered, kept in view
   while you finish the setup that depends on it. */
.wiz-ok{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 var(--s3);
  padding:var(--s2) var(--s3);border-radius:var(--r-sm);
  background:var(--good-soft);color:color-mix(in srgb,var(--good) 74%,var(--ink));
  font-size:var(--fs-sm);
}
.wiz-choice{
  display:block;width:100%;text-align:left;background:var(--surface);color:var(--ink);
  border:1px solid var(--line-2);border-radius:var(--r-md);padding:var(--s3) var(--s4);
  margin-bottom:var(--s2);font:inherit;box-shadow:none;min-height:0;justify-content:flex-start;
  transition:background var(--t-fast),border-color var(--t-fast),box-shadow var(--t-fast);
}
.wiz-choice:hover{background:var(--surface-2);border-color:color-mix(in srgb, var(--accent) 45%, var(--line-2))}
.wiz-choice.sel{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.wiz-choice b{display:inline-flex;align-items:center;gap:var(--s2);font-size:var(--fs-md);font-weight:620}
.wiz-choice .muted{display:block;margin-top:5px;font-size:var(--fs-sm)}
.wiz-choice:disabled{opacity:.45;cursor:not-allowed}
.wiz-choice:disabled:hover{background:var(--surface);border-color:var(--line-2)}
.wiz-opt{border:1px solid var(--line);border-radius:var(--r-md);padding:var(--s3) var(--s4);margin-bottom:var(--s3);background:var(--surface-2)}
.wiz-opt h4{font-size:var(--fs-base);margin-bottom:var(--s2);display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2)}
.wiz-opt .wiz-ol{margin-bottom:var(--s2)}
.wiz-group{border:1px solid var(--line);border-radius:var(--r-md);padding:var(--s3) var(--s4);margin-bottom:var(--s3);background:var(--surface-2)}
.wiz-group>label:last-child,.wiz-group>.formgrid:last-child{margin-bottom:0}
.wiz-group-h{font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin-bottom:var(--s2)}
/* A wiz-group that folds: the summary keeps the group heading's look and gains a caret. */
details.wiz-group>summary.wiz-group-h{cursor:pointer;list-style:none;display:flex;align-items:center;gap:6px}
details.wiz-group>summary.wiz-group-h::-webkit-details-marker{display:none}
details.wiz-group>summary.wiz-group-h::after{content:"";width:6px;height:6px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  transform:rotate(-45deg);transition:transform .15s ease;margin-left:auto;flex:none}
details.wiz-group[open]>summary.wiz-group-h::after{transform:rotate(45deg)}
details.wiz-group>summary.wiz-group-h:hover{color:var(--ink)}
details.wiz-group:not([open]){margin-bottom:var(--s2)}
.wiz-note{
  margin:0 var(--s5);padding:10px var(--s3);border-radius:var(--r-sm);font-size:var(--fs-base);
  background:var(--good-soft);color:color-mix(in srgb,var(--good) 72%,var(--ink));
  border:1px solid color-mix(in srgb,var(--good) 30%,transparent);
}
.wiz-note.err{background:var(--critical-soft);color:var(--critical);border-color:color-mix(in srgb,var(--critical) 30%,transparent)}
.wiz-foot{
  /* flex-end so a lone primary action sits right (conventional) instead of stranded on
     the left of a wide band; .wiz-foot-r's auto margin still splits two-part footers. */
  display:flex;align-items:center;justify-content:flex-end;gap:var(--s2);
  padding:var(--s3) var(--s5);border-top:1px solid var(--line);margin-top:var(--s3);
  background:var(--surface-2);flex-shrink:0;flex-wrap:wrap;
}
dialog:not(.wizard) .wiz-foot{margin:var(--s5) calc(var(--s6) * -1) calc(var(--s6) * -1);border-radius:0 0 var(--r-xl) var(--r-xl)}
.wiz-foot-l,.wiz-foot-r{display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap}
.wiz-foot-r{margin-left:auto}
.wiz-foot button{margin:0}

.copyrow{
  display:flex;align-items:center;gap:var(--s2);background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s2) var(--s3);margin:6px 0 var(--s3);
}
.copyrow-label{font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.07em;font-weight:640;flex-shrink:0}
/* min-height:0 made these 18px tall — they are click-to-select controls, so they get a
   real target height while staying visually borderless. */
.copyrow-val{flex:1;font-family:ui-monospace,Menlo,monospace;font-size:var(--fs-xs);word-break:break-all;background:transparent;border:0;padding:0;min-height:26px}

/* toast */
#toast{
  position:fixed;bottom:var(--s6);right:var(--s6);z-index:var(--z-toast);
  background:var(--n900);color:var(--n0);
  padding:11px var(--s5);border-radius:var(--r-md);font-size:var(--fs-base);font-weight:520;
  box-shadow:var(--shadow-3);max-width:min(440px,90vw);word-break:break-word;overflow-wrap:anywhere;
  opacity:0;transform:translateY(14px) scale(.96);
  transition:opacity var(--t) var(--ease),transform var(--t-slow) var(--ease-spring);
}
#toast.show{opacity:1;transform:none}

/* spinners & button loading dots */
.spinner{
  width:15px;height:15px;border:2px solid var(--line-2);border-top-color:var(--accent);
  border-radius:50%;animation:spin .7s linear infinite;flex-shrink:0;
}
@keyframes spin{to{transform:rotate(360deg)}}
/* Buttons load with a progressing CIRCLE (owner, 2026-08-26): an arc that spins while the work
   cannot count itself and fills 0-100% the moment it can (.known + dashoffset from btnLoadStart).
   [hidden] guard included: an explicit display always beats the hidden attribute. */
.btn-ringlet{display:inline-flex;margin-left:var(--s2);vertical-align:middle;width:16px;height:16px}
.btn-ringlet[hidden]{display:none}
.btn-ringlet svg{width:16px;height:16px;transform:rotate(-90deg)}
.btn-ringlet .rg-bg{fill:none;stroke:color-mix(in srgb,var(--accent) 18%,transparent);stroke-width:2.6}
.btn-ringlet .rg-fg{fill:none;stroke:var(--accent);stroke-width:2.6;stroke-linecap:round;transition:stroke-dashoffset var(--t-fast)}
.btn-ringlet:not(.known) svg{animation:rgSpin 1s linear infinite}
.btn-ringlet:not(.known) .rg-fg{stroke-dashoffset:30.6}   /* ~25% arc while indeterminate */
@keyframes rgSpin{to{transform:rotate(270deg)}}


/* =============================================================================
   15 · COMMAND PALETTE (⌘K)
   ========================================================================== */
.cmdk{
  border:0;background:transparent;padding:0;width:min(620px,94vw);max-height:none;overflow:visible;
  position:fixed;top:14vh;left:50%;transform:translateX(-50%);margin:0;box-shadow:none;
}
.cmdk::backdrop{background:var(--scrim);backdrop-filter:blur(8px) saturate(130%)}
.cmdk-panel{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-3);overflow:hidden;display:flex;flex-direction:column;max-height:70vh;
}
.cmdk-input-wrap{display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s4);border-bottom:1px solid var(--line)}
.cmdk-input-wrap svg{color:var(--muted);flex-shrink:0}
.cmdk-input{
  border:0;background:transparent;font-size:var(--fs-lg);padding:0;min-height:30px;
  color:var(--ink);width:100%;
}
/* The input's own ring is suppressed because the whole row reads as the field — but SOMETHING has
   to show focus, or a keyboard user opening ⌘K has no idea where they are. The wrapper takes it. */
.cmdk-input:focus{outline:none;box-shadow:none}
.cmdk-input-wrap:focus-within{background:var(--accent-soft)}
.cmdk-input-wrap:focus-within svg{color:var(--accent)}
.cmdk-list{overflow-y:auto;padding:var(--s1);flex:1;min-height:0}
.cmdk-group{font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);padding:var(--s3) var(--s3) var(--s1)}
.cmdk-item{
  display:flex;align-items:center;gap:var(--s3);padding:9px var(--s3);border-radius:var(--r-sm);
  cursor:pointer;color:var(--ink2);font-size:var(--fs-base);min-width:0;
}
.cmdk-item .cmdk-ic{display:inline-flex;color:var(--muted);flex-shrink:0}
.cmdk-item .cmdk-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:520}
.cmdk-item .cmdk-meta{font-size:var(--fs-xs);color:var(--muted);flex-shrink:0}
.cmdk-item[aria-selected="true"]{background:var(--accent-soft);color:var(--tint-ink)}
.cmdk-item[aria-selected="true"] .cmdk-ic,.cmdk-item[aria-selected="true"] .cmdk-meta{color:var(--accent)}
.cmdk-empty{padding:var(--s6) var(--s4);text-align:center;color:var(--muted);font-size:var(--fs-base)}
.cmdk-foot{
  display:flex;align-items:center;gap:var(--s4);padding:var(--s2) var(--s4);
  border-top:1px solid var(--line);background:var(--surface-2);font-size:var(--fs-xs);color:var(--muted);flex-wrap:wrap;
}
.cmdk-foot span{display:inline-flex;align-items:center;gap:5px}

/* =============================================================================
   16 · STATES — empty, loading skeleton, error
   ========================================================================== */
.empty-state{
  text-align:center;padding:var(--s8) var(--s4);
  display:flex;flex-direction:column;align-items:center;gap:var(--s2);
}
.empty-state .empty-ic{
  width:46px;height:46px;border-radius:var(--r-md);display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-3);color:var(--muted);margin-bottom:var(--s1);
}
.empty-state h4{font-size:var(--fs-md);font-weight:620}
.empty-state p{margin:0 0 var(--s3);font-size:var(--fs-base);color:var(--muted);max-width:52ch}
.empty-state>div{display:flex;gap:var(--s2);justify-content:center;flex-wrap:wrap}

/* skeleton loader */
.skel{display:flex;flex-direction:column;gap:var(--s3)}
.skel-line,.skel-block,.skel-tile{
  background:linear-gradient(90deg,var(--surface-3) 25%,color-mix(in srgb,var(--ink) 7%,var(--surface-3)) 37%,var(--surface-3) 63%);
  background-size:400% 100%;animation:shimmer 1.4s ease infinite;border-radius:var(--r-sm);
}
.skel-line{height:12px}
.skel-line.w40{width:40%} .skel-line.w60{width:60%} .skel-line.w25{width:25%}
.skel-tile{height:96px;border-radius:var(--r-lg)}
.skel-block{height:220px;border-radius:var(--r-lg)}
.skel-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));gap:var(--s3)}
@keyframes shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}

/* =============================================================================
   17 · PAGE MODULES
   ========================================================================== */
/* — login — */
.login-wrap{
  min-height:100vh;min-height:100dvh;display:grid;grid-template-columns:1fr;
  place-items:center;padding:var(--s5);
  /* The wordmark's own ramp — deep navy into teal — as the entry-hall backdrop. */
  background:
    radial-gradient(1100px 620px at 12% -10%, color-mix(in srgb, var(--brand-500) 26%, transparent), transparent 60%),
    radial-gradient(900px 520px at 100% 108%, color-mix(in srgb, var(--brand-700) 22%, transparent), transparent 60%),
    linear-gradient(160deg, color-mix(in srgb, var(--brand-900) 10%, var(--page)), var(--page) 55%);
}
.login-card{
  /* Glass card: translucent surface + blur, both themes. Falls back to the solid surface where
     backdrop-filter is unsupported — the border and shadow carry the shape either way. */
  background:color-mix(in srgb, var(--surface) 78%, transparent);
  -webkit-backdrop-filter:blur(14px) saturate(1.15);backdrop-filter:blur(14px) saturate(1.15);
  border:1px solid color-mix(in srgb, var(--line) 65%, transparent);border-radius:var(--r-xl);
  padding:var(--s7);width:min(390px,100%);
  box-shadow:var(--shadow-3), inset 0 1px 0 rgba(255,255,255,.09);
}
@supports not (backdrop-filter: blur(1px)){.login-card{background:var(--surface)}}
.login-card h1{font-size:var(--fs-2xl);margin-bottom:var(--s1);letter-spacing:-.028em}
.login-card h1 span{
  display:block;font-size:var(--fs-xs);font-weight:660;color:var(--accent);
  letter-spacing:.13em;text-transform:uppercase;margin-top:6px;
}
.login-card .login-sub{font-size:var(--fs-base);color:var(--muted);margin-bottom:var(--s5)}
.login-card label{margin-bottom:var(--s4)}
.login-card input{min-height:38px}
.login-card button[type=submit]{width:100%;margin-top:var(--s1);padding:10px;min-height:40px;font-size:var(--fs-md)}
.login-card .remember{
  display:flex;flex-direction:row;align-items:center;gap:var(--s2);
  font-size:var(--fs-base);margin:var(--s1) 0 var(--s4);cursor:pointer;color:var(--ink2);
}
.login-card .remember input{width:auto;margin:0}
.login-foot{margin-top:var(--s5);padding-top:var(--s4);border-top:1px solid var(--line);font-size:var(--fs-hint);color:var(--muted);text-align:center}

/* — playbook — */
.playbook h3{margin:var(--s6) 0 var(--s2);font-size:var(--fs-lg);letter-spacing:-.018em}
.playbook p,.playbook li{color:var(--ink2);font-size:var(--fs-base);margin-bottom:6px;line-height:var(--lh)}
.playbook ol,.playbook ul{padding-left:var(--s5);margin-bottom:var(--s3)}
.playbook strong{color:var(--ink);font-weight:620}
.pb-toc{
  font-size:var(--fs-sm);line-height:2;background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:var(--s3) var(--s4);margin:var(--s3) 0 var(--s5);
  columns:14em;column-gap:var(--s6);
}
.pb-toc a{text-decoration:none;display:block;break-inside:avoid}
.pb-toc a:hover{text-decoration:underline}

/* — Business Network — */
.net-opts{display:flex;flex-direction:column;gap:var(--s2);margin-bottom:var(--s1)}
.net-opts .wiz-choice{margin-bottom:0}
/* nowrap: with the expander, star and avatar all in front of the name, this column wrapped the
   chevron onto its own line at table widths */
.net-org{display:flex;align-items:flex-start;gap:var(--s2);min-width:0;flex-wrap:nowrap}
#netTable th:first-child,#netTable td:first-child{min-width:264px}
.net-org-t{min-width:0}
.net-av{
  flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;
  border-radius:var(--r-sm);background:var(--accent-soft);color:var(--tint-ink);
  font-size:var(--fs-sm);font-weight:700;letter-spacing:.02em;
}
.net-loc{font-size:var(--fs-xs);margin-top:1px}
.net-why{font-size:var(--fs-hint);color:var(--ink2);margin-top:3px;max-width:360px}
.net-exp{
  background:transparent;color:var(--muted);border:0;box-shadow:none;padding:6px var(--s2) 6px var(--s1);
  /* top-aligned with the star and avatar: centred, it floated away from them on a three-line row */
  font-size:var(--fs-base);line-height:1;align-self:flex-start;margin-top:8px;flex-shrink:0;min-height:0;
}
.net-exp:hover{color:var(--accent);background:transparent}
/* the disclosure control is a real chevron now; it rotates rather than swapping glyphs */
.net-exp>svg{width:13px;height:13px;display:block;transition:transform var(--t) var(--ease)}
.net-exp[aria-expanded="true"]>svg{transform:rotate(90deg)}
.net-apply{font-weight:600;font-size:var(--fs-sm);white-space:nowrap;text-decoration:none}
.net-apply:hover{text-decoration:underline}
.net-quick{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;margin-bottom:var(--s3)}
.net-goalfilter{display:flex;align-items:center;gap:5px;flex-wrap:wrap}
.net-goalchip{cursor:pointer}
.net-goalchip.on{background:var(--accent-soft);color:var(--tint-ink);border-color:transparent;font-weight:600}
.net-detail{padding:var(--s4) var(--s5) var(--s5) 56px}
.net-detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:var(--s3) var(--s5);margin-bottom:var(--s3)}
.net-detail p{font-size:var(--fs-base);color:var(--ink2);margin:2px 0 0}
.net-dl{font-size:var(--fs-2xs);font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin-top:var(--s1)}
.net-steps{margin:var(--s1) 0 var(--s3);padding-left:var(--s5);font-size:var(--fs-base);color:var(--ink2)}
.net-steps li{margin-bottom:3px}
.net-why-full{font-size:var(--fs-base);color:var(--ink2);margin:6px 0}
.net-why-full strong{color:var(--ink)}
.net-detail-foot{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;margin-top:var(--s3);padding-top:var(--s3);border-top:1px solid var(--line);font-size:var(--fs-hint)}
.net-future-sum{cursor:pointer;font-weight:640;font-size:var(--fs-md)}
.net-future-list{display:flex;flex-direction:column;gap:var(--s2)}
.net-future-item{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s3);border:1px solid var(--line);border-radius:var(--r-md);padding:var(--s3) var(--s4);background:var(--surface-2)}
.net-needed{font-size:var(--fs-sm);color:var(--serious);font-weight:600;margin-top:var(--s1)}

/* — Find Leads panel / wizard — */
.mlf-foot{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;margin-top:var(--s5);padding-top:var(--s4);border-top:1px solid var(--line)}
.mlf-foot .spacer{flex:1}
.mlf-foot .hint{font-size:var(--fs-hint);color:var(--muted)}
@keyframes flwIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.flw-step,.flw-reveal{animation:flwIn var(--t) var(--ease)}

/* Step rail — replaced three anonymous progress dots. A dot cannot say which step you are on, what
   that step is called, or take you back to it; a named, clickable rail does all three. */
.flw-rail{display:flex;gap:var(--s1);margin:var(--s2) 0 var(--s4);flex-wrap:wrap}
.flw-rail-step{
  display:inline-flex;align-items:center;gap:var(--s2);margin:0;padding:5px 12px 5px 6px;
  font-size:var(--fs-sm);font-weight:560;line-height:1.2;color:var(--muted);background:transparent;
  border:1px solid transparent;border-radius:var(--r-full);cursor:pointer;min-height:0;
  transition:color var(--t-fast),background var(--t-fast),border-color var(--t-fast);
}
.flw-rail-step i{
  display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;flex:0 0 20px;
  border-radius:var(--r-full);background:var(--surface-3);color:var(--muted);
  font-size:var(--fs-2xs);font-weight:700;font-style:normal;
}
.flw-rail-step:hover{color:var(--ink);background:var(--surface-3)}
.flw-rail-step.done i{background:var(--accent-soft-2);color:var(--tint-ink)}
.flw-rail-step.on{color:var(--tint-ink);background:var(--accent-soft);border-color:var(--accent)}
.flw-rail-step.on i{background:var(--accent);color:var(--ink-invert)}

.flw-h{font-size:var(--fs-xl);font-weight:660;color:var(--ink);margin:0;line-height:var(--lh-tight)}
.flw-q{font-weight:640;font-size:var(--fs-base);margin:var(--s5) 0 var(--s2)}
.flw-q .muted{font-weight:450}
.flw-reveal>.flw-q:first-child{margin-top:var(--s4)}

/* Searchable multi-select — one control for every long list in the wizard. The panel opens INLINE
   rather than as an absolute dropdown: the dialog body scrolls, and an absolute panel holding a
   hundred job titles is clipped at exactly the fold. */
.flw-pick .dd{display:block}
.flw-pick .dd summary{width:100%;justify-content:space-between;white-space:normal}
.flw-pick .dd-panel{
  position:static;width:auto;min-width:0;max-height:none;overflow:visible;
  box-shadow:none;background:var(--surface-2);margin-top:6px;padding:var(--s2);
}
.flw-pick-q{margin:0 0 var(--s1);width:100%}
.flw-pick-list{max-height:216px;overflow-y:auto;overscroll-behavior:contain}
.flw-pick-none{font-size:var(--fs-sm);color:var(--muted);margin:var(--s2) 0 0;padding:0 var(--s2)}
.flw-picked{margin-top:var(--s2)}
.flw-picked:empty{display:none}
.flw-chip{
  display:inline-flex;align-items:center;gap:6px;margin:0;padding:4px 9px 4px 11px;min-height:0;
  font-size:var(--fs-sm);font-weight:560;line-height:1.3;color:var(--tint-ink);background:var(--accent-soft);
  border:1px solid var(--accent-soft-2);border-radius:var(--r-full);cursor:pointer;
  transition:background var(--t-fast),border-color var(--t-fast),color var(--t-fast);
}
.flw-chip span{color:var(--muted);font-size:var(--fs-xs)}
.flw-chip:hover{background:var(--critical-soft);border-color:var(--critical);color:var(--critical)}
.flw-chip:hover span{color:var(--critical)}

/* The recap, on screen on every step, so "Generate leads" is never a guess — and so the ICP
   fallbacks are visible: an empty field means "your whole saved ICP", not "no filter". */
.flw-sum{
  margin-top:var(--s5);padding:var(--s3) var(--s4);background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-md);
}
.flw-sum-h{font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin-bottom:var(--s2)}
.flw-sum-row{display:flex;gap:var(--s3);font-size:var(--fs-sm);line-height:var(--lh-snug);padding:2px 0}
.flw-sum-row span{flex:0 0 84px;color:var(--muted)}
.flw-sum-row b{color:var(--ink);font-weight:560;min-width:0;overflow-wrap:anywhere}
.flw-cost{font-size:var(--fs-hint);color:var(--muted);line-height:var(--lh-snug);margin:var(--s2) 0 0}

/* The per-step guard. Plain red body text read as prose and was missed; and .err's reserved
   min-height left a blank gap on every step that had nothing to say. */
#flwErr:empty{display:none}
#flwErr:not(:empty){
  margin-top:var(--s3);padding:8px 11px;font-weight:560;min-height:0;
  background:var(--critical-soft);border:1px solid color-mix(in srgb,var(--critical) 30%,transparent);
  border-radius:var(--r-sm);
}
@media (max-width:620px){
  .flw-sum-row{flex-direction:column;gap:0}
  .flw-sum-row span{flex:none}
}

/* — compact icon actions (collection cards) —
   Square icon buttons instead of a row of text buttons: the same four actions in
   roughly a third of the width, so the card footer stays on one line. Every one
   carries a title + aria-label, since the glyph alone isn't a label. */
.icon-act{
  --act-ink:var(--muted);
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;min-height:30px;padding:0;margin:0;flex-shrink:0;
  /* Quiet at rest (owner, 2026-08-25: a row of eight bordered grey squares read as clutter):
     no box until the pointer arrives, then a soft surface + a hairline. Size never changes. */
  background:transparent;color:var(--act-ink);border:1px solid transparent;
  border-radius:var(--r-md);box-shadow:none;cursor:pointer;
  transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast);
}
.icon-act svg{width:15px;height:15px;display:block;pointer-events:none}
.icon-act:hover{background:var(--surface-3);color:var(--ink);border-color:var(--line)}
.icon-act.danger{background:transparent;border-color:transparent}
.icon-act.danger:hover{background:var(--critical-soft);color:var(--critical);border-color:color-mix(in srgb,var(--critical) 34%,transparent)}
.icon-act:focus-within,.icon-act:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* toggle variant — the checkbox is the control, the box is the affordance */
.icon-act.toggle input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.icon-act.toggle:has(input:checked){background:var(--accent-soft);color:var(--tint-ink);border-color:var(--accent)}
/* "on" variant — same lit state the toggle gets, for an action button that HOLDS a setting
   rather than flipping one. Sharing with named people is a dialog, not a checkbox, so without this
   a collection shared with three people looked identical to one shared with nobody. */
.icon-act.on{background:var(--accent-soft);color:var(--tint-ink);border-color:var(--accent)}
/* The count rides on the corner opposite the card's own controls. aria-hidden in the markup: the
   number is already spelled out in the button's aria-label, and read twice it is just noise. */
.icon-act-n{
  position:absolute;right:-2px;bottom:-2px;min-width:14px;height:14px;padding:0 3px;
  display:inline-flex;align-items:center;justify-content:center;
  font-size:9px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;
  background:var(--accent);color:var(--ink-invert);border-radius:var(--r-full);
  border:1.5px solid var(--surface);
}
/* select variant — icon shows, the native select sits invisibly on top so it stays a real select */
/* Assign is an ICON, like every other action on this card. It used to carry a visible select
   showing "Assign…", which made one action twice the width of the rest and pushed the row of them
   out of the card. The select is still a real <select> — it is stretched invisibly over the icon,
   so clicking anywhere on the button opens the native list of people. Keyboard and screen readers
   get the select; the eye gets the icon. */
.icon-act.select{width:auto;padding:0;gap:0;position:relative;overflow:hidden}
.icon-act.select svg{margin:0 7px}
.icon-act.select select{
  position:absolute;inset:0;width:100%;height:100%;min-width:0;min-height:0;padding:0;margin:0;
  border:0;background:none;box-shadow:none;color:transparent;
  opacity:0;cursor:pointer;appearance:none;-webkit-appearance:none;
}
/* Focus has to be visible on the BUTTON, since the control carrying it is invisible. */
.icon-act.select:focus-within{background:var(--surface-3);border-color:var(--accent);box-shadow:var(--ring-focus)}
.icon-act.select select:focus{box-shadow:none;outline:none}
/* Every action stays inside the card: one row that wraps within the block rather than spilling. */
/* Auto draft is a LABELLED control on the meta line, never another .icon-act in the utility row.
   It was one, eighth in a row of near-identical squares that are transparent at rest, and the owner
   could not find it. Findability was the smaller half: the other controls on that row are view,
   rename, share and archive, all undoable, while this one arms a loop that writes rows as approved,
   which on email is what the auto-send loop drains with nobody between the click and the send.
   Armed state is a COLOUR CHANGE ONLY (owner, 2026-08-15: no size change, no ring, no animation),
   and the colours come from the accent vars rather than a literal: the house accent has ONE source
   (lib/brand.js) because four copies of it had drifted before that module existed. */
.coll-auto{
  display:inline-flex;align-items:center;gap:6px;max-width:100%;
  padding:1px 8px;border:1px solid var(--line);border-radius:var(--r-full);
  background:transparent;color:var(--muted);cursor:pointer;
  font-size:var(--fs-xs);font-weight:520;line-height:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast);
}
.coll-auto:hover{color:var(--ink);border-color:var(--line)}
/* Armed. Colour only. */
.coll-auto.on{background:var(--accent-soft);color:var(--tint-ink);border-color:var(--accent);cursor:default}
.coll-auto-stop{
  background:transparent;border:0;padding:0;margin:0;min-height:0;box-shadow:none;
  font:inherit;color:inherit;text-decoration:underline;cursor:pointer;
}
.coll-auto-stop:hover{background:transparent;opacity:.75}
/* WHAT a collection pitches, beside WHETHER it auto-drafts. Deliberately the same shape, size and
   resting colour as .coll-auto: they are one pair of decisions on one line, and a control that looks
   different reads as a different KIND of thing. Narrowed is the accent tint, exactly as armed is. */
.coll-svc{
  display:inline-flex;align-items:center;gap:6px;max-width:100%;
  padding:1px 8px;border:1px solid var(--line);border-radius:var(--r-full);
  background:transparent;color:var(--muted);cursor:pointer;
  font-size:var(--fs-xs);font-weight:520;line-height:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast);
}
.coll-svc:hover{color:var(--ink);border-color:var(--accent)}
.coll-svc.on{background:var(--accent-soft);color:var(--tint-ink);border-color:var(--accent)}
/* Documents on a collection: chips shaped like .coll-svc, each a download, with an × for the owner. */
.coll-docs{display:inline-flex;align-items:center;gap:4px;flex-wrap:wrap;max-width:100%;min-width:0}
.coll-doc{display:inline-flex;align-items:center;gap:2px;max-width:100%;padding:1px 4px 1px 8px;border:1px solid var(--line);border-radius:var(--r-full);background:var(--surface-2);font-size:var(--fs-xs);font-weight:520;line-height:18px;white-space:nowrap}
.coll-doc a{color:var(--ink2);text-decoration:none;overflow:hidden;text-overflow:ellipsis}
.coll-doc a:hover{color:var(--accent)}
.coll-doc-x{border:0;background:transparent;color:var(--muted);cursor:pointer;padding:0 4px;line-height:18px;font-size:var(--fs-sm)}
.coll-doc-x:hover{color:var(--critical)}
.coll-doc-add{display:inline-flex;align-items:center;padding:1px 8px;border:1px dashed var(--line);border-radius:var(--r-full);background:transparent;color:var(--muted);cursor:pointer;font-size:var(--fs-xs);font-weight:520;line-height:18px;white-space:nowrap}
.coll-doc-add:hover{color:var(--accent);border-color:var(--accent)}
.coll-svc.bad{background:var(--warning-soft);color:var(--warning);border-color:var(--warning)}
/* A borrowed collection's focus is a fact, not a control — it renders as a <span>, which must not
   offer a pointer or a hover. */
span.coll-svc{cursor:default}
span.coll-svc:hover{color:var(--muted);border-color:var(--line)}
span.coll-svc.on:hover{color:var(--tint-ink);border-color:var(--accent)}

/* The picker. Chips of the company's own services, wrapping — there may be two or ten of them. */
/* The deploy bar: fixed, slim, bottom-centre — visible from any page without covering the one
   control somebody is about to press. Tokens only, both themes. */
#verBar{
  position:fixed;left:50%;bottom:18px;transform:translateX(-50%);z-index:9999;
  display:flex;align-items:center;gap:10px;padding:8px 10px 8px 14px;
  background:var(--ink);color:var(--surface);border-radius:var(--r-full);
  box-shadow:var(--shadow-2, 0 6px 24px rgba(0,0,0,.25));font-size:var(--fs-sm);white-space:nowrap;
}
#verBar button{min-height:28px;padding:3px 12px;font-size:var(--fs-sm)}
#verBar button.ghost{background:transparent;color:var(--surface);border-color:color-mix(in srgb,var(--surface) 40%,transparent)}
/* The two shelves: a small caps heading over each group's grid. */
.svcf-sec{margin-top:var(--s3)}
.svcf-h{margin:0 0 2px;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
/* A uniform two-column grid, not a ragged wrap (owner, 2026-09-09: "enhance the popup"): every
   chip the same width, scanning down a column instead of hunting across rows of uneven pills. */
.svcf-chips{display:grid;grid-template-columns:1fr 1fr;gap:var(--s2);margin:var(--s3) 0}
@media (max-width:560px){.svcf-chips{grid-template-columns:1fr}}
.svcf-chip{
  display:flex;align-items:center;gap:9px;cursor:pointer;
  padding:9px 12px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface-2);font-size:var(--fs-sm);line-height:1.3;
  transition:background var(--t-fast),border-color var(--t-fast),color var(--t-fast);
}
.svcf-chip:hover{border-color:var(--accent)}
/* Ticked is a COLOUR change and nothing else — no size change, no weight jump, the house rule the
   auto-draft control is under. :has() so the whole chip lights rather than only its label. */
.svcf-chip:has(input:checked){background:var(--accent-soft);color:var(--tint-ink);border-color:var(--accent)}
.svcf-chip input{margin:0;flex-shrink:0}
.svcf-chip:focus-within{outline:2px solid var(--accent);outline-offset:2px}
.coll-actions{display:flex;gap:5px;align-items:center;flex-wrap:wrap;justify-content:flex-end;min-width:0;max-width:100%}

/* — collapsible "data sources" strip —
   Was a permanently-open row of up to 14 connector chips sitting above the data on
   several pages; now one line that opens on demand. */
/* Out of the page flow entirely (owner, 2026-08-25): the pill used to open a page's content and
   push everything down a row on every connector page. Fixed bottom-LEFT — the bug FAB owns the
   bottom-right — and the chip panel opens UPWARD from the pill, so open or closed, no page layout
   ever moves. Pages keep rendering it wherever they always did; position:fixed ignores that. */
/* left of the CONTENT column, not the viewport: at 18px it sat on top of the sidebar's footer.
   --side-w is scoped on #app and flips with rail mode, so the pill tracks the collapse live. */
details.poweredby{position:fixed;left:calc(var(--side-w, 0px) + 18px);bottom:18px;z-index:var(--z-pop);margin:0}
details.poweredby>summary{
  list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:6px;
  font-size:var(--fs-sm);color:var(--ink2);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-full);padding:4px 13px;user-select:none;
  box-shadow:var(--shadow-2);
}
details.poweredby>summary::-webkit-details-marker{display:none}
details.poweredby>summary::after{content:"▾";color:var(--muted);font-size:10px;transition:transform var(--t) var(--ease)}
details.poweredby[open]>summary::after{transform:rotate(180deg)}
details.poweredby>summary:hover{background:var(--surface-3);border-color:var(--line-2)}
.poweredby-chips{
  position:absolute;left:0;bottom:calc(100% + 8px);width:max-content;max-width:min(420px,calc(100vw - 36px));
  display:flex;align-items:center;flex-wrap:wrap;gap:5px;margin:0;padding:10px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--shadow-3);
  transform-origin:bottom left;animation:bugPopIn var(--t-slow) var(--ease-spring) both;
}
/* phones: clear the bottom nav, same lift as the bug FAB opposite it */
@media (max-width:640px){
  details.poweredby{left:12px;bottom:74px}
}

/* — Leads filter bar: filters and saved views on one row instead of two — */
.leadbar{align-items:center}
.leadbar-sep{width:1px;align-self:stretch;min-height:26px;background:var(--line);margin:0 var(--s1)}
.savedviews{display:inline-flex;align-items:center;gap:5px;flex-wrap:wrap;font-size:var(--fs-sm)}
.savedviews .muted{font-size:var(--fs-xs)}
@media (max-width:900px){.leadbar-sep{display:none}.savedviews{width:100%}}


/* — company brief bubble (Leads 🏢) —
   position:fixed and appended to <body>: the leads table scrolls horizontally, so a
   popover anchored inside it would drift away from its button. */
.brief-pop{
  position:absolute;z-index:var(--z-pop);width:min(380px,calc(100vw - 20px));
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-3);display:flex;flex-direction:column;
  max-height:none;
  transform-origin:var(--caret-x,50%) top;animation:notifPopIn var(--t-slow) var(--ease-spring) both;
}
/* The cloud's tail: a rotated square on the edge facing the button, at the button's centre. The
   head band is dark, so the tail below the button is dark too; above the button it is the surface. */
.brief-pop::before{
  content:"";position:absolute;left:var(--caret-x,50%);top:-7px;width:14px;height:14px;
  transform:translateX(-50%) rotate(45deg);background:var(--brand-800);border:1px solid var(--line);
  border-right:0;border-bottom:0;border-radius:2px 0 0 0;
}
.brief-pop.above{transform-origin:var(--caret-x,50%) bottom}
.brief-pop.above::before{top:auto;bottom:-7px;background:var(--surface);border:1px solid var(--line);border-left:0;border-top:0;border-radius:0 0 2px 0}
.brief-pop.no-caret::before{display:none}
.brief-pop.wide{width:min(920px,calc(100vw - 20px))}
.zh-sum{margin:0 0 var(--s2);font-size:var(--fs-sm);color:var(--ink2)}
/* The table FITS the bubble — no sideways scrolling (owner, 2026-09-11). Fixed layout so the columns
   share the width, and the text cells wrap instead of running off; only quote, date, status and total
   stay on one line. */
.brief-pop.wide .table-wrap{overflow-x:visible}
.zh-table{table-layout:fixed;width:100%}
.zh-table td,.zh-table th{font-size:var(--fs-xs);padding:6px 8px;vertical-align:top;white-space:normal;overflow-wrap:anywhere}
.zh-table th{overflow:visible;text-overflow:clip;line-height:1.25}
.zh-table th:nth-child(1),.zh-table td:nth-child(1){width:14%}
.zh-table th:nth-child(2),.zh-table td:nth-child(2){width:10%}
.zh-table th:nth-child(3),.zh-table td:nth-child(3){width:9%}
.zh-table th:nth-child(4),.zh-table td:nth-child(4){width:12%}
.zh-table th:nth-child(5),.zh-table td:nth-child(5){width:13%}
.zh-table th.num,.zh-table td.num{width:12%}
.zh-table td.nowrap{white-space:nowrap}
.zh-table .num{text-align:right}
.zh-subj{white-space:normal;overflow-wrap:anywhere}
.zh-stats{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 var(--s3)}
.zh-stat{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border:1px solid var(--line);border-radius:var(--r-full);background:var(--surface-2);font-size:var(--fs-xs)}
.zh-stat .zh-stat-k{text-transform:capitalize;color:var(--muted)}
.zh-stat.zh-accepted,.zh-stat.zh-invoiced{border-color:var(--good);background:var(--good-soft)}
.zh-stat.zh-declined,.zh-stat.zh-expired{border-color:var(--critical);background:var(--critical-soft)}
.zh-diag{font-size:var(--fs-2xs);margin:0 0 var(--s2)}
.zh-pager{display:flex;align-items:center;justify-content:center;gap:var(--s3);margin-top:var(--s2)}
.zh-st{font-weight:600;text-transform:capitalize;color:var(--ink2);white-space:nowrap}
.zh-table td:nth-child(3){white-space:nowrap}
.zh-st.zh-accepted,.zh-st.zh-invoiced,.zh-st.zh-approved{color:var(--good)}
.zh-st.zh-declined,.zh-st.zh-expired{color:var(--critical)}
.zh-st.zh-sent{color:var(--brand-500)}
.zh-st.zh-draft{color:var(--muted)}
/* A tool chip with nothing behind it (no history to show) is drawn but plainly inert. */
.act-chip.act-tool.is-off{opacity:.38;cursor:default}
.act-chip.act-tool.is-off:hover{border-color:transparent;background:transparent;color:inherit}
.brief-pop .bp-head{border-radius:var(--r-lg) var(--r-lg) 0 0}
.bp-foot{display:flex;align-items:center;justify-content:flex-end;gap:var(--s3);padding:var(--s2) var(--s4);border-top:1px solid var(--line);background:var(--surface-2);flex-shrink:0;border-radius:0 0 var(--r-lg) var(--r-lg)}
.bp-foot-note{font-size:var(--fs-2xs);flex:1;min-width:0}

/* Same band as the notifications bubble — the two popovers are the same object at different sizes,
   and letting one go dark while the other stayed grey is how a platform stops looking designed. */
.bp-head{
  display:flex;align-items:center;gap:var(--s2);padding:var(--s3) var(--s4);
  border-bottom:1px solid var(--line);background:var(--brand-800);color:#fff;flex-shrink:0;
}
.bp-head .bp-title strong{color:#fff}
.bp-head .bp-title a,.bp-head .bp-title .muted{color:rgba(255,255,255,.78)}
/* The letter-tile fallback when a company has no logo: white on the band, like the real logo tile. */
.bp-head .bp-logo-fb{background:#fff;color:var(--brand-800)}
.bp-logo{width:32px;height:32px;border-radius:var(--r-sm);object-fit:contain;background:#fff;border:1px solid var(--line);flex-shrink:0}
.bp-logo-fb{display:inline-flex;align-items:center;justify-content:center;background:var(--accent-soft);color:var(--tint-ink);font-weight:700;font-size:var(--fs-md);border-color:transparent}
.bp-title{display:flex;flex-direction:column;min-width:0;flex:1;line-height:1.3}
.bp-title strong{font-size:var(--fs-md);font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bp-title a,.bp-title .muted{font-size:var(--fs-xs);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bp-body{padding:var(--s3) var(--s4) var(--s4);min-height:0}
.bp-sec{
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);margin:var(--s4) 0 var(--s2);
}
.bp-body>.bp-sec:first-child{margin-top:0}
.bp-what{font-size:var(--fs-sm);line-height:var(--lh);color:var(--ink2);margin:0;white-space:pre-wrap;overflow-wrap:anywhere}
.bp-ul{margin:0;padding-left:18px}
.bp-ul li{margin:3px 0;font-size:var(--fs-sm);line-height:var(--lh);color:var(--ink)}
.bp-sub{font-size:var(--fs-xs);font-weight:600;color:var(--ink2);margin:6px 0 2px}
.bp-age{font-size:var(--fs-2xs);margin:4px 0 0}
.bp-src{font-weight:400;font-size:var(--fs-2xs);margin-left:6px;text-transform:none;letter-spacing:0}
/* A row whose lead carries a brief or an offer: the chip is solid, so the table says which leads have one. */
.act-chip.has-brief{background:var(--accent-soft);color:var(--accent);border-color:var(--accent)}
.bp-err{font-size:var(--fs-xs);margin-top:6px;min-height:0}
.bp-row{display:grid;grid-template-columns:92px minmax(0,1fr);gap:var(--s2);padding:3px 0;font-size:var(--fs-sm)}
.bp-k{color:var(--muted)}
.bp-v{color:var(--ink);overflow-wrap:anywhere}
.bp-signal{
  margin-top:var(--s2);font-size:var(--fs-xs);font-weight:600;
  color:color-mix(in srgb,var(--warning) 74%,var(--ink));
  background:var(--warning-soft);border-radius:var(--r-sm);padding:5px 9px;
}

/* — Leads: composed cells + Reach slots —
   Secondary lines carry what used to be their own columns (industry under company,
   title under contact), which is what lets the table fit without horizontal scroll. */
.cell-sub{font-size:var(--fs-xs);color:var(--ink2);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cell-age{font-size:var(--fs-xs);white-space:nowrap;font-variant-numeric:tabular-nums}
.reach-cell{display:inline-flex;gap:4px;align-items:center}
.reach{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:28px;height:28px;min-height:28px;padding:0;flex-shrink:0;
  border-radius:var(--r-sm);border:1px solid var(--line);background:var(--surface-2);
  color:var(--muted);box-shadow:none;cursor:pointer;text-decoration:none;
  transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast);
}
.reach svg{width:15px;height:15px;display:block;pointer-events:none}
.reach:hover{background:var(--surface-3);border-color:var(--line-2)}
/* Data PRESENT reads green (owner, 2026-09-09) — available is the fact this column reports, and how
   verified it is lives in the tooltip note. Only flagged-bad keeps red: green over a known-bad
   address would hide a problem. */
.reach.ok,.reach.unk,.reach.guess{color:var(--good);border-color:color-mix(in srgb,var(--good) 30%,transparent);background:var(--good-soft)}
.reach.bad{color:var(--critical);border-color:color-mix(in srgb,var(--critical) 30%,transparent);background:var(--critical-soft)}
.reach.guess{color:color-mix(in srgb,var(--warning) 78%,var(--ink));border-color:color-mix(in srgb,var(--warning) 30%,transparent);background:var(--warning-soft)}
.reach.unk{color:var(--ink2)}
/* nothing on file, and not fetchable (LinkedIn) — present but visibly inert */
.reach.none{background:transparent;border-style:dashed;color:var(--n400);cursor:default}
.reach.none:hover{background:transparent;border-color:var(--line)}
/* fetchable gap — dashed with a small + badge */
.reach.add{background:transparent;border-style:dashed;color:var(--muted)}
/* queued in a running bulk enrich — visibly part of the batch, waiting its turn. Still dashed
   (nothing found yet), but in the accent so the eye can count what the run will cover; the row
   whose turn it is right now swaps this for .is-busy and its travelling sweep. */
.reach.rv-queued{border-color:var(--accent);color:var(--tint-ink);background:var(--accent-soft)}
.reach.rv-queued .reach-plus{display:none}
/* near-miss LinkedIn profiles waiting for a hand confirm: solid rather than dashed, with the count */
.reach.cand{border-style:solid;color:color-mix(in srgb,var(--warning) 78%,var(--ink));border-color:color-mix(in srgb,var(--warning) 40%,transparent);background:var(--warning-soft);position:relative}
.reach.cand .reach-n{position:absolute;top:-6px;right:-6px;min-width:15px;height:15px;padding:0 4px;border-radius:8px;background:var(--warning);color:#fff;font-size:10px;line-height:15px;font-weight:700;text-align:center}
.lic-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s2)}
.lic-row{display:flex;align-items:center;gap:var(--s3);padding:var(--s2) 0;border-top:1px solid var(--line)}
.lic-row:first-child{border-top:0;padding-top:0}
.lic-who{flex:1;min-width:0}
.lic-who a{font-weight:600}
.lic-head{font-size:var(--fs-xs);color:var(--ink2);margin-top:2px}
.lic-meta{font-size:var(--fs-2xs);color:var(--muted);margin-top:2px}
.reach.add:hover{background:var(--accent-soft);border-color:var(--accent);color:var(--tint-ink)}
/* looking one up right now — the button itself lights up and blinks.
   A 28px icon can't carry the sibling dot-wave the wide buttons use: it doubles the width of the
   Reach cell and detaches the feedback from the control that was actually clicked. This keeps the
   signal on the button, and makes it loud — accent fill, accent border, and a halo that breathes.
   The resting state below is already strong on its own, so the global prefers-reduced-motion rule
   (which stops the pulse) still leaves an unmistakably busy control rather than a dead one.
   ~1.05s per cycle, deliberately under 3 flashes/second (WCAG 2.3.1). */
.reach.is-busy{
  border-color:transparent;background:var(--accent-soft);color:var(--tint-ink);
  cursor:progress;position:relative;
}
/* the progressing-circle language here too — but as a light that TRAVELS around the chip's own
   rounded border, not a rotated box: transform-rotating a rounded RECT visibly tumbles the whole
   chip (owner flagged it), so the angle of a conic gradient animates instead and the shape never
   moves. The mask keeps only the 2px rim of the gradient. Browsers without @property show the
   rim's resting gradient — still unmistakably busy, just not moving. */
.reach.is-busy::after{
  content:"";position:absolute;inset:-2px;border-radius:inherit;pointer-events:none;
  padding:2px;
  background:conic-gradient(from var(--reach-sweep,0deg),
    var(--accent) 0 22%, color-mix(in srgb,var(--accent) 18%,transparent) 30% 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
  animation:reachSweep 1s linear infinite;
}
@property --reach-sweep{syntax:"<angle>";inherits:false;initial-value:0deg}
@keyframes reachSweep{to{--reach-sweep:360deg}}
/* it is disabled while it works, and the global disabled rule would halve exactly the glow we want seen */
.reach.is-busy:disabled{opacity:1;cursor:progress}
/* the + badge promises "click to find this" — it is already finding it */
.reach.is-busy .reach-plus{display:none}
@keyframes reachBusy{
  0%,100%{
    box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 32%,transparent),0 0 8px 1px color-mix(in srgb,var(--accent) 45%,transparent);
    border-color:var(--accent);opacity:1;
  }
  50%{
    box-shadow:0 0 0 6px color-mix(in srgb,var(--accent) 16%,transparent),0 0 20px 5px color-mix(in srgb,var(--accent) 90%,transparent);
    border-color:color-mix(in srgb,var(--accent) 55%,var(--surface));opacity:.58;
  }
}
.reach-plus{
  position:absolute;right:-3px;bottom:-3px;width:13px;height:13px;border-radius:50%;
  background:var(--accent);color:#fff;display:inline-flex;align-items:center;justify-content:center;
  border:1.5px solid var(--surface);
}
.reach-plus svg{width:8px;height:8px}
/* — lead activity timeline —
   One row per event, icon-led, with the timestamp always visible: the point of the feed is when
   something happened, so the date is never behind a tooltip. */
.hist-feed{display:flex;flex-direction:column;gap:1px}
.hist-row{display:flex;gap:var(--s3);padding:var(--s2) var(--s2) var(--s2) 0;border-bottom:1px solid var(--line-soft)}
.hist-row:last-child{border-bottom:0}
.hist-ic{
  width:26px;height:26px;border-radius:var(--r-sm);flex-shrink:0;margin-top:1px;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-3);color:var(--ink2);
}
.hist-ic svg{width:14px;height:14px}
.hist-body{min-width:0;flex:1}
.hist-head{display:flex;align-items:baseline;gap:var(--s2);flex-wrap:wrap}
.hist-head>b{font-size:var(--fs-sm);font-weight:620;color:var(--ink)}
.hist-head .muted{font-size:var(--fs-xs);font-variant-numeric:tabular-nums}
.hist-note{font-size:var(--fs-sm);color:var(--ink2);line-height:var(--lh-snug);margin-top:2px;overflow-wrap:anywhere}
.note-meta{font-size:var(--fs-xs);font-variant-numeric:tabular-nums}

/* — grouped dialog form (Add / Edit lead) —
   The lead form was fifteen equal-weight fields in one column: no grouping, no idea which of them
   mattered. Sections give it shape without hiding anything. */
.form-sec{
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);
  padding:var(--s3) var(--s3) var(--s2);margin-bottom:var(--s3);
}
.form-sec>h4,.form-sec>summary{
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink2);margin:0 0 var(--s2);
}
.form-sec>h4 .muted,.form-sec>summary .muted{letter-spacing:0;text-transform:none;font-weight:520}
.form-sec label{margin-bottom:var(--s2)}
.form-sec>label:last-child,.form-sec .q-row:last-child label{margin-bottom:var(--s1)}
.form-sec input,.form-sec select,.form-sec textarea{background:var(--surface)}
.form-sec .hint{margin:0 0 var(--s1)}
/* "required" reads better than a bare asterisk nobody has a legend for */
.req{
  font-size:var(--fs-2xs);font-weight:640;letter-spacing:.04em;text-transform:uppercase;
  color:var(--accent);margin-left:5px;
}
details.form-sec>summary{cursor:pointer;user-select:none;display:flex;align-items:center;gap:6px}
details.form-sec>summary::-webkit-details-marker{display:none}
details.form-sec>summary::before{content:"▸";transition:transform var(--t) var(--ease);color:var(--muted)}
details.form-sec[open]>summary::before{transform:rotate(90deg)}
.dossier-body{
  white-space:pre-wrap;font-family:inherit;font-size:var(--fs-sm);line-height:var(--lh-normal);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:var(--s3);max-height:220px;overflow:auto;margin:0;color:var(--ink2);
}
.lead-notes-feed{max-height:190px;overflow-y:auto;margin-bottom:var(--s2)}
.note-add{display:flex;gap:var(--s2);align-items:flex-start}
.note-add textarea{flex:1;min-width:0;margin:0}
.note-add button{flex-shrink:0;white-space:nowrap}
/* pushes the destructive action away from the confirming one */
.foot-spacer{flex:1}

/* Back sits above the page title: it belongs to the page you are on, not to the title. */
.back-btn{margin-bottom:var(--s2);padding-left:8px}
.back-btn svg{width:14px;height:14px}
.cell-num{font-variant-numeric:tabular-nums}

/* — CSV export picker (Leads → Export) —
   A tick list rather than a dropdown, because you can now export several collections into one file. */
.exp-list{
  display:flex;flex-direction:column;gap:1px;margin:var(--s2) 0 var(--s3);
  max-height:min(46vh,340px);overflow-y:auto;
  border:1px solid var(--line);border-radius:var(--r-md);padding:var(--s1);background:var(--surface-2);
}
.exp-row{
  display:flex;align-items:center;gap:var(--s2);padding:7px var(--s2);border-radius:var(--r-sm);
  cursor:pointer;font-size:var(--fs-sm);color:var(--ink2);margin:0;
}
.exp-row:hover{background:var(--surface-3)}
.exp-row input{width:16px;height:16px;flex-shrink:0;margin:0}
.exp-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:520}
.exp-row.lead .exp-name{font-weight:640}
.exp-row>b{font-variant-numeric:tabular-nums;color:var(--ink);flex-shrink:0}
/* the aggregate row sits apart from the collections it aggregates */
.exp-row.lead{
  background:var(--accent-soft);color:var(--tint-ink);font-weight:620;
  margin-bottom:var(--s1);border:1px solid var(--accent-ring);
}
.exp-row.lead>b{color:inherit}
.exp-list+.wiz-foot,.exp-list~.wiz-foot{display:flex;align-items:center;justify-content:space-between;gap:var(--s3)}

/* — business network table —
   "What it is" clamps to three lines with the full text on hover, and the row's actions are icons:
   at seven columns of prose the table was unreadable and nothing stood out as clickable. */
.net-concept{
  display:-webkit-box;-webkit-line-clamp:3;line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
  max-width:34ch;color:var(--ink2);
}
.net-rules{
  display:inline-flex;align-items:center;gap:5px;margin-top:5px;
  font-size:var(--fs-2xs);color:var(--ink2);white-space:nowrap;
}
.net-rules svg{width:12px;height:12px;color:var(--muted);flex-shrink:0}
.net-acts{display:inline-flex;gap:var(--s1)}
.net-acts .icon-act{width:28px;height:28px;min-height:28px}
.net-acts .icon-act svg{width:14px;height:14px}

/* — form branding (lead-magnet builder) — */
.lm-brand{display:grid;grid-template-columns:auto 1fr;gap:var(--s3) var(--s4);align-items:start}
.lm-logo-box{
  width:132px;height:76px;border:1px dashed var(--line-2);border-radius:var(--r-md);
  background:var(--surface-2);display:flex;align-items:center;justify-content:center;padding:var(--s2);
}
.lm-logo-box img{max-width:100%;max-height:100%;object-fit:contain}
.lm-brand-ctl{display:flex;flex-direction:column;gap:var(--s2);align-items:flex-start;min-width:0}
/* the file input itself is unstyleable across browsers, so it's hidden behind its own label */
.lm-file input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.lm-file{display:flex;flex-direction:column;gap:4px;margin:0;cursor:pointer}
.lm-file .btn-like{
  display:inline-flex;align-items:center;gap:6px;align-self:flex-start;
  padding:7px 14px;border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--surface);font-size:var(--fs-base);font-weight:560;color:var(--ink2);
}
.lm-file:hover .btn-like{border-color:var(--accent);color:var(--tint-ink)}
.lm-file .btn-like svg{width:14px;height:14px}
.lm-file input:focus-visible+.btn-like{outline:2px solid var(--accent);outline-offset:2px}
.lm-swatches{grid-column:1/-1;display:flex;gap:var(--s4);flex-wrap:wrap}
.lm-swatch{display:flex;flex-direction:column;gap:4px;margin:0;font-size:var(--fs-sm);font-weight:560}
.lm-swatch.fixed{color:var(--ink2)}
.lm-swatch-fixed{
  display:inline-block;width:64px;height:32px;border:1px solid var(--line-2);border-radius:var(--r-sm);
}
.lm-swatch input[type=color]{
  width:64px;height:32px;padding:2px;border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--surface);cursor:pointer;
}
.lm-swatch .hint{margin:0}
/* the preview iframe is the real public page, so it gets a real viewport rather than a thumbnail */
.lm-preview{border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;background:var(--surface-2)}
.lm-preview iframe{display:block;width:100%;height:min(70vh,620px);border:0}
@media (max-width:640px){
  .lm-brand{grid-template-columns:1fr}
}

/* — newsletter brand & email design —
   The template picker is a .net-seg whose buttons stack a pure-CSS thumbnail over their label. No
   image assets: three PNGs of three email shells is three PNGs that go stale the first time the
   renderer changes, and these are drawn from the same two colours the emails are. */
.nlt-seg{flex-wrap:wrap;padding:4px}
.nlt-seg button{
  display:flex;flex-direction:column;align-items:center;gap:5px;padding:9px 12px;
  max-width:170px;text-align:center;line-height:1.35;
}
.nlt-seg button[disabled]{opacity:.55;cursor:default}
.nlt-lb{font-weight:inherit}
.nlt-sub{font-size:var(--fs-2xs);color:var(--ink2);font-weight:460;display:block}
.nlt-th{
  position:relative;display:flex;align-items:center;justify-content:center;
  width:52px;height:38px;border:1px solid var(--line);background:var(--surface);overflow:hidden;
}
/* card — white card on grey, accent hairline across the top of it */
/* flat — full-bleed block with the rule centred underneath it */
/* editorial — a serif A over a heavy rule */

.nlb-cols{display:flex;gap:var(--s5) var(--s6);flex-wrap:wrap}
.nlb-col{display:flex;flex-direction:column;gap:5px;margin:0;min-width:260px;font-size:var(--fs-sm);font-weight:560}
.nlb-col .hint{margin:0}
.nlb-colrow{display:flex;align-items:center;gap:var(--s2)}
.nlb-colrow input[type=color]{
  width:46px;height:34px;padding:2px;border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--surface);cursor:pointer;
}
.nlb-hex{width:104px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;text-transform:lowercase}
.nlb-chiprow{display:flex}
/* the sample the main colour actually has to carry: white label on a real button */
.nlb-chip{
  display:inline-block;padding:9px 20px;border-radius:6px;color:#fff;
  font-size:var(--fs-md);font-weight:600;letter-spacing:.01em;
}
/* advisory, never blocking — a contrast warning does not stop the brand being saved */
.nlb-warn{
  margin:0;font-size:var(--fs-sm);font-weight:520;color:var(--warning);
  background:var(--warning-soft);border-radius:var(--r-sm);padding:7px 10px;
}
.nlb-preview{border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;background:var(--surface-2);max-width:520px}
.nlb-preview iframe{display:block;width:100%;height:min(66vh,600px);border:0;background:#fff}
/* the editor's read-only reminder of where the colours and the logo come from */
.nlb-strip{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;margin-top:var(--s4);font-size:var(--fs-sm)}
.nlb-sw{width:22px;height:22px;border-radius:var(--r-xs);border:1px solid var(--line-2)}
.nlb-striplogo{height:22px;width:auto;max-width:120px;object-fit:contain}

/* a note in the filter bar when the table is showing a capped slice of a bigger result */

/* — business network favourites — */
.net-tabs{margin:var(--s2) 0 var(--s3)}
.net-tabs button{font-weight:560}
.net-tabs .net-tab-n{
  display:inline-block;margin-left:6px;padding:0 6px;border-radius:var(--r-full);
  background:var(--accent-soft);color:var(--tint-ink);font-size:var(--fs-2xs);font-weight:700;
  font-variant-numeric:tabular-nums;
}
.net-tabs button.on .net-tab-n{background:var(--accent);color:var(--ink-invert)}
.net-star{margin-top:3px;
  background:transparent;border:0;box-shadow:none;padding:0;min-height:0;flex-shrink:0;
  width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;
  color:var(--muted);border-radius:var(--r-sm);
}
.net-star svg{width:16px;height:16px}
.net-star:hover{background:var(--warning-soft);color:var(--warning)}
.net-star.on{color:var(--warning)}
.net-star.on svg{fill:currentColor}
.empty-state .net-star,.empty-ic svg{width:20px;height:20px}

/* — bulk-selection toolbar (Leads, Deals) —
   Was a .card.banner with inline padding, which read as another notice rather than "you are in
   selection mode". Accent-tinted, its own component, and its controls hug their content: the bar
   is a toolbar sitting outside .filterbar, so without this the global select{width:100%} made each
   dropdown claim the whole row and the toolbar stacked one control per line. */
.bulk-bar{
  display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  padding:var(--s3) var(--s4);margin-bottom:var(--s3);
  background:var(--accent-soft);border:1px solid var(--accent-ring);
  border-radius:var(--r-lg);box-shadow:var(--shadow-1);
}
.bulk-lead{display:flex;align-items:center;gap:var(--s2);min-width:0}
.bulk-ic{
  width:22px;height:22px;border-radius:var(--r-full);flex-shrink:0;
  background:var(--accent);color:var(--ink-invert);
  display:inline-flex;align-items:center;justify-content:center;
}
.bulk-ic svg{width:13px;height:13px}
.bulk-txt{display:flex;flex-direction:column;gap:0;min-width:0}
.bulk-count{font-size:var(--fs-base);font-weight:640;color:var(--tint-ink);white-space:nowrap}
.bulk-scope{font-size:var(--fs-2xs);color:var(--ink2);line-height:var(--lh-snug)}
.bulk-scope:empty{display:none}
.bulk-actions{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center;margin-left:auto}
.bulk-bar select,.bulk-bar input{width:auto;min-width:126px}
/* select + Apply read as one control instead of six loose buttons */
.bulk-pair{display:inline-flex;align-items:center;gap:var(--s1)}
.bulk-pair>select,.bulk-pair>input{border-top-right-radius:0;border-bottom-right-radius:0}
.bulk-pair>button{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}
.bulk-sep{width:1px;align-self:stretch;min-height:22px;background:var(--accent-ring)}
@media (max-width:760px){
  .bulk-actions{margin-left:0;width:100%}
  .bulk-bar select,.bulk-bar input{min-width:0;flex:1 1 120px}
  .bulk-pair{flex:1 1 200px}
}

/* — activity chips (what has already been done to this lead) —
   Glyph identifies the channel/object, tone identifies the state. They live under the status
   select rather than in a ninth column: same question ("how is this doing"), and the table has
   no width left for another column. */
.lead-acts{display:flex;flex-wrap:wrap;gap:3px;margin-top:5px;max-width:112px}
.act-chip{
  --act:var(--accent);
  /* 22px and r-sm (owner, 2026-08-25: the old 20px hard-cornered boxes read as clutter) —
     roomier glyph, same colour contract: tinted only when the action really happened. */
  width:22px;height:22px;border-radius:var(--r-sm);flex-shrink:0;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--act);background:color-mix(in srgb,var(--act) 15%,transparent);
  border:1px solid color-mix(in srgb,var(--act) 26%,transparent);
}
.act-chip svg{width:13px;height:13px}
/* On touch, the ⋯ tool chip is the ONLY way into draft/call/timeline actions — a 20px target
   fails the finger. Uplift the actionable chips (not the read-only state ones) on coarse pointers. */
@media (pointer:coarse){
  .act-tool{width:28px;height:28px}
  .act-tool svg{width:15px;height:15px}
}
/* Three states, three readings, because they are three different situations (owner, 2026-09-09):
   not drafted — the loud accent button: it is the to-do, click drafts the message.
   drafted / approved — amber: written or cleared, WAITING. Click goes to the message.
   sent — green, filled. It happened. Click opens it in Sent.
   A failed attempt still overrides all of them in critical red. */
.act-chip.draft{background:var(--warning-soft);border-style:solid;border-color:color-mix(in srgb,var(--warning) 40%,transparent);color:var(--warning);filter:none}
.act-chip.ready{--act:var(--warning)}
/* legacy alias — older payloads sent "pending" for anything not yet out */
.act-chip.wait{background:transparent;border-style:dashed;color:var(--muted);border-color:var(--line-2)}
/* This channel CANNOT reach the lead — no address, number or profile on file (owner, 2026-09-09).
   Dashed and grey with a not-allowed cursor: plainly inert, deliberately still there, because an
   absent chip reads as a rendering bug and the tooltip says what to go and find. */
.act-chip.none{
  background:var(--surface-2);border-style:dashed;border-color:var(--line);
  color:var(--n400);filter:grayscale(1);cursor:not-allowed;box-shadow:none;
  min-height:0;min-width:0;padding:0;margin:0;font:inherit;line-height:1;appearance:none;
}
:root[data-theme="dark"] .act-chip.none{color:var(--n600)}
/* Never contacted this way — and that is exactly why it is the LOUD one (owner, 2026-09-09,
   reversing the earlier quiet treatment): this chip is the to-do, the click that drafts the
   message, so it reads as a clear, pressable button. Everything already handled fades instead. */
.act-chip.off{
  background:var(--surface);border-color:color-mix(in srgb,var(--accent) 45%,var(--line));
  color:var(--accent);filter:none;cursor:pointer;
  box-shadow:0 1px 2px color-mix(in srgb,var(--ink) 8%,transparent);
}
.act-chip.off:hover{background:var(--accent-soft);border-color:var(--accent)}
.act-chip.bad{--act:var(--critical)}
/* Already dealt with — sent, approved, done. Legible, but deliberately quiet: only a draft is
   waiting on the reader, so only a draft earns the colour and the click. */
.act-chip.done-quiet{background:var(--surface-2);border-color:var(--line);color:var(--muted)}
/* A drafted message: FADED, because it is already handled — the loud chip is the un-drafted one
   (owner, 2026-09-09, reversing 2026-08-15's neutral-draft rule). Still a door: click → straight
   to the message in the queue, and hover brings it back to say so. Same 20×20 box as the sibling
   spans: the app-wide button rule's min-height:34px was stretching this one taller, so every
   button default it inherits is zeroed here. */
.act-chip.draft-live{
  min-height:0;min-width:0;box-shadow:none;transform:none;
  padding:0;margin:0;box-sizing:border-box;cursor:pointer;
  font:inherit;line-height:1;appearance:none;
  /* Yellow, not grey (owner, 2026-09-09): drafted means WAITING — the same amber the queue's
     approved state speaks — while quieter than the accent to-do chip beside it. */
  color:var(--warning);background:var(--warning-soft);
  border:1px solid color-mix(in srgb,var(--warning) 40%,transparent);
}
.act-chip.draft-live:hover{border-color:var(--accent);color:var(--accent);background:var(--surface)}
.act-chip.draft-live:active{transform:none}
.act-chip.draft-live:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Row tools (brief / prep / ⋯) — the second chip row under the channel chips in the Status
   column. Same 20×20 box and quiet-until-hover language as the draft chip: these are actions,
   so they never carry state colour. */
.lead-tools{margin-top:2px}
.act-chip.act-tool{
  min-height:0;min-width:0;box-shadow:none;transform:none;
  padding:0;margin:0;box-sizing:border-box;cursor:pointer;
  font:inherit;line-height:1;appearance:none;
  /* quiet until hovered — same language the collection cards' icon row moved to */
  color:var(--muted);background:transparent;
  border:1px solid transparent;
}
.act-chip.act-tool:hover{border-color:var(--line);color:var(--ink);background:var(--surface-3)}
.act-chip.act-tool:active{transform:none}
.act-chip.act-tool:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* The message a jump landed on — held long enough to find, then released. */
.oq-item.oq-focus{
  border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 26%,transparent);
  animation:oqFocusIn .5s ease-out;
}
@keyframes oqFocusIn{ from{box-shadow:0 0 0 10px color-mix(in srgb,var(--accent) 0%,transparent)} }
/* Per-message jumps under a bell notification. */
.notif-acts{display:flex;flex-direction:column;gap:4px;padding:2px 34px 4px 14px}
.notif-act{
  display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;
  padding:6px 9px;border-radius:var(--r-sm);border:1px solid var(--line);
  background:var(--surface-2);cursor:pointer;font-size:var(--fs-hint);
}
.notif-act:hover{border-color:var(--accent);background:var(--accent-soft)}
.notif-act .na-who{font-weight:650;color:var(--ink);white-space:nowrap}
.notif-act .na-what{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.notif-act .na-go{color:var(--accent);font-weight:650;white-space:nowrap}
.notif-act-more{padding:2px 9px;font-size:var(--fs-hint);color:var(--accent)}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .act-chip{background:color-mix(in srgb,var(--act) 24%,transparent)}
}
:root[data-theme="dark"] .act-chip{background:color-mix(in srgb,var(--act) 24%,transparent)}

/* Column budget for the composed table. Company/Contact get the room; Reach, Status and the row
   actions are pinned to their content so Notes absorbs the remaining width.
   Everything has to fit WITHOUT horizontal scrolling — a table you have to drag sideways hides
   exactly the columns you were trying to compare. Three things bought the width back without
   dropping a single feature: the four row actions became a 2x2 block instead of one long row, the
   activity chips moved onto their own line under the status select, and the two text columns lost
   their generous minimums (they wrap now, which is what the sub-lines already did). */
/* table-layout:fixed is what actually guarantees it: the columns are budgeted as a share of the
   width available, so no cell's content can push the table wider than its container. Everything
   wraps instead. */
/* calc() rather than 100%: sub-pixel column rounding was leaving the table one pixel wider than its
   wrapper, which is enough for a scrollbar to appear and for the whole point of this to be lost. */
#leadTable{table-layout:fixed;width:calc(100% - 1px);max-width:100%;box-sizing:border-box}
#leadTable td{overflow-wrap:anywhere}
#leadTable th:nth-child(1),#leadTable td:nth-child(1){width:32px}
#leadTable th:nth-child(2),#leadTable td:nth-child(2){width:23%}
#leadTable th:nth-child(3),#leadTable td:nth-child(3){width:18%}
#leadTable th:nth-child(4),#leadTable td:nth-child(4){width:120px}
#leadTable th:nth-child(5),#leadTable td:nth-child(5){width:138px}
#leadTable th:nth-child(6),#leadTable td:nth-child(6){width:auto}
#leadTable th:nth-child(7),#leadTable td:nth-child(7){width:72px}
/* 2 x 2 rather than four in a line: same four actions, a third of the width. */
.rowacts{display:grid;grid-template-columns:repeat(2,1fr);gap:2px;justify-items:center}
.rowacts .icon-btn{margin:0;min-width:28px;padding-left:2px;padding-right:2px}
#leadTable .cell-notes{max-width:none;vertical-align:middle}
/* A status is a BADGE that happens to be changeable, so it is sized to the word it is showing.
   width:100% made every row carry a full-column-wide bar of flat colour: the pill shape was lost,
   short statuses like "New" and "Won" were mostly empty space, and the eye had nothing to lock on
   to when scanning the column. Sized to content, with a ceiling so the longest option
   ("Junk / Not relevant") cannot widen the column — its full text is readable in the open list. */
#leadTable .status-select{
  /* field-sizing is the only way a native <select> sizes to the SELECTED option — width:auto sizes
     it to the longest one, so every pill came out the width of "Junk / Not relevant" regardless of
     what it said. Where it is unsupported the rule is ignored and the uniform pill remains, which
     is still far better than the full-column bar this replaced. */
  field-sizing:content;
  /* No ceiling any more: the table shows the SHORT status words now ("Not interested", not
     "Junk / Not relevant" — the full wording is swapped in as the list opens), and the longest of
     them fits in less room than the old ceiling clipped it to. Capping it here only produced
     "Not Interes…" in a column with space to spare. */
  width:auto;min-width:0;max-width:none;padding-right:24px;font-size:var(--fs-sm);
  text-overflow:ellipsis;
}
/* The cell holds the pill, the CRM sub-line and the channel chips, so it stacks them rather than
   letting the pill be stretched by whatever sits beneath it. */
#leadTable td:has(> .status-select){white-space:normal}
/* Notes edit in place. One row at rest so the table stays compact, three on focus so a real note is
   writable, and a brief green edge on save — the only feedback a silent PUT can honestly give. */
.note-inline{
  width:100%;margin:0;padding:6px 8px;font:inherit;font-size:var(--fs-sm);line-height:var(--lh-snug);
  border:1px solid transparent;border-radius:var(--r-sm);background:transparent;color:var(--ink2);
  resize:none;overflow:hidden;transition:border-color var(--t),background var(--t),box-shadow var(--t);
}
.note-inline:hover{border-color:var(--line);background:var(--surface-2)}
.note-inline:focus{
  outline:none;border-color:var(--accent);background:var(--surface);color:var(--ink);
  box-shadow:var(--ring-focus);overflow:auto;
}
.note-inline.saved{border-color:var(--good);box-shadow:0 0 0 3px color-mix(in srgb,var(--good) 20%,transparent)}
.note-inline::placeholder{color:var(--n400)}
#leadTable td{vertical-align:middle}
#leadTable .reach{width:26px;height:26px;min-height:26px}
#leadTable .reach svg{width:14px;height:14px}
/* The notes slot is the Notes column's stand-in on narrow screens only. */
.reach.note{display:none}
/* Under ~1300px the sidebar leaves too little room for eight rich columns, so Notes
   folds into an icon in the Reach cell (tooltip + click-to-copy keeps the text
   reachable) instead of the whole table scrolling sideways. */
@media (max-width:1080px){
  #leadTable th:nth-child(6),#leadTable td:nth-child(6){display:none}
  .reach.note{display:inline-flex}
  /* tighter gutters and controls buy back the remaining width without hiding anything */
  #leadTable th,#leadTable td{padding-left:8px;padding-right:8px}
  #leadTable .status-select{min-width:0;width:auto;max-width:100%;padding-right:22px}
  #leadTable .reach{width:26px;height:26px;min-height:26px}
  #leadTable .icon-btn{min-width:26px;padding-left:4px;padding-right:4px}
  #leadTable .cell-sub{font-size:var(--fs-2xs)}
}

/* — action menus (⋯) —
   List-height rows instead of full-width .wiz-choice blocks, so a ten-action menu is a
   short grouped list rather than a stack of buttons taller than the viewport. */
.menu-sub{font-size:var(--fs-sm);margin:calc(var(--s1) * -1) 0 var(--s3)}
/* Anchored variant: the same .menu-list rows in a bubble that grows out of the ⋯ button,
   matching the notification bell's "cloud". position:fixed because the button sits inside
   .table-wrap, an overflow:auto scroller that would otherwise clip it. */
.act-pop{
  position:fixed;z-index:var(--z-pop);width:min(280px,calc(100vw - 16px));
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-3);
  animation:notifPopIn var(--t-slow) var(--ease-spring) both;
}
/* the scroller is the inner box: on the outer one, overflow:auto would clip the pointer */
.act-pop-in{
  padding:var(--s2);max-height:min(74vh,540px);
  overflow-y:auto;overscroll-behavior:contain;border-radius:inherit;
}
/* pointer — two stacked squares so the 1px border reads as continuous, like .notif-pop */
.act-pop::before,.act-pop::after{
  content:"";position:absolute;top:-7px;left:var(--arrow);margin-left:-6px;
  width:12px;height:12px;background:var(--surface);transform:rotate(45deg);
}
.act-pop::before{border:1px solid var(--line);border-right:0;border-bottom:0}
.act-pop::after{top:-5px}
.act-pop.up::before,.act-pop.up::after{top:auto;bottom:-7px;border:1px solid var(--line);border-left:0;border-top:0}
.act-pop.up::after{bottom:-5px;border:0}
.act-pop-head{
  display:flex;flex-direction:column;gap:1px;padding:var(--s1) var(--s2) var(--s2);
  border-bottom:1px solid var(--line);margin-bottom:var(--s1);
}
.act-pop-head b{font-size:var(--fs-sm);font-weight:640;line-height:var(--lh-snug);overflow-wrap:anywhere}
.act-pop-head .muted{font-size:var(--fs-xs);overflow-wrap:anywhere}
.act-pop .menu-list{margin:0;gap:0}
.act-pop .menu-label{padding:var(--s2) var(--s2) 2px}
.act-pop .menu-row{padding:7px var(--s2)}
/* a hint wraps here rather than being cut off -- these read "already on a newsletter", and a
   clipped half-sentence is worse than two lines */
.act-pop .menu-txt{white-space:normal}
.act-pop .menu-txt .hint{white-space:normal;line-height:var(--lh-snug)}
.menu-list{display:flex;flex-direction:column;gap:1px;margin:0 calc(var(--s2) * -1)}
.menu-label{
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);padding:var(--s3) var(--s2) var(--s1);
}
.menu-list>.menu-label:first-child{padding-top:0}
.menu-row{
  display:flex;align-items:center;gap:var(--s3);width:100%;text-align:left;
  background:transparent;border:1px solid transparent;box-shadow:none;color:var(--ink);
  border-radius:var(--r-sm);padding:9px var(--s2);min-height:40px;font-weight:500;
  justify-content:flex-start;
}
.menu-row:hover:not(:disabled){background:var(--surface-3);border-color:var(--line)}
.menu-row:disabled{opacity:1;cursor:not-allowed;color:var(--muted)}
.menu-row:disabled .menu-ic{opacity:.4}
.menu-row.danger{color:var(--critical)}
.menu-row.danger:hover:not(:disabled){background:var(--critical-soft);border-color:color-mix(in srgb,var(--critical) 26%,transparent)}
.menu-ic{display:inline-flex;align-items:center;justify-content:center;width:20px;flex-shrink:0;color:var(--muted)}
.menu-ic svg{width:16px;height:16px}
.menu-row:hover:not(:disabled) .menu-ic{color:var(--accent)}
.menu-txt{display:flex;flex-direction:column;gap:1px;min-width:0}
.menu-txt>b{font-weight:560;font-size:var(--fs-base)}
.menu-txt .hint{font-size:var(--fs-xs);margin:0}

/* — Outreach queue —
   Replaces a bare grey status line and a run of inline-styled flex rows. */
.oq-status{
  display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);
  padding:9px var(--s4);margin:0 0 var(--s4);font-size:var(--fs-sm);color:var(--ink2);
}
.oq-status .oq-dot{width:8px;height:8px;border-radius:50%;background:var(--muted);flex-shrink:0}
.oq-status.on{background:var(--good-soft);border-color:color-mix(in srgb,var(--good) 26%,transparent)}
.oq-status.on .oq-dot{background:var(--good);animation:pulseDot 1.8s ease-in-out infinite}
.oq-status strong{color:var(--ink)}
.oq-queued{margin-left:auto;font-size:var(--fs-xs);color:var(--muted);white-space:nowrap}
.oq-when{font-size:var(--fs-xs);white-space:nowrap;flex-shrink:0}
/* channel chip carries its icon inline with the label */
.tag.chan{display:inline-flex;align-items:center;gap:5px}
.tag.chan svg{width:12px;height:12px;flex-shrink:0}
.oq-acts{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center;margin-top:var(--s3);padding-top:var(--s3);border-top:1px solid var(--line-soft)}
/* — outreach queue items —
   Collapsed by default (drafts and failures open), so a queue of twenty reads as a list you can scan
   instead of twenty stacked editors. */
/* — the queue reads by colour before it reads by word —
   Every item carries a 3px status rail on its left edge, the same device the metric tiles use, so a
   queue of twenty answers "what is waiting on me?" by shape alone. A DRAFT takes the brand accent:
   it is the one state that is waiting on a person, and the accent is the app's own "this is yours to
   do" colour (the same one the rail, the active nav item and the primary button use). Approved is
   amber — cleared, about to leave, nothing to do. Sent is green and deliberately the quietest.
   Failed is the only one that also tints its whole summary, because it is the only one that is wrong. */
.oq-item{padding:0;overflow:hidden;position:relative;--oq:var(--line-2)}
.oq-item::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--oq);
  border-radius:var(--r-lg) 0 0 var(--r-lg);
}
/* The draft card also borrows the accent for its border and its badge, so a draft is identifiable
   from the rail, the wash, the border and the status pill — four quiet signals rather than one loud
   one, which is what keeps a queue of twenty calm while still being instantly sortable by eye. */
.oq-draft{--oq:var(--accent);border-color:color-mix(in srgb,var(--accent) 34%,var(--line))}
.oq-draft .badge{background:var(--accent-soft);border-color:color-mix(in srgb,var(--accent) 30%,transparent);color:var(--tint-ink)}
.oq-draft .badge::before{background:var(--accent)}
.oq-approved{--oq:var(--warning)}
.oq-sent,.oq-done{--oq:var(--good)}
/* A SENT message, read-only (2026-09-08). Deliberately not the composer with its inputs disabled: a
   disabled field is still a box somebody tries to type in, and this is history.
   pre-wrap is load-bearing — the blank row under the greeting and the single rows between body lines
   are the house layout, and collapsing them here would hide whether it worked. overflow-wrap so a
   long resolved link cannot push the card sideways. */
.oq-ro{display:flex;flex-direction:column;gap:8px}
.oq-ro-meta{display:flex;gap:10px;flex-wrap:wrap;font-size:var(--fs-xs)}
.oq-ro-rep{color:var(--good)}
.oq-ro-subj{font-size:var(--fs-md)}
.oq-ro-body{
  white-space:pre-wrap;overflow-wrap:anywhere;
  font-family:Calibri,'Segoe UI',Helvetica,Arial,sans-serif;font-size:var(--fs-md);
  line-height:1.5;color:var(--ink);
  background:var(--surface-2, transparent);border:1px solid var(--line);border-radius:var(--r-md);
  padding:12px 14px;margin:0;
}
.oq-failed{--oq:var(--critical)}
.oq-skipped{--oq:var(--line-2);opacity:.72}
/* A DRAFT is the only row you have to do something about, so its header takes the house dark
   teal-green outright (2026-08-22) — the same band the table headers and the calendar weekday strip
   wear. It was a 7% accent wash, which grouped the drafts but did not lead the page. Everything
   inside it is recoloured for the dark ground; the status rail on the left edge and the pills keep
   their own colours, so "draft" is still readable by shape as well as by band. */
.oq-draft>.oq-sum{background:var(--brand-800);color:#fff}
.oq-draft[open]>.oq-sum{background:var(--brand-800)}
.oq-draft>.oq-sum:hover{background:var(--brand-900)}
.oq-draft>.oq-sum .oq-lead{color:#fff}
.oq-draft>.oq-sum .muted{color:rgba(255,255,255,.78)}
/* The chevron is drawn from two borders, so it needs its own line — `color` does not reach it. */
.oq-draft>.oq-sum::after{border-right-color:rgba(255,255,255,.85);border-bottom-color:rgba(255,255,255,.85)}
.oq-failed>.oq-sum{background:color-mix(in srgb,var(--critical) 5%,transparent)}
.oq-failed[open]>.oq-sum{background:color-mix(in srgb,var(--critical) 7%,var(--surface-2))}
/* the pale FAILED tint needs more presence on a dark surface to survive at all; the draft header is
   a solid brand band in both themes and needs no boost. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .oq-failed>.oq-sum{background:color-mix(in srgb,var(--critical) 12%,transparent)}
}
:root[data-theme="dark"] .oq-failed>.oq-sum{background:color-mix(in srgb,var(--critical) 12%,transparent)}
.oq-sum{
  display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s4) var(--s3) calc(var(--s4) + 4px);
  cursor:pointer;list-style:none;user-select:none;
}
.oq-sum::-webkit-details-marker{display:none}
.oq-sum:hover{background:var(--surface-2)}
.oq-item[open]>.oq-sum{border-bottom:1px solid var(--line);background:var(--surface-2)}
/* AFTER the generic open rule, not before it: both selectors weigh the same, so the later one wins
   and an expanded draft was dropping back to grey the moment it was opened. The header keeps the
   band open or closed; the body below stays light because it holds the editor. */
.oq-draft[open]>.oq-sum{background:var(--brand-800);color:#fff;border-bottom-color:transparent}
.oq-main{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
.oq-line1{display:flex;align-items:baseline;gap:6px;min-width:0;flex-wrap:wrap}
/* The lead is the subject of every row in this queue, so it gets one colour everywhere — the same
   brand-tinted ink the All-leads card uses — and the same scored avatar it has on the Leads page.
   Before this the company sat in body ink among five other body-ink strings and you had to read the
   row to find out who it was about. */
.oq-line1>strong.oq-lead{font-size:var(--fs-md);font-weight:660;color:var(--tint-ink)}
.oq-sum .lead-avatar-ring{width:34px;height:34px;min-width:34px}
.oq-line1 .muted{font-size:var(--fs-sm)}
.oq-line2{font-size:var(--fs-sm);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.oq-tags{display:flex;align-items:center;gap:5px;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end}
.oq-tags .tag,.oq-tags .badge{margin:0}
.oq-body{padding:var(--s3) var(--s4) var(--s4)}
.oq-body>label{margin-bottom:var(--s3)}
.oq-body .oq-acts{margin-top:var(--s2)}
/* a chevron so it reads as expandable without adding a second control */
.oq-sum::after{
  content:"";width:7px;height:7px;flex-shrink:0;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:rotate(-45deg);transition:transform var(--t) var(--ease);
}
.oq-item[open]>.oq-sum::after{transform:rotate(45deg)}
@media (max-width:820px){
  .oq-tags{width:100%;justify-content:flex-start;order:3}
  .oq-sum{flex-wrap:wrap}
  .oq-when{order:2}
}

/* — outreach channel tabs —
   The channel used to be a filter chip and an icon on every row. It is now the page you are on:
   each channel sends as a different identity and reports a different thing back, so the row itself
   no longer has to say which one it is. */
/* scrolls rather than wrapping on a phone — three channels don't fit on 390px */
.oq-tabs{margin:0 0 var(--s4);max-width:100%;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;flex-wrap:nowrap}
.oq-tabs::-webkit-scrollbar{display:none}
.oq-tabs button{flex:0 0 auto}
.oq-tabs button{display:inline-flex;align-items:center;gap:7px}
/* — Sent / Replied views (round-2 outreach) —
   The Queue/Sent/Replied seg above the channel tabs, and the flat row cards those two history views
   list. Rows reuse the queue summary's own layout classes (.oq-main/.oq-tags/.oq-when) so the two
   lists read as one family. */
/* The view band. Was `.net-seg.oq-views`, a segmented control; it is a filter-tile band now
   (2026-09-08) so the spacing rule moves to the container the tiles are rendered into. The seg rule
   is gone rather than left behind: a selector matching nothing is how a class ends up styled by
   nothing, which this project has shipped before. */
#oqViewTiles{margin:0 0 var(--s4)}
#oqViewTiles .page-metrics{margin-bottom:0}
.r2-bulk{display:flex;align-items:center;gap:var(--s4);padding:var(--s2) var(--s4);margin-bottom:var(--s3)}
.r2-bulk .r2-all{display:flex;flex-direction:row;align-items:center;gap:8px;margin:0;font-size:var(--fs-sm);color:var(--ink2)}
.r2-row{display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s4);margin-bottom:var(--s2)}
.r2-row .lead-avatar-ring{width:34px;height:34px;min-width:34px}
.badge.r2{background:var(--good-soft);border-color:transparent;color:color-mix(in srgb,var(--good) 70%,var(--ink))}
.badge.r2::before{background:var(--good)}
@media (max-width:820px){
  .r2-row{flex-wrap:wrap}
  .r2-row .oq-tags{width:100%;justify-content:flex-start;order:3}
}
/* A channel that cannot send cannot be opened either (2026-08-22). It still shows — the tab is how you
   learn the channel exists and why it is shut — but it reads as unavailable and does not respond. */
/* The filters currently applied, each removable on its own — "Clear filters" as the only exit meant
   dropping all four to look at one. */
/* The auto-send switch sits inline on the status bar, so it must not take the switch-row's own
   block layout and margins. */
.oq-status .oq-auto{margin:0 0 0 auto;display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-sm);white-space:nowrap}
.oq-status .oq-auto+.oq-queued{margin-left:var(--s3)}
/* The filter pattern that replaced the filter bars (2026-08-22): a search box and a Filters button in
   the page header, the rest behind them. */
/* The search grows LEFT into the header's spare room (owner, 2026-09-09) — flex:1 inside the
   actions row, bounded so it never crowds the buttons beside it. The magnifier sits inside the
   right edge, decorative and pointer-through. */
.head-search-wrap{position:relative;display:inline-flex;flex:1 1 260px;max-width:480px;min-width:170px}
/* .pagehead .actions input sets width:auto at higher specificity — this must outrank it or the
   input shrinks to its placeholder and the absolute magnifier floats beside, not inside, it. */
.head-search,.pagehead .actions .head-search{width:100%;min-width:0;padding-right:34px}
.hs-ic{position:absolute;right:11px;top:50%;transform:translateY(-50%);color:var(--muted);pointer-events:none;display:inline-flex}
.hs-ic svg{width:15px;height:15px}
/* the browser's own clear ✕ would collide with the magnifier on WebKit; keep the native one hidden */
.head-search::-webkit-search-cancel-button{display:none}
.fchip-n{display:inline-flex;align-items:center;justify-content:center;min-width:17px;height:17px;padding:0 5px;
  border-radius:var(--r-full);background:var(--accent);color:#fff;font-size:var(--fs-2xs);font-weight:700;margin-left:4px}
/* One row per filter: its name on the left, its control on the right, every row the same shape. A
   bare ddSingle() select carries its own name in the "Status: all" option, so it stands alone and is
   right-aligned to line up with the labelled rows beside it. */
.fdlg{display:flex;flex-direction:column;gap:var(--s2)}
.fdlg label.inline,.fdlg>label:not(.switch-row){
  /* label.inline is a COLUMN elsewhere (a caption over a control in a toolbar); in this panel every
     row is name-left / control-right, so the direction is overridden rather than a new class invented. */
  display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:var(--s3);
  margin:0;font-size:var(--fs-base);color:var(--ink2);
}
.fdlg label.inline select,.fdlg label.inline input,
.fdlg>label:not(.switch-row) select,.fdlg>label:not(.switch-row) input{
  width:58%;max-width:58%;min-width:0;flex:0 0 58%;margin-top:0;
}
.fdlg>select{width:58%;align-self:flex-end}
.fdlg .switch-row{margin:var(--s1) 0 0}
.fdlg .wiz-foot{margin-top:var(--s3);padding:var(--s3) 0 0;border-top:1px solid var(--line)}
/* NESTED HEADERS (2026-08-24, owner's ask). `.tablehead` is the outer band; the table header row and
   the calendar's weekday strip sit directly under it and are the inner one, so they take brand-700 —
   one step lighter, same hue. Scoped to the ADJACENT case on purpose: a table that is not under a
   card band is the only header there is, and stays the full-strength colour. */
/* ONE dark band per section (owner, 2026-08-25 and again 2026-09-11). The column row is the same
   tinted label row everywhere — the `th` rule decides it — so under a card band nothing changes but
   the rounded corners, which the band already supplies. The Meetings weekday strip matches. */
.tablehead + .table-wrap th,
.tablehead + .table-wrap .data-table thead th{border-radius:0}
.tablehead ~ .mt-grid .mt-head,
.mt-card .tablehead ~ .mt-grid .mt-head{
  background:color-mix(in srgb, var(--accent) 7%, var(--surface));color:var(--ink);border-bottom:2px solid color-mix(in srgb, var(--accent) 45%, var(--line));
}

/* Anything focusable on one of the dark header bands: the accent focus ring is ~1.9:1 against
   --brand-800, which is a WCAG 2.4.11 failure. White ring, white halo. */
.tablehead :focus-visible,.notif-pop-head :focus-visible,.bp-head :focus-visible{
  outline-color:#fff;box-shadow:0 0 0 4px rgba(255,255,255,.28);
}
/* The refresh and Clear controls in the notifications bubble kept their light-ground ink when that
   header went dark — ~1.3:1 and ~2.1:1 respectively, i.e. invisible and nearly so. */
.notif-pop-head .tb-icon,.notif-pop-head .notif-clear{color:#fff}
/* The bubbles' own dismiss sits at the end of the band, like the dialogs'. */
.notif-pop-head .wiz-x{margin-left:6px;align-self:center}
.notif-pop-head .notif-pop-count+.wiz-x{margin-left:auto}
.notif-pop-head .tb-icon:hover,.notif-pop-head .notif-clear:hover{background:rgba(255,255,255,.18);color:#fff}
/* Who a form is shared with, under its title. */
.oq-chips{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 var(--s3)}
.oq-chips .tag.mini{cursor:pointer}
.oq-tabs button[disabled]{opacity:.6;cursor:not-allowed}
.oq-tabs button[disabled]:hover{background:transparent}
.oq-tabs button svg{width:15px;height:15px}
.oq-tab-n:empty{display:none}
.oq-tab-n{
  min-width:18px;padding:0 5px;border-radius:var(--r-full);background:var(--warning-soft);
  color:var(--warning);font-size:var(--fs-xs);font-weight:660;line-height:17px;text-align:center;
}
/* A channel this user cannot send on. The tab still opens — the card inside it says what to do — so
   the pill is the only thing carrying the state. Its hover state is pinned to the same values as its
   resting one: it lives inside a button, and neither the colour nor the size may shift when that
   button is hovered or pressed. Both tones come from the theme's own critical pair, so it is legible
   light and dark without a second palette. */
.tag.mini.static.oq-off,.tag.mini.static.oq-off:hover{
  background:var(--critical-soft);color:var(--critical);
  border-color:color-mix(in srgb,var(--critical) 30%,transparent);
  font-size:var(--fs-2xs);font-weight:620;padding:1px 6px;
}
/* who this channel sends as — the addresses and accounts that used to only reveal
   themselves after something had already gone out */
.oq-senders{
  display:flex;flex-wrap:wrap;gap:var(--s3) var(--s6);align-items:flex-start;
  padding:var(--s3) var(--s4);margin-bottom:var(--s4);
}
.oq-id{display:flex;flex-direction:column;gap:1px;min-width:0}
/* A parked sending identity. Full width and toned, because it is the one row on this card that says
   the queue behind it is not moving — sat inline among the others it reads as another fact about the
   sender rather than the reason nothing is going out. Both tokens are theme-aware, so it inverts
   with the rest of the page instead of staying a light box on a dark card. */
.oq-parked{
  flex-basis:100%;background:var(--warning-soft);border:1px solid var(--warning);
  border-radius:var(--r2,8px);padding:var(--s2) var(--s3);
}
.oq-parked strong{color:var(--warning)}
.oq-parked .oq-idmeta .muted{text-transform:none;letter-spacing:0}
.oq-id .muted{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.04em}
.oq-id strong{font-size:var(--fs-sm);font-weight:600;word-break:break-word}
/* Manage/Change/Edit read as links, not buttons — they navigate, they don't act. `button` is used
   for the ones that open an editor in place, so the global button styling has to be undone. */
.oq-idlink{
  font-size:var(--fs-xs);margin-left:6px;font-weight:500;display:inline-block;padding:5px 0;
  background:none;border:0;box-shadow:none;min-height:0;color:var(--accent);cursor:pointer;
  text-decoration:underline;text-decoration-color:transparent;text-underline-offset:2px;
  transition:text-decoration-color var(--t);
}
.oq-idlink:hover{background:none;text-decoration-color:currentColor;filter:none;transform:none}
.oq-senders-off{justify-content:space-between;align-items:center}
.oq-senders-off p{margin:2px 0 0;font-size:var(--fs-sm)}
/* — first-run channel setup —
   One row per channel: what it is, whether it is connected (the same .conn-chip the sender card
   uses) and the one button that connects it. */
.onb-rows{display:flex;flex-direction:column;gap:var(--s2);margin:var(--s3) 0}
.onb-row{
  display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  padding:var(--s3);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-sm);
}
.onb-chan{display:inline-flex;align-items:center;gap:var(--s2);min-width:104px}
.onb-chan svg{width:18px;height:18px;flex:0 0 auto}
.onb-state{display:inline-flex;align-items:center;gap:var(--s2);flex:1 1 12ch;min-width:0;font-size:var(--fs-sm)}
/* The chip is a fixed label, the reason beside it is the part that reflows — without this the two
   words in "Not connected" wrapped and the chip grew over the sentence next to it. */
.onb-state .conn-chip{flex:0 0 auto;white-space:nowrap}
.onb-state .muted{min-width:0;overflow-wrap:anywhere}
.onb-row button{margin-left:auto;flex:0 0 auto}
.onb .wiz-note{margin:0}
/* the "How to connect X" links under the per-lead channel picker */
.onb-inline{display:flex;flex-wrap:wrap;gap:var(--s4);margin:var(--s3) 0 0}
/* addressing block inside an open item */
.oq-addr{
  display:flex;flex-wrap:wrap;gap:var(--s2) var(--s5);
  padding:var(--s2) var(--s3);margin-bottom:var(--s3);
  background:var(--surface-2);border:1px solid var(--line-soft);border-radius:var(--r-sm);
}
.oq-addr>div{display:flex;flex-direction:column;gap:1px;min-width:0}
.oq-addr .muted{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.04em}
.oq-addr strong{font-size:var(--fs-sm);font-weight:600;word-break:break-word}
.oq-addr .err{font-size:var(--fs-xs);margin-left:6px}
.oq-sig{margin:calc(var(--s1) * -1) 0 var(--s3)}
.oq-sig .muted{font-size:var(--fs-xs)}
.oq-sig pre{
  margin:3px 0 0;padding:var(--s2) var(--s3);white-space:pre-wrap;font:inherit;
  font-size:var(--fs-sm);color:var(--ink2);background:var(--surface-2);
  border-left:2px solid var(--line);border-radius:0 var(--r-sm) var(--r-sm) 0;
}
.oq-hint{font-size:var(--fs-sm);margin:calc(var(--s1) * -1) 0 var(--s3)}
/* sent → received → read. Steps are buttons because LinkedIn invitations never report a
   receipt, and whoever saw the reply knows more than the API does. */
.oq-dlv{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;margin-bottom:var(--s3)}
.oq-dlv>.muted:first-of-type{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.04em}
.oq-dlv-steps{display:inline-flex;gap:2px;padding:2px;background:var(--surface-3);border:1px solid var(--line);border-radius:var(--r-full)}
.oq-dlv-step{
  background:transparent;border:0;box-shadow:none;color:var(--muted);
  padding:3px 12px;border-radius:var(--r-full);font-size:var(--fs-xs);font-weight:560;min-height:24px;
}
.oq-dlv-step:hover{background:color-mix(in srgb,var(--ink) 6%,transparent);color:var(--ink)}
.oq-dlv-step.on{background:var(--good-soft);color:var(--good);font-weight:660}
.oq-dlv-note{font-size:var(--fs-xs);flex:1;min-width:140px;text-transform:none;letter-spacing:0}
@media (max-width:560px){ .oq-senders{gap:var(--s3)} }

/* — lead magnets / newsletter — */
/* form-field builder rows: contained and aligned, rather than bare inputs floating in
   a flex row with inline widths */
.lm-frow{
  display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:6px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);padding:6px 8px;
}
.lm-frow.locked{background:var(--surface-2);border-style:dashed}
.lm-f-label{flex:2;min-width:150px}
.lm-f-type{width:auto;min-width:118px;flex:0 0 auto}
/* Choices sit on their own line: a comma list is long, and squeezing it into the control row pushed
   the remove button onto a line by itself. flex-basis:100% forces the wrap deliberately. */
.lm-f-opts{flex:0 0 100%;display:flex;align-items:center;gap:6px;margin:2px 0 0}
.lm-f-opts-l{
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);flex-shrink:0;padding-left:26px;
}
.lm-f-opts input{flex:1;min-width:0}
.lm-f-req{
  display:flex;align-items:center;gap:5px;margin:0;font-size:var(--fs-xs);
  white-space:nowrap;color:var(--ink2);flex-shrink:0;
}
.lm-f-req input{margin:0}
.lm-f-lock{font-size:var(--fs-2xs);color:var(--muted);white-space:nowrap;flex-shrink:0;padding:0 4px}
.lm-frow>.icon-act{width:28px;height:28px;min-height:28px}
.lm-frow>.icon-act svg{width:12px;height:12px}
.tw-region{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;font-size:var(--fs-sm);color:var(--ink2)}

/* =============================================================================
   18 · RESPONSIVE
   ========================================================================== */
@media (max-width:1180px){
  main{padding:var(--s5) var(--s5) var(--s8)}
}
@media (max-width:980px){
  #app{grid-template-columns:minmax(0,1fr);grid-template-areas:"topbar" "main"}
  aside{
    position:fixed;top:0;left:0;height:100vh;height:100dvh;width:272px;z-index:var(--z-side);
    transform:translateX(-100%);transition:transform var(--t-slow) var(--ease);
    box-shadow:var(--shadow-3);padding-top:var(--s3);background:var(--surface);
  }
  #app.nav-open aside{transform:translateX(0)}
  #app.rail aside{width:272px}
  #app.rail aside nav a{justify-content:flex-start;padding:7px var(--s3);margin:0 var(--s1)}
  #app.rail aside nav a>span:not(.nav-badge),#app.rail aside nav a .tag{display:inline}
  #app.rail aside nav a::after{display:none}
  #app.rail .nav-group-h{justify-content:space-between;padding:var(--s4) var(--s3) var(--s1);pointer-events:auto}
  #app.rail .nav-group-h>span{display:inline-flex}
  #app.rail .nav-group-h::after{display:none}
  #app.rail .nav-group-chev{display:block}
  .nav-toggle{
    display:inline-flex;align-items:center;justify-content:center;
    width:34px;height:34px;min-height:34px;padding:0;font-size:17px;flex-shrink:0;
    background:transparent;color:var(--ink2);border:1px solid transparent;box-shadow:none;border-radius:var(--r-sm);
  }
  .nav-toggle:hover{background:color-mix(in srgb, var(--ink) 7%, transparent);color:var(--ink)}
  .side-foot{display:none}
  #app::after{
    content:"";position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;
    transition:opacity var(--t-slow);z-index:var(--z-scrim);
  }
  #app.nav-open::after{opacity:1;pointer-events:auto}
  main{padding:var(--s5) var(--s4) var(--s8)}
  .crumbs{display:none}
}
@media (max-width:720px){
  :root{--topbar-h:48px} /* still a step DOWN from the 52px desktop bar — 54 predated the compact pass */
  main{padding:var(--s4) var(--s3) var(--s7)}
  .pagehead{align-items:flex-start;gap:var(--s3)}
  .pagehead h2{font-size:var(--fs-xl)}
  .pagehead .actions{width:100%}
  .grid2{grid-template-columns:1fr}
  .tiles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--s2)}
  .tile{padding:var(--s3) var(--s3) var(--s3) var(--s4)}
  .tile .n{font-size:var(--fs-2xl)}
  .tb-user .tb-username{display:none}
  .tb-word span{display:none}
  .pb-toc{columns:1}
  .card{padding:var(--s4);border-radius:var(--r-md)}
  dialog{padding:var(--s5);width:min(560px,96vw);border-radius:var(--r-lg)}
  dialog:not(.wizard) .wiz-foot{margin:var(--s4) calc(var(--s5) * -1) calc(var(--s5) * -1);border-radius:0 0 var(--r-lg) var(--r-lg)}
  .wiz-top,.wiz-foot{padding-left:var(--s4);padding-right:var(--s4)}
  .wiz-body,.wiz-steptitle,.wiz-head{padding-left:var(--s4);padding-right:var(--s4)}
  .wiz-note{margin:0 var(--s4)}
  #toast{left:var(--s3);right:var(--s3);bottom:var(--s3);max-width:none}
}
@media (max-width:720px){
  /* Filter dropdowns sit part-way across the bar, so a left-anchored panel runs
     off the right edge on a phone. Anchor them to the trigger's right instead. */
  .dd-panel{left:auto;right:0;max-width:calc(100vw - 32px)}
}
@media (max-width:560px){
  .formgrid,.q-row{grid-template-columns:1fr}
  .coll-grid,.conn-grid{grid-template-columns:1fr}
  .board-col{flex-basis:84vw;width:84vw}
}

/* Business Network table collapses to cards on narrow screens */
@media (max-width:760px){
  #netTable thead .filter-row{display:none}
  #netTable,#netTable tbody,#netTable tr.dt-row,#netTable tr.dt-row td{display:block;width:100%}
  #netTable thead{position:absolute;left:-9999px}
  #netTable tr.dt-row{border:1px solid var(--line);border-radius:var(--r-md);margin-bottom:var(--s2);padding:6px var(--s1);background:var(--surface)}
  #netTable tr.dt-row td{border-bottom:0;padding:6px var(--s3)}
  #netTable tr.dt-row td.cell-wrap{max-width:none}
  .net-detail{padding-left:var(--s4)}
}

/* =============================================================================
   19 · PRINT & REDUCED MOTION
   ========================================================================== */
@media print{
  .topbar,aside,.pagehead .actions,.net-quick,.filter-row,.net-exp,.card.banner button,
  .page-progress,#toast,.side-foot{display:none!important}
  #app{display:block}
  main{max-width:none;padding:0}
  .card{break-inside:avoid;border-color:#ccc;box-shadow:none}
  .dt-detail{display:none!important}
  th{position:static}
}
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
}


/* =============================================================================
   20 · SETTINGS — grouped rail + pane
   ========================================================================== */
.set-layout{display:grid;grid-template-columns:216px minmax(0,1fr);gap:var(--s6);align-items:start}
.set-rail{
  display:flex;flex-direction:column;gap:1px;position:sticky;top:calc(var(--topbar-h) + var(--s5));
  background:transparent;border:0;padding:0;max-height:calc(100vh - var(--topbar-h) - var(--s8));overflow-y:auto;
}
.set-rail-label{
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);padding:var(--s4) var(--s3) var(--s1);
}
.set-rail>.set-rail-label:first-child{padding-top:0}
.set-rail .set-pill{
  position:relative;flex:0 0 auto;justify-content:flex-start;text-align:left;
  border-radius:var(--r-sm);padding:8px var(--s3);font-size:var(--fs-base);min-height:34px;
  color:var(--ink2);background:transparent;box-shadow:none;font-weight:500;
}
.set-rail .set-pill:hover{background:color-mix(in srgb, var(--ink) 6%, transparent);color:var(--ink)}
.set-rail .set-pill.on{background:var(--accent-soft);color:var(--tint-ink);font-weight:640;box-shadow:none}
.set-rail .set-pill.on::before{
  content:"";position:absolute;left:calc(var(--s1) * -1);top:50%;transform:translateY(-50%);
  width:3px;height:18px;border-radius:0 3px 3px 0;background:var(--accent);
}
.set-sec-h{font-size:var(--fs-xl);font-weight:650;letter-spacing:-.02em;margin-bottom:var(--s1);display:flex;align-items:center;gap:var(--s2)}
.set-sec-ic{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:var(--r-sm);
  background:var(--accent-soft);color:var(--accent)}
.set-sec-ic svg{width:15px;height:15px}
.set-pane .set-intro{margin:0 0 var(--s5);max-width:78ch}
/* Rail furniture: a filter box, per-pill glyphs, and an amber unsaved-changes dot. */
.set-filter{
  margin:0 0 var(--s2);padding:6px 10px;font-size:var(--fs-sm);min-height:30px;
  border-radius:var(--r-sm);background:var(--surface);border:1px solid var(--line);
}
.set-pill-ic{display:inline-flex;width:15px;height:15px;margin-right:8px;color:var(--muted);flex:0 0 auto}
.set-pill-ic svg{width:15px;height:15px}
.set-rail .set-pill.on .set-pill-ic{color:var(--accent)}
.set-pill-dot{display:none;width:7px;height:7px;border-radius:50%;background:var(--warning);margin-left:auto;flex:0 0 auto}
.set-rail .set-pill.dirty .set-pill-dot{display:inline-block}
/* Switching tabs: content arrives, it doesn't teleport. Honors the global reduced-motion override. */
.set-sec:not([hidden]){animation:setSecIn var(--t) var(--ease)}
@keyframes setSecIn{from{opacity:.4;transform:translateY(4px)}to{opacity:1;transform:none}}
@media (max-width:980px){
  .set-layout{grid-template-columns:minmax(0,1fr);gap:var(--s4)}
  .set-rail{
    position:static;max-height:none;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;
    gap:2px;padding:3px;background:var(--surface-3);border:1px solid var(--line);border-radius:var(--r-full);
    scrollbar-width:none;
  }
  .set-rail::-webkit-scrollbar{display:none}
  .set-rail-label{display:none}
  /* the rail's filter box (its first child) shrank to a ~24px sliver in the horizontal
     strip (2026-08-25 responsive sweep) — pin a usable width and match the pill shape */
  .set-rail .set-filter{flex:0 0 150px;min-width:150px;margin:0;border-radius:var(--r-full)}
  .set-rail .set-pill{border-radius:var(--r-full);padding:6px 14px;white-space:nowrap}
  .set-rail .set-pill.on{background:var(--surface);color:var(--ink);box-shadow:var(--shadow-1)}
  .set-rail .set-pill.on::before{display:none}
}

/* grouped integrations: a heading per purpose, hidden when its cards are filtered out */
.conn-cat{margin-bottom:var(--s6)}
.conn-cat>.section-head{margin-top:0}
.conn-cat>.section-head .muted{
  font-size:var(--fs-xs);font-weight:700;background:var(--surface-3);border-radius:var(--r-full);
  padding:1px 8px;letter-spacing:0;
}

/* =============================================================================
   21 · TARGET SIZE + FINAL ICON UNIFICATION
   ========================================================================== */
/* Every close/remove control now renders an SVG rather than a ✕ glyph, so they need
   a box instead of a font size. */
.wiz-x>svg,.bp-x>svg{width:16px;height:16px;display:block}
.notif-x>svg,.sv-x>svg{width:13px;height:13px;display:block}
.icon-btn-dots>svg{width:18px;height:18px}
.icon-btn-dots{padding-top:0;letter-spacing:0}

/* Saved-view delete was 19x18 — under a comfortable target. Now a real 24px control,
   and it no longer inherits link underline behaviour. */
.sv-x{
  display:inline-flex;align-items:center;justify-content:center;
  width:24px;height:24px;margin-left:2px;border-radius:var(--r-xs);  /* 24px floor: WCAG 2.2 SC 2.5.8 */
  color:var(--muted);text-decoration:none;flex-shrink:0;
}
.sv-x:hover{background:var(--critical-soft);color:var(--critical)}

/* <details> toggles are controls, not text: they were rendering 19–22px tall. WCAG 2.2
   target size wants 24. (The 16px checkboxes are left alone deliberately — table cell
   padding leaves ~36px between adjacent ones, which meets the spacing exception.) */
summary{min-height:26px;display:flex;align-items:center;gap:5px}
.dd summary{min-height:32px}
details.poweredby>summary{min-height:26px}
.coll-archive>summary,.notif-minor>summary{min-height:26px}
summary>svg{width:14px;height:14px;flex-shrink:0}

/* =============================================================================
   22 · LEAD MAGNETS
   ========================================================================== */
.lm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:var(--s4)}
.lm-card{margin-bottom:0;display:flex;flex-direction:column;gap:var(--s2)}
/* A paused form reads as dormant before you find the switch — same idiom as .lm-frow.locked. */
.lm-card.paused{border-style:dashed;background:var(--surface-2)}
.lm-card.paused .lm-head{background:color-mix(in srgb,var(--brand-800) 88%,var(--ink))}
.badge.live{background:var(--good-soft);color:color-mix(in srgb,var(--good) 72%,var(--ink));border-color:color-mix(in srgb,var(--good) 32%,transparent)}
.badge.live::before{background:var(--good)}
.badge.paused{color:var(--muted)}
.badge.paused::before{background:var(--muted)}
/* A form's title wears the house band like every other card header (2026-08-24). Negative margins
   pull it to the card's edges — .card pads by --s5 — and the top corners follow the card's radius. */
.lm-head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);min-width:0;
  background:var(--brand-800);color:#fff;padding:10px var(--s5);
  margin:calc(var(--s5) * -1) calc(var(--s5) * -1) var(--s2);
  border-radius:calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0;
}
.lm-head h3{margin:0;font-size:var(--fs-md);font-weight:700;color:#fff;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lm-head .badge{flex-shrink:0}
/* Live and Paused have to read AGAINST the band: the light-ground pills are ~2:1 on brand-800. Live
   keeps its green as a solid dot on a white pill; paused stays quiet, in white at 70%. */
.lm-head .badge.live{background:#fff;color:color-mix(in srgb,var(--good) 78%,#0b2b1c);border-color:transparent}
.lm-head .badge.live::before{background:var(--good)}
.lm-head .badge.paused{background:rgba(255,255,255,.16);color:#fff;border-color:rgba(255,255,255,.34)}
.lm-head .badge.paused::before{background:rgba(255,255,255,.8)}
.lm-teaser{
  font-size:var(--fs-sm);color:var(--ink2);margin:0;line-height:var(--lh-snug);
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
/* Only shown when a form overrides the account-wide sender — a quiet note, not a badge, because
   the common case is the default and that needs no explanation. */
.lm-from-note{
  display:flex;align-items:center;gap:6px;margin:0;
  font-size:var(--fs-xs);color:var(--muted);min-width:0;
}

/* response count is the page's primary read — a number worth clicking, not a ghost button */
.lm-count{
  align-self:flex-start;background:var(--accent-soft);color:var(--tint-ink);border:1px solid transparent;
  box-shadow:none;font-size:var(--fs-sm);font-weight:520;padding:4px 11px;min-height:28px;border-radius:var(--r-full);
}
.lm-count strong{font-weight:700;font-variant-numeric:tabular-nums}
.lm-count:hover{background:var(--accent-soft-2);color:var(--tint-ink)}
.lm-count .muted{font-size:var(--fs-xs)}
.lm-link{margin:0}
.lm-link .copyrow-val{color:var(--ink2)}
.lm-foot{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);flex-wrap:wrap;
  margin-top:auto;padding-top:var(--s3);border-top:1px solid var(--line-soft);
}
.lm-foot .switch-row{margin:0;font-size:var(--fs-sm)}
.lm-foot-r{display:flex;align-items:center;gap:var(--s2)}
.lm-warn>div{display:flex;flex-direction:column;gap:3px;min-width:0}

/* In dialog() popups the footer sits inside the scrolling .wiz-body (runWizard keeps it
   as a sibling instead). On a long form — the lead-magnet editor is ~1000px — that put
   the primary action below the fold. Pin it to the bottom of the scroll area so Save is
   always reachable without hunting for it. */
/* `>form>` too: most form dialogs (task, deal, log-call, event, tender, organisation…) keep the
   footer inside their <form> so Enter submits — those rendered as an inset grey box floating inside
   the body padding instead of the full-bleed band every other dialog shows. Same band either way. */
dialog.wizard .wiz-body>.wiz-foot,dialog.wizard .wiz-body>form>.wiz-foot{
  position:sticky;bottom:calc(var(--s3) * -1);z-index:2;
  margin-left:calc(var(--s5) * -1);margin-right:calc(var(--s5) * -1);
  margin-bottom:calc(var(--s3) * -1);
  border-top:1px solid var(--line);
  background:color-mix(in srgb, var(--surface-2) 94%, transparent);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
}
@media (max-width:720px){
  dialog.wizard .wiz-body>.wiz-foot,dialog.wizard .wiz-body>form>.wiz-foot{margin-left:calc(var(--s4) * -1);margin-right:calc(var(--s4) * -1)}
}

/* — Data Enrich — */
.en-card{max-width:820px}
/* The tags that say where the answer came from ride in the band, so they take the band's ink. */
.en-src{display:inline-flex;align-items:center;gap:5px;flex-wrap:wrap;justify-content:flex-end}
.tablehead .tag,.tablehead .tag.auto-full{background:rgba(255,255,255,.16);color:#fff;border-color:rgba(255,255,255,.3)}
/* "Who" / "Where" inside a banded card are the INNER headers here — tinted, not another dark slab. */
.en-card .wiz-group-h{
  background:var(--accent-soft);color:var(--tint-ink);
  padding:5px var(--s3);border-radius:var(--r-xs);margin-bottom:var(--s3);
}
.en-card .wiz-group:last-of-type{margin-bottom:0}


/* inline "open in a new tab" links: the icon rides with the text */
.ext-link{display:inline-flex;align-items:center;gap:4px;white-space:nowrap}
.ext-link svg{width:11px;height:11px;flex-shrink:0;opacity:.8}

/* — Events table column budget —
   Category and Location fold into the Event and When cells, so six columns carry what
   eight did and the table stops scrolling sideways. */
.ev-name{min-width:220px;max-width:340px}
.ev-name>strong{display:block}
.ev-meta{display:flex;align-items:center;gap:5px;flex-wrap:wrap;margin-top:4px}
.ev-meta .tag{margin:0}
/* The registered-by stack sits on the meta line beside the category tag, so it takes that row's
   centre alignment (.av-stack ships align-self:flex-start for card layouts) and drops its padding.
   Faces step down to 18px to match the mini tag's height — a 22px orb sets the row height here. */
.ev-meta .av-stack{align-self:center;padding:0}
.ev-meta .av-face{width:18px;height:18px;font-size:8px}
.ev-meta .av-face.av-more{font-size:7px}
.ev-when{min-width:150px;white-space:nowrap}
.ev-when>div+div{white-space:normal;font-size:var(--fs-xs);margin-top:2px}
.ev-acts{width:1%}
.ev-acts .icon-btn{min-width:30px}
/* Interested star — quiet until active (same rule as .act-chip.draft-live: colored only when on). */
.ev-int.on{color:var(--accent);border-color:var(--accent)}
.ev-int.on svg{fill:currentColor}
/* "I have registered and got my badge" — offered only on a favourited event, because registering for
   something you have not starred is a state with nothing to mean. :has keeps that out of the JS: the
   star's own .on repaint reveals and hides this control. Three row states, none of them colour-only —
   no button at all (not favourited), thin hollow tick (favourited, still reminding), filled pill with
   a heavier tick (registered, reminders off), plus aria-pressed on the last two. */
.ev-acts:not(:has(.ev-int.on)) .ev-reg{display:none}
.ev-reg.on{background:var(--good-soft);color:var(--good);border-color:var(--good)}
.ev-reg.on svg{stroke-width:2.6}
/* Under ~1300px the sidebar leaves too little room; tighten gutters and floors rather
   than let the table scroll sideways. */
@media (max-width:1300px){
  .ev-name{min-width:180px;max-width:260px}
  .ev-when{min-width:120px}
  #evtTable th,#evtTable td,.data-table th,.data-table td{padding-left:8px;padding-right:8px}
  .ev-acts .icon-btn{min-width:26px;padding-left:4px;padding-right:4px}
  .cell-desc{-webkit-line-clamp:1;line-clamp:1}
  .ev-name{min-width:160px;max-width:230px}
  .ev-meta{gap:4px}
  .ev-meta .tag.mini{font-size:var(--fs-2xs);padding:1px 6px}
  .ev-when{min-width:106px;font-size:var(--fs-sm)}
}

/* — board columns tinted by stage —
   The tint comes from --stage, set per column from the same STATUS_DOT palette the status
   pills and badges use, so a colour carries one meaning app-wide. Applied to the column's
   top rule and its title only: enough to tell the columns apart while scanning across,
   without colouring the cards and drowning the status badges sitting on them. */
.board-col{--stage:var(--seq-3);border-top:3px solid var(--stage)}
.board-col-head{background:color-mix(in srgb, var(--stage) 9%, var(--surface-2))}
.board-col-title>span:first-child{color:color-mix(in srgb, var(--stage) 72%, var(--ink))}
.board-col-count{
  background:color-mix(in srgb, var(--stage) 16%, var(--surface));
  border-color:color-mix(in srgb, var(--stage) 30%, transparent);
  color:color-mix(in srgb, var(--stage) 74%, var(--ink));
}
.board-col.drag-over{border-color:var(--stage);box-shadow:0 0 0 2px color-mix(in srgb, var(--stage) 26%, transparent) inset}

/* =============================================================================
   23 · LEAD-MAGNET FORM BUILDER
   ========================================================================== */
/* flex, not grid: as a grid this picked up extra implicit tracks and dropped the canvas
   into the 186px column. Flex sizes the two panes explicitly and can't do that. */
.fb{display:flex;gap:var(--s3);align-items:flex-start}
.fb>.fb-pal{flex:0 0 186px;width:186px}
.fb>.fb-canvas{flex:1 1 auto;min-width:0}
.fb-pal{
  display:flex;flex-direction:column;gap:4px;padding:var(--s2);
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-md);
  position:sticky;top:0;
  /* The global aside{} sidebar rule hands every <aside> z-index:var(--z-side) (45), which put this
     palette ABOVE the dialog's sticky footer (z-index:2) — covering the Cancel button mid-scroll
     and stealing its clicks. Pin it under the footer. */
  z-index:1;
}
.fb-pal-h{
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);padding:2px 4px var(--s1);
}
.fb-pal-item{
  display:flex;align-items:center;gap:var(--s2);justify-content:flex-start;width:100%;
  background:var(--surface);border:1px solid var(--line);box-shadow:none;color:var(--ink2);
  border-radius:var(--r-sm);padding:6px 9px;min-height:32px;font-size:var(--fs-sm);font-weight:500;
  cursor:grab;
}
.fb-pal-item:hover{background:var(--accent-soft);border-color:var(--accent);color:var(--tint-ink)}
.fb-pal-item:active{cursor:grabbing}
.fb-pal-ic{display:inline-flex;color:var(--muted);flex-shrink:0}
.fb-pal-ic svg{width:14px;height:14px}
.fb-pal-item:hover .fb-pal-ic{color:var(--accent)}
.fb-pal-note{font-size:var(--fs-2xs);color:var(--muted);margin:var(--s2) 4px 0;line-height:var(--lh-snug)}
.fb-canvas{
  display:flex;flex-direction:column;gap:6px;min-height:150px;
  border:1px dashed var(--line-2);border-radius:var(--r-md);padding:var(--s2);
  background:var(--surface-2);
}
.fb-grip{display:inline-flex;width:14px;color:var(--n400);cursor:grab;flex-shrink:0}
.fb-grip svg{width:14px;height:14px}
.lm-frow.locked .fb-grip{cursor:default}
.fb-tic{display:inline-flex;color:var(--muted);flex-shrink:0}
.fb-tic svg{width:14px;height:14px}
.lm-frow[draggable="true"]{cursor:grab}
.lm-frow.dragging{opacity:.4}
.lm-frow.drop-at{border-color:var(--accent);box-shadow:0 -2px 0 0 var(--accent)}
.lm-f-req.switch-row{margin:0;gap:6px}
.lm-f-req .switch{width:34px;height:20px}
.lm-f-req .switch span::before{width:14px;height:14px}
.lm-f-req .switch input:checked+span::before{transform:translateX(14px)}
.fb-resp{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);flex-wrap:wrap}
.fb-resp strong{font-size:var(--fs-lg);font-variant-numeric:tabular-nums}
@media (max-width:720px){
  /* .fb is a FLEX row — the grid-template-columns that used to be here did nothing at all, so the
     186px palette stayed pinned beside a canvas squeezed to nothing. Stack them instead. */
  .fb{flex-direction:column}
  .fb>.fb-pal{flex:0 0 auto;width:100%}
  .fb-pal{position:static;flex-direction:row;flex-wrap:wrap}
  .fb-pal-h,.fb-pal-note{width:100%}
  .fb-pal-item{width:auto}
}

/* Connected Microsoft mailboxes in the integration wizard. One row per mailbox, its last sync
   state beside it, so a mailbox that has quietly stopped syncing is visible without opening it. */
.ms-list{display:flex;flex-direction:column;gap:2px;margin:var(--s3) 0}
.ms-row{
  display:flex;align-items:center;gap:var(--s3);padding:var(--s2) var(--s3);
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-2);
}
.ms-mail{display:flex;align-items:center;gap:6px;min-width:0;flex:1;font-size:var(--fs-sm);font-weight:560}
.ms-mail svg{width:14px;height:14px;flex-shrink:0;color:var(--muted)}
.ms-mail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ms-when{font-size:var(--fs-xs);flex-shrink:0}
/* Whose calendar it is, shown only when it is not yours — an admin looking at everyone's. */
.ms-owner{font-size:var(--fs-2xs);flex-shrink:0;background:var(--surface-3);border-radius:var(--r-full);padding:1px 8px}
.ms-acts{display:flex;gap:var(--s2);margin-top:var(--s3);flex-wrap:wrap}
@media (max-width:560px){
  .ms-row{flex-wrap:wrap}
  .ms-mail{flex:1 1 100%}
}

/* Per-collection breakdown inside the All-leads card */
/* Sits inside the card like every other block: full width of the content box, with long
   names ellipsised so a row can never widen the card past its padding. Same hairline and
   label rhythm as .kvrow. */
.coll-breakdown{
  display:flex;flex-direction:column;gap:1px;width:100%;min-width:0;
  margin-top:auto;padding-top:var(--s2);border-top:1px solid var(--line);
  font-size:var(--fs-xs);
}
.coll-breakdown>div{
  display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:var(--s2);align-items:baseline;
  color:var(--ink2);padding:2px 0;
}
.coll-breakdown>div+div{border-top:1px solid var(--line-soft)}
.coll-breakdown span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.coll-breakdown b{font-variant-numeric:tabular-nums;color:var(--ink);font-weight:620}
.coll-breakdown .uncoll{color:var(--muted)}
.coll-breakdown .uncoll b{color:var(--muted)}

/* Industry chip — categorical, not status. Fixed saturation/lightness keeps these visibly
   a different family from the green/amber/red status palette, so the two don't get confused. */
.ind-chip{
  display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  padding:1px 8px;border-radius:var(--r-full);font-size:var(--fs-2xs);font-weight:620;
  background:hsl(var(--ind) 62% 93%);color:hsl(var(--ind) 55% 30%);
  border:1px solid hsl(var(--ind) 55% 84%);
}
:root[data-theme="dark"] .ind-chip{background:hsl(var(--ind) 40% 18%);color:hsl(var(--ind) 65% 78%);border-color:hsl(var(--ind) 35% 30%)}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .ind-chip{background:hsl(var(--ind) 40% 18%);color:hsl(var(--ind) 65% 78%);border-color:hsl(var(--ind) 35% 30%)}
}

/* Newsletter: the add-unsubscriber field needs a usable width without an inline style */
/* Add-leads picker (newsletter editor): a scrollable tick-list that reads like the audience table */
.nl-unsub-input{min-width:240px;flex:0 1 320px}

/* — dashboard insight card —
   Reads as sentences, not tiles: each line is a fact plus what to do about it, linked to the list
   it names. The channel strip underneath answers "which one is landing" in one glance. */
.ins-card .ins-list{list-style:none;margin:0 0 var(--s3);padding:0;display:flex;flex-direction:column;gap:2px}
.ins-list li{display:flex;align-items:center;gap:9px;padding:var(--s2) 0;border-bottom:1px solid var(--line-soft);font-size:var(--fs-base)}
.ins-list li:last-child{border-bottom:0}
.ins-list a{color:var(--ink);text-decoration:none;display:block;flex:1;min-height:24px;line-height:1.55}
.ins-list a:hover{color:var(--accent)}
.ins-list li::before{content:"";flex:0 0 auto;width:6px;height:6px;border-radius:50%;background:var(--muted)}
.ins-list li.ins-warning::before{background:var(--warning)}
.ins-list li.ins-good::before{background:var(--good)}
.ins-chans{display:flex;flex-wrap:wrap;gap:var(--s2)}
.ins-chan{
  flex:1 1 160px;display:flex;flex-direction:column;gap:1px;
  padding:var(--s2) var(--s3);background:var(--surface-2);
  border:1px solid var(--line-soft);border-radius:var(--r-sm);
}
.ins-chan-h{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.04em;color:var(--ink2)}
.ins-chan-n{font-size:var(--fs-lg);font-weight:660;line-height:1.15}
.ins-chan-n .muted{font-size:var(--fs-sm);font-weight:400}
.ins-chan .muted{font-size:var(--fs-xs)}

/* — lead-magnet responses table —
   Every form asks for something different, so the columns after Reach are whatever that form
   defined. Free-text answers are clamped to one line (full text in the title) rather than letting
   one long paragraph set the height of every row. */
.lm-resp .lm-reach{white-space:nowrap}
.lm-resp .lm-reach a{
  display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;
  border-radius:var(--r-sm);color:var(--ink2);background:var(--surface-2);border:1px solid var(--line-soft);
}
.lm-resp .lm-reach a:hover{color:var(--accent);border-color:var(--accent)}
.lm-resp .lm-reach svg{width:14px;height:14px}
.lm-resp .lm-reach .cell-sub{max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lm-ans{display:inline-block;max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
/* a response already in the pipeline has no checkbox — this says why, in its place */
.lm-inpipe{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--good)}
.lm-inpipe svg{width:14px;height:14px}

/* — email signature composer —
   contentEditable with a toolbar, so a signature pasted out of Outlook keeps its own markup and can
   then be edited here. The editor is styled like an email body, not like a form field: what you see
   is what lands in the inbox. */
.sig-wrap{border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;background:var(--surface)}
.sig-tools{
  display:flex;flex-wrap:wrap;align-items:center;gap:3px;padding:6px;
  background:var(--surface-2);border-bottom:1px solid var(--line);
}
.sig-b{
  min-width:30px;height:28px;padding:0 7px;background:transparent;border:0;box-shadow:none;
  color:var(--ink2);font-size:var(--fs-base);line-height:1;border-radius:var(--r-sm);
}
.sig-b:hover{background:color-mix(in srgb,var(--ink) 8%,transparent);color:var(--ink)}
/* APPLIED AT THE CURSOR (2026-09-09). The toolbar painted nothing back, so bold, italic, underline
   and the alignment buttons never showed themselves as on — the only way to know was to type and
   look. `.on` is set by paintRteToolbar from the document's own queryCommandState, and it carries
   aria-pressed with it, so a screen reader is told the same thing the colour says.
   Accent-tinted rather than merely darker: this sits in a row of buttons whose hover is already a
   grey wash, and "active" has to be distinguishable from "the pointer is over it". */
.sig-b.on{background:var(--accent-soft);color:var(--tint-ink);box-shadow:inset 0 0 0 1px var(--accent)}
.sig-b.on:hover{background:var(--accent-soft)}
.sig-sel{width:auto;min-width:74px;height:28px;padding:0 22px 0 8px;font-size:var(--fs-sm);margin:0}
/* A select showing a real value rather than its placeholder reads as a fact, not a prompt. */
.sig-sel:not([value=""]){color:var(--ink)}
.sig-sep{width:1px;height:18px;background:var(--line);margin:0 4px}
.sig-colour{position:relative;display:inline-flex;align-items:center;justify-content:center;width:30px;height:28px;border-radius:var(--r-sm);cursor:pointer;color:var(--ink2);margin:0}
.sig-colour:hover{background:color-mix(in srgb,var(--ink) 8%,transparent)}
.sig-colour svg{width:15px;height:15px;pointer-events:none}
.sig-colour input{position:absolute;inset:0;opacity:0;width:100%;height:100%;padding:0;margin:0;border:0;cursor:pointer}
.sig-edit{
  min-height:160px;max-height:420px;overflow-y:auto;padding:var(--s4);
  line-height:1.55;color:var(--ink);background:var(--surface);
}
.sig-edit:focus{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--accent) 28%,transparent)}
.sig-edit:empty::before{content:attr(data-placeholder);color:var(--muted)}
.sig-edit img{max-width:100%;height:auto}
.sig-edit table{border-collapse:collapse}
.sig-edit a{color:var(--accent)}
.sig-foot{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;padding:var(--s3) var(--s4);border-top:1px solid var(--line);background:var(--surface-2)}
.sig-foot .muted{font-size:var(--fs-xs);flex:1;min-width:180px}
/* the signature preview in the queue renders the real thing, so it needs email-body styling
   rather than the app's own */
.oq-sig-html{
  margin-top:3px;padding:var(--s2) var(--s3);background:var(--surface-2);
  border-left:2px solid var(--line);border-radius:0 var(--r-sm) var(--r-sm) 0;
  font-size:var(--fs-sm);line-height:1.5;color:var(--ink2);overflow-x:auto;
}
.oq-sig-html img{max-width:100%;height:auto}
.oq-sig-html table{border-collapse:collapse}

/* — connection status chips (Outreach sender cards) —
   Two meanings only, same as everywhere: ok / needs attention. "Unchecked" is deliberately its own
   amber state rather than a green guess — claiming a domain is verified when the provider never
   answered is worse than saying nothing. */
.conn-chip{
  display:inline-flex;align-items:center;gap:5px;padding:2px 8px;border-radius:var(--r-full);
  font-size:var(--fs-xs);font-weight:560;border:1px solid var(--line);color:var(--ink2);background:var(--surface-2);
}
.conn-chip .conn-dot{width:6px;height:6px;border-radius:50%;background:var(--muted)}
.conn-chip.ok{background:var(--good-soft);border-color:color-mix(in srgb,var(--good) 30%,transparent);color:var(--good)}
.conn-chip.ok .conn-dot{background:var(--good)}
.conn-chip.wait{background:var(--warning-soft);border-color:color-mix(in srgb,var(--warning) 30%,transparent);color:var(--warning)}
.conn-chip.wait .conn-dot{background:var(--warning)}
.conn-chip.bad{background:var(--critical-soft);border-color:color-mix(in srgb,var(--critical) 30%,transparent);color:var(--critical)}
.conn-chip.bad .conn-dot{background:var(--critical)}
.oq-idmeta{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-top:3px}
.oq-idmeta .muted{font-size:var(--fs-xs);text-transform:none;letter-spacing:0}
.oq-idlink.warn{color:var(--warning);font-weight:600}
.oq-senders{gap:var(--s4) var(--s6)}
.oq-dates{display:inline-flex;align-items:center;gap:6px}
.oq-dates input{width:auto}
/* the channel tabs carry the providers' own marks, which are full colour */
.oq-tabs button svg{width:16px;height:16px;border-radius:3px}

/* — settings: per-tab save bar —
   A settings tab can hold several cards, each with its own Save. It was easy to change three things
   and walk away having saved one. This appears the moment anything in the tab is edited and saves
   the whole tab; the per-card buttons still work for anyone who prefers them. */
.set-savebar{
  position:sticky;bottom:0;z-index:5;display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  margin-top:var(--s4);padding:var(--s3) var(--s4);
  background:var(--surface);border:1px solid color-mix(in srgb,var(--warning) 40%,var(--line));
  border-radius:var(--r-md);box-shadow:var(--shadow-2);
}
.set-savebar .set-dirty{flex:1;min-width:150px;font-size:var(--fs-sm);font-weight:560;color:var(--warning)}
.set-savebar .set-dirty::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--warning);margin-right:8px;vertical-align:middle}

/* — settings groups —
   Every tab is a short stack of titled groups: what the group is for on one line, its fields, and
   its own Save in a footer. Consistent shape everywhere means the Save button is always in the same
   place, rather than sometimes under a field and sometimes after a paragraph. */
.set-group{display:flex;flex-direction:column;gap:var(--s3)}
.set-group-h{display:flex;flex-direction:column;gap:3px}
.set-group-h h3{margin:0}
/* WHAT THE AI HAS LEARNED (2026-09-09). The compiled per-industry brief, rendered rather than hidden:
   the owner has to be able to read what the AI believes about their business and fix the profile if
   it is wrong, which is the difference between a setting and a black box.
   Each industry is a <details> so a long table does not bury the button under it. */
.pb-list{display:flex;flex-direction:column;gap:2px;margin:10px 0}
.pb-ind{border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-2)}
.pb-ind>summary{cursor:pointer;padding:7px 10px;font-size:var(--fs-sm);list-style:revert}
.pb-ind[open]>summary{border-bottom:1px solid var(--line)}
.pb-dl{margin:0;padding:8px 10px;display:grid;grid-template-columns:auto 1fr;gap:4px 10px;font-size:var(--fs-xs)}
/* The label column is nowrap so "Never claim" cannot wrap to two lines beside a one-line value and
   throw the rows out of alignment down the card. */
.pb-dl dt{color:var(--muted);white-space:nowrap}
.pb-dl dd{margin:0;color:var(--ink)}
.set-group-h .muted{margin:0;font-size:var(--fs-sm);line-height:var(--lh-snug);max-width:78ch}
.set-group>label,.set-group>.formgrid,.set-group>textarea,.set-group>p{margin:0}
.set-group-f{
  display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  padding-top:var(--s3);border-top:1px solid var(--line-soft);
}
.set-group-f .muted{font-size:var(--fs-xs)}
.set-group-note{margin-left:auto;text-align:right}
/* ICP's groups sit in the two-column grid; the wide ones span it */
#icpForm .set-group:nth-child(n+3){grid-column:1/-1}
/* The two ICP cards sit level (owner, 2026-09-10): .grid2 aligns items to the start, which left
   "Company size & proof" ending a screen above "Who you're aiming at". Stretch the row, and let the
   proof-points textarea take the slack — it is the field that benefits from the room. */
#icpForm.grid2{align-items:stretch}
#icpForm .icp-proof{flex:1;display:flex;flex-direction:column;min-height:0}
#icpForm .icp-proof textarea{flex:1;min-height:96px;resize:vertical}

/* — the Dashboard's global date filter —
   It sits in the page head because it governs the whole page: every number below it answers for
   that window, which is the thing the old two-hardcoded-sections layout could never say. */
.date-filter{margin:0}
.date-filter select{min-width:150px}
.pagehead .actions .oq-dates{margin-left:var(--s2)}

/* — newsletter editor —
   Blocks are repeatable items inside one card, not nested cards: a card inside a card doubled every
   border, shadow and radius for no gain. The grip arms the drag, the same rule the form builder
   uses, so a drag can never start from inside a field you were selecting text in. */
.nl-block{
  border:1px solid var(--line);border-radius:var(--r-md);padding:var(--s3);
  background:var(--surface);margin-bottom:var(--s2);
}
.nl-block:hover{border-color:var(--line-2)}
.nl-block[draggable="true"]{cursor:grabbing}
.nl-bhead{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s2);flex-wrap:wrap}
.nl-bhead>strong{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--ink2)}
.nl-bhead>.muted{font-size:var(--fs-xs);flex:1;min-width:120px}
.nl-grip{display:inline-flex;width:18px;height:18px;color:var(--muted);cursor:grab;flex-shrink:0}
.nl-grip svg{width:100%;height:100%}
.nl-bacts{display:inline-flex;gap:2px;flex-shrink:0}
.nl-add{display:flex;flex-wrap:wrap;gap:var(--s2);margin-top:var(--s3);padding-top:var(--s3);border-top:1px solid var(--line-soft)}
/* a banner that carries a consequence, not just information */
.card.banner.warn{border-color:color-mix(in srgb,var(--warning) 45%,var(--line));background:var(--warning-soft)}
.card.banner.warn p{margin:0}
.banner-acts{display:flex;gap:var(--s2);flex-wrap:wrap;margin-top:var(--s3)}

/* The collection signal controls (.icon-act.sig, .coll-sigline) went with the Clay watcher on
   2026-09-09. Removed rather than left behind: a selector matching nothing is how a class ends up
   styled by nothing when somebody reuses the name, which this stylesheet has already been caught by. */
/* a gated control still reads, it just can't be pressed — and its tooltip says what would change that */
button[aria-disabled="true"],.icon-act[disabled]{cursor:not-allowed}
/* the signature editor's own table affordances — contentEditable gives you none */
.sig-edit td,.sig-edit th{border:1px dashed transparent;min-width:24px}
.sig-edit:focus td,.sig-edit:focus th{border-color:color-mix(in srgb,var(--accent) 22%,transparent)}

/* — one table component, everywhere —
   The Leads table is the reference: a header that reads as a label rather than a heading, rows that
   respond to the pointer, muted sub-lines under the primary value, ages that never wrap, and an
   empty state that says what to do rather than "no data". These rules give every other table on the
   platform the same behaviour without each page re-styling its own. */
/* No colour of its own: the header colour is decided ONCE by the `th` rule above. This copy carried a
   white that was right on the old navy band and unreadable on the light one (owner, 2026-09-11: "I
   can't read the header column names"). Size and tracking only. */
.data-table thead th{
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  white-space:nowrap;
}
.data-table tbody tr:hover{background:var(--surface-2)}
.data-table .cell-sub{font-size:var(--fs-xs);color:var(--muted);margin-top:2px}
.data-table .cell-age{white-space:nowrap;font-size:var(--fs-xs)}
.data-table td.nowrap,.data-table th.nowrap{white-space:nowrap}
.data-table .empty-cell{padding:var(--s6) var(--s4)}
.data-table .rowacts{justify-items:end}
/* Settings' own tables were the last three with no shared class at all. */
.set-table th.set-perm-h{text-align:center;font-size:var(--fs-2xs);min-width:64px;white-space:normal;line-height:1.2}
.set-table td{vertical-align:middle}
/* The role name owns its column: ten permission columns squeezed it to four characters and broke
   "manager" mid-word. The permission heads already wrap; the name must not. */
.set-table td:first-child{white-space:nowrap}
/* the activity log keeps its denser type, everything else it now shares */
.activity-table{font-size:var(--fs-sm)}

/* — rich message body (Outreach queue) —
   Calibri 11pt, because that is what actually leaves the building: the body and the signature are
   one letter, so the editor writes in the same face and size the recipient will read. */
.rte{
  font-family:Calibri,"Segoe UI",Helvetica,Arial,sans-serif;font-size:14.7px;line-height:1.5;
  color:var(--ink);background:var(--surface);
}
.oq-body-rte{
  min-height:120px;max-height:340px;overflow-y:auto;padding:var(--s3);
  border:1px solid var(--line);border-top:0;border-radius:0 0 var(--r-sm) var(--r-sm);
}
.oq-body-rte:focus{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--accent) 26%,transparent)}
.rte-tools{border:1px solid var(--line);border-radius:var(--r-sm) var(--r-sm) 0 0;margin-top:6px}
.oq-body-rte a{color:var(--accent)}
/* the signature composer writes in the same face for the same reason */
.sig-edit{font-family:Calibri,"Segoe UI",Helvetica,Arial,sans-serif;font-size:14.7px}
/* ideas are suggestions, not alarms: same rows, folded away, and never counted in the bell */
.notif-ideas>summary{color:var(--accent)}
.notif-ideas .notif-item{border-left:2px solid color-mix(in srgb,var(--accent) 35%,transparent)}

/* — loading status —
   The chrome never goes away and the content underneath stays put; this strip says what is being
   fetched right now, and the main area dims rather than blanking. A page that blanks to grey bars
   on every navigation feels like a reload even when it isn't one. */
.page-progress{
  position:sticky;top:0;z-index:calc(var(--z-pop) - 1);
  display:flex;align-items:center;gap:var(--s2);
  padding:5px var(--s5);background:var(--surface);border-bottom:1px solid var(--line);
  font-size:var(--fs-xs);color:var(--ink2);
}
.pp-bar{
  position:relative;flex:0 0 74px;height:3px;border-radius:2px;overflow:hidden;
  background:color-mix(in srgb,var(--accent) 18%,transparent);
}
.pp-bar::after{
  content:"";position:absolute;inset:0 auto 0 0;width:40%;border-radius:2px;background:var(--accent);
  animation:ppSlide 1.05s var(--ease) infinite;
}
@keyframes ppSlide{0%{left:-40%}100%{left:100%}}
.pp-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#main.is-loading{opacity:.55;transition:opacity var(--t)}
#main{transition:opacity var(--t)}
@media (prefers-reduced-motion:reduce){ .pp-bar::after{animation:none;width:100%} }

/* — a metric broken down inside its own tile —
   Three compartments, each divided by a rule in that channel's brand colour. The point is to read
   three numbers at a glance rather than parse a sentence; the colours match the Outreach tabs, so
   the same colour means the same channel wherever you are. */
.tile-parts{display:flex;margin-top:var(--s3);padding-top:var(--s2);border-top:1px solid var(--line-soft)}
.tile-part{
  flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;
  padding-left:9px;border-left:2px solid var(--part);
}
.tile-part:first-child{padding-left:8px}
.tile-part b{font-size:var(--fs-md);font-weight:680;line-height:1.1;font-variant-numeric:tabular-nums;color:var(--ink)}
.tile-part i{
  font-style:normal;font-size:var(--fs-2xs);font-weight:640;letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tile-part-ic{display:inline-flex;width:13px;height:13px;border-radius:3px;overflow:hidden;margin-top:2px}
.tile-part-ic svg{width:100%;height:100%;display:block}
.tile .tile-parts{padding-right:44px}
@media (max-width:520px){ .tile .tile-parts{padding-right:0} }
/* visually hidden, still announced — the compartment shows a brand mark, screen readers get the word */
.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 form — phone control, source editor
   ========================================================================== */
/* Country code + number as one field. The code is select-only on purpose: a dial code that can be
   typed is a dial code that gets typed wrong ("00971", "+9715"), and every downstream match on the
   number then fails silently. */
/* Wraps rather than squeezes. The country select needs room to show a country NAME — that is what
   makes the list usable — so on a narrow form it takes its own line instead of both controls
   shrinking until neither can be read. */
/* wrap, not nowrap (2026-08-25 responsive sweep): in a tight formgrid column (profile at
   tablet width: 155px) the 95px dial button left the number input 53px. Wrapping puts the
   number on its own full-width line ONLY when dial + 120px basis don't fit — anywhere the
   field fits today, nothing moves. */
.phone-field{display:flex;flex-wrap:wrap;gap:6px;align-items:center;min-width:0}
.phone-field .phone-num{flex:1 1 120px;min-width:0;font-variant-numeric:tabular-nums}

.login-help{margin:10px 0 0;text-align:center}
.login-help .linkish{font-size:var(--fs-sm)}

/* ---------- ICP editor ----------
   Chip lists in place of newline-separated textareas: countable at a glance, removable one at a
   time, and de-duplicated as you go. */
.icp-lists{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--s4)}
.chip-label{display:block;font-size:var(--fs-sm);font-weight:600;margin-bottom:6px;color:var(--ink2)}
.chipset{display:flex;flex-direction:column;gap:6px;min-width:0}
.chip-list{
  display:flex;flex-wrap:wrap;gap:5px;align-content:flex-start;
  min-height:64px;max-height:180px;overflow-y:auto;
  padding:7px;border:1px solid var(--line-2);border-radius:var(--r-sm);background:var(--surface);
}
.chip{
  display:inline-flex;align-items:center;gap:4px;max-width:100%;
  padding:3px 4px 3px 9px;border-radius:var(--r-full);
  background:var(--accent-soft);color:var(--tint-ink);
  font-size:var(--fs-xs);font-weight:560;line-height:1.5;
}
.chip>button{
  display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;min-height:0;padding:0;flex-shrink:0;
  border:0;border-radius:50%;background:transparent;color:inherit;opacity:.65;box-shadow:none;
}
.chip>button:hover{opacity:1;background:color-mix(in srgb,var(--accent) 24%,transparent)}
.chip>button svg{width:11px;height:11px}
.chip-add{display:flex;gap:6px}
.chip-add input{flex:1;min-width:0}
/* The count is the whole point of chips over a textarea — and an empty list is a real warning,
   because scoring silently uses nothing. */
.chip-count{font-size:var(--fs-2xs);margin:0}
.chip-count.empty{color:var(--warning)}
.icp-gap{border-color:color-mix(in srgb,var(--warning) 40%,transparent)}

/* Services as two real fields per row, not a pipe-delimited line. */
.svc-rows{display:flex;flex-direction:column;gap:6px}
.svc-row{display:grid;grid-template-columns:minmax(120px,1fr) minmax(160px,2fr) auto;gap:6px;align-items:center}
@media (max-width:560px){ .svc-row{grid-template-columns:1fr auto} .svc-row .svc-desc{grid-column:1/-1} }

/* Activity log: the person on top, the login they signed in with underneath. The login stays
   because it is what the audit row actually records and what older rows are keyed on. */
.act-who{display:block;font-weight:560}
.act-login{display:block;font-size:var(--fs-2xs);line-height:1.4}

/* Country picker. A button showing flag + dial code, opening a searchable list — see phoneField()
   in app.js for why this cannot be a native <select>. Same control the public lead-magnet form
   uses, so the interaction is identical wherever a phone number is typed. */
.dialpick{position:relative;flex:0 0 auto}
.dialbtn{
  display:inline-flex;align-items:center;gap:6px;cursor:pointer;
  /* Same box model as the input beside it — padding, border and LINE-HEIGHT — so the two halves
     are the same height. Matching only the padding left the button 2px short, because its content
     is a flag glyph rather than a line of text. */
  min-height:34px;padding:7px 9px 7px 10px;line-height:1.5;
  background:var(--surface);color:var(--ink);
  border:1px solid var(--line-2);border-radius:var(--r-sm);
  font:inherit;font-variant-numeric:tabular-nums;white-space:nowrap;
}
/* `button:hover` is (0,1,1) and beats a bare `.dialbtn`, so without restating the background here
   the control turned solid accent the moment the pointer touched it — it has to keep looking like
   the input it sits beside, not like a submit button. */
.dialbtn:hover{background:var(--surface-2);border-color:color-mix(in srgb, var(--ink) 24%, var(--line-2))}
.dialbtn:focus-visible{outline:none;border-color:var(--accent);box-shadow:var(--ring-focus)}
/* Same reason: the shared button rules nudge on press and cast a shadow. This is a field, not a
   button that submits something. */
.dialbtn,.dialbtn:hover,.dialbtn:active{box-shadow:none}
.dialbtn:active{transform:none}
.dialflag{font-size:17px;line-height:1}
.dialnum{font-weight:560}
/* The chevron, drawn rather than shipped as an icon — it is 6px of geometry. */
.dialcar{
  width:0;height:0;margin-left:1px;
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--muted);
}
.dialbtn[aria-expanded="true"] .dialcar{transform:rotate(180deg)}

.dialpop{
  position:absolute;top:calc(100% + 5px);left:0;z-index:var(--z-pop);
  width:clamp(240px,78vw,320px);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  box-shadow:var(--shadow-3);padding:var(--s2);
}
.diallist{list-style:none;margin:0;padding:0;max-height:280px;overflow-y:auto;outline:none}
.diallist li{
  display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:var(--r-xs);
  cursor:pointer;font-size:var(--fs-sm);
}
.diallist li:hover,.diallist li.active{background:var(--accent-soft);color:var(--tint-ink)}
.diallist li.on{font-weight:650}
/* The list holds focus (it is the thing arrow keys drive), so the active row carries a ring rather
   than relying on the hover tint alone — otherwise there is no visible cursor at all. */
.diallist:focus-visible li.active{box-shadow:inset 0 0 0 2px var(--accent)}
.diallist .f{flex:0 0 auto;font-size:16px;line-height:1}
/* Name takes the slack, code is pinned right — the layout the whole control is for. */
.diallist .n{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.diallist .c{flex:0 0 auto;color:var(--muted);font-variant-numeric:tabular-nums}
.diallist li:hover .c,.diallist li.active .c{color:inherit}
@media (max-width:560px){ .dialpop{width:min(300px,86vw)} }

.src-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.src-row{display:flex;gap:6px;align-items:center}
.src-row .src-name{flex:1;min-width:0}
.src-empty{padding:4px 0}
.src-add{display:flex;gap:6px;margin-top:10px}
.src-add input{flex:1;min-width:0}
.src-builtin{display:flex;flex-wrap:wrap;gap:6px}

/* Icon + label buttons (the outreach queue's Approve / Send / Skip). The glyph carries the meaning
   at a glance; the word stays because an icon-only row of three actions is a guessing game. */
.btn-ic{display:inline-flex;align-items:center;gap:6px}
.btn-ic svg{width:14px;height:14px;flex-shrink:0}

/* The signature note doubles as the failure message for adding an image, so it has to beat the
   .muted colour it normally carries — a red warning rendered grey is not a warning. */
#sigNote.err{color:var(--critical);font-weight:600}

/* =============================================================================
   Live LLM status — collapsed into the topbar (2026-08-17): one icon per
   connected provider next to the bell, its dot carrying health at a glance;
   clicking an icon opens the full card as a bubble reusing .notif-pop.
   ========================================================================== */
.llm-anchor{position:relative;flex-shrink:0;display:flex;align-items:center}
/* One tight, evenly-spaced button cluster: s1 between the AI icons, and the bell pulled in to the
   same s1 (offsetting the topbar's s3 flex gap — only while the AI anchor is actually showing). */
.llm-icons{display:flex;align-items:center;gap:var(--s1)}
.llm-anchor:not([hidden])+.notif-anchor{margin-left:calc(var(--s1) - var(--s3))}
.llm-tb svg{width:18px;height:18px;display:block}
.llm-pop-ic{display:inline-flex;color:var(--muted);flex-shrink:0}
.llm-pop-ic svg{width:16px;height:16px}
/* Top-right, where the bell's badge sits — one place on a topbar icon means "state of this button",
   and two different corners for the same idea is what made it read as decoration. */
.llm-tb-dot{
  position:absolute;top:-3px;right:-3px;width:9px;height:9px;border-radius:50%;
  background:var(--n400);box-shadow:0 0 0 2px var(--surface);
}
.llm-tb-dot.good{background:var(--good)}
.llm-tb-dot.warning{background:var(--warning)}
.llm-tb-dot.critical{background:var(--critical)}
.llm-tb-dot.muted{background:var(--n400)}
/* Three states, readable without opening anything: the one answering the next call, the ones standing
   by behind it, and the ones the router is skipping. */
.llm-tb.next{color:var(--accent)}
.llm-tb.next::after{
  content:"";position:absolute;inset:1px;border-radius:var(--r-full);pointer-events:none;
  box-shadow:0 0 0 1.5px color-mix(in srgb,var(--accent) 55%,transparent);
  background:color-mix(in srgb,var(--accent) 10%,transparent);
}
.llm-tb.standby{opacity:.8}
/* Out of the chain: dimmed AND desaturated, so it does not read as merely "further down the list". */
.llm-tb.paused{opacity:.5;filter:grayscale(1)}
/* The fallback order, in the bubble. The head of the chain is the one answering now. */
.llm-chain{margin:0;padding:var(--s1) var(--s3) 0;font-size:var(--fs-xs);line-height:1.7}
.llm-chain b{font-weight:650}
.llm-chain b.on{color:var(--accent)}
.llm-tb[aria-expanded="true"]{background:color-mix(in srgb, var(--ink) 9%, transparent)}
.llm-pop{width:min(300px,calc(100vw - 24px))}
.llm-pop .notif-pop-head .llm-state{margin-left:auto}
.llm-pop .llm-state.ok{background:var(--good-soft);color:color-mix(in srgb,var(--good) 72%,var(--ink))}
.llm-pop-body{padding:var(--s2)}
.llm-pop .llm-card{border:0}
.llm-pop .llm-none{padding:0 var(--s3) var(--s2);margin:0}
@media (max-width:560px){
  /* inherits .notif-pop's fixed placement; keep the fixed left/right sizing, not the 300px width */
  .llm-pop{width:auto}
  .llm-icons .llm-tb{width:29px;height:29px;min-height:29px}
}
.llm-card{
  border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s2) var(--s3);
  background:var(--surface);min-width:0;
}
/* The one that will actually answer the next call — the single most useful fact here. */
.llm-card.next{border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 16%,transparent)}
.llm-card.capped,.llm-card.off{opacity:.72}
.llm-top{display:flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap}
.llm-name{font-size:var(--fs-sm);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.llm-next{
  margin-left:auto;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  color:var(--accent);background:var(--accent-soft);border-radius:var(--r-full);padding:1px 7px;flex-shrink:0;
}
.llm-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--muted)}
.llm-dot.good{background:var(--good)}
.llm-dot.warning{background:var(--warning)}
.llm-dot.critical{background:var(--critical)}
.llm-dot.muted{background:var(--n400)}
.llm-model{font-size:var(--fs-2xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}
.llm-bar{height:4px;border-radius:var(--r-full);background:var(--surface-3);margin:6px 0 4px;overflow:hidden}
.llm-bar>i{display:block;height:100%;background:var(--accent);border-radius:inherit}
.llm-card.capped .llm-bar>i{background:var(--critical)}
.llm-card.resting .llm-bar>i{background:var(--warning)}
/* One figure per row since each line carries used AND left — two nowrap spans forced onto one
   line is what made them overlap. Wrapping stays allowed for narrow cards. */
.llm-figs{display:flex;flex-wrap:wrap;justify-content:flex-start;gap:2px var(--s2);font-size:var(--fs-2xs);min-width:0}
.llm-left{font-weight:650}
/* The state in words, for the cases where a provider is NOT answering. Tinted to match its dot, but
   the words are what carry the meaning — the colour only reinforces it. */
.llm-state{
  font-size:var(--fs-2xs);font-weight:650;line-height:1.5;white-space:nowrap;
  padding:1px 7px;border-radius:var(--r-full);
  background:var(--surface-3);color:var(--ink2);
}
.llm-state.warning{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 72%,var(--ink))}
.llm-state.critical{background:var(--critical-soft);color:color-mix(in srgb,var(--critical) 72%,var(--ink))}
.llm-state.muted{background:var(--surface-3);color:var(--muted)}
.llm-money{color:var(--ink2);font-weight:600}
.llm-figs .muted{white-space:nowrap}
/* Why a provider is not being used. The whole reason this panel exists is that silence here
   used to be indistinguishable from working. */
.llm-why{font-size:var(--fs-2xs);margin-top:4px;line-height:1.35}
.llm-none{font-size:var(--fs-sm);color:var(--muted);margin:var(--s2) 0 0}


/* =============================================================================
   Channel-native composers (Outreach queue)
   A draft is easier to judge when it looks like the thing that will land. The only literal colours
   in this section are the three channel brand hues — a channel's own colour is the one thing a
   design token cannot supply. Everything else comes off the ramp, so both themes follow.
   ========================================================================== */

/* — email: an Outlook-style field stack — */
.oc{
  --oc-blue:#0F6CBD;
  margin-bottom:var(--s3);overflow:hidden;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
}
.oc-row{
  display:grid;grid-template-columns:74px minmax(0,1fr) auto;align-items:center;gap:var(--s2);
  min-height:34px;padding:0 var(--s3);border-bottom:1px solid var(--line);
}
.oc-lbl{margin:0;padding:0;line-height:1;font-size:var(--fs-xs);font-weight:520;letter-spacing:.02em;color:var(--muted)}
.oc-val{
  display:flex;align-items:center;flex-wrap:wrap;gap:6px;min-width:0;padding:7px 0;
  font-family:Calibri,"Segoe UI",Helvetica,Arial,sans-serif;font-size:14.7px;color:var(--ink);word-break:break-word;
}
.oc-val .err{font-size:var(--fs-xs);margin:0}
.oc-pill{
  display:inline-flex;align-items:center;max-width:100%;padding:2px 9px;border-radius:var(--r-full);
  font-size:var(--fs-sm);color:var(--ink);
  background:color-mix(in srgb,var(--oc-blue) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--oc-blue) 24%,transparent);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.oc-in{
  width:100%;min-height:0;padding:7px 0;background:transparent;border:0;border-radius:0;box-shadow:none;
  font-family:Calibri,"Segoe UI",Helvetica,Arial,sans-serif;font-size:14.7px;color:var(--ink);
}
.oc-in:hover:not(:disabled){border:0;background:color-mix(in srgb,var(--ink) 3%,transparent)}
.oc-in:focus{outline:none;border:0;box-shadow:inset 0 -2px 0 var(--oc-blue);background:color-mix(in srgb,var(--oc-blue) 5%,transparent)}
.oc-row-subject{border-bottom:1px solid var(--line)}
.oc-subj{font-weight:600}
.oc-note{margin:5px var(--s3) 0;font-size:var(--fs-2xs);color:var(--muted)}
.oc .rte-tools{margin:var(--s2) 0 0;border-left:0;border-right:0;border-radius:0}
.oc .oq-body-rte{min-height:200px;max-height:420px;padding:var(--s4) var(--s3);background:var(--surface);border:0;border-radius:0}
.oc .oq-body-rte:focus{box-shadow:inset 3px 0 0 var(--oc-blue)}
.oc .oq-sig{margin:0;padding:0 var(--s3) var(--s3)}
.oc .oq-sig .muted{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.04em}
.oc .oq-sig-html,.oc .oq-sig pre{background:transparent;border-left:2px solid color-mix(in srgb,var(--oc-blue) 30%,transparent)}
.oc .oq-hint{margin:0;padding:0 var(--s3) var(--s3);font-size:var(--fs-xs)}
@media (max-width:560px){
  .oc-row{grid-template-columns:60px minmax(0,1fr);row-gap:0}
}

/* — LinkedIn: avatar-led, rounder, one blue button, and a live cap — */
.oq-item[data-chan="linkedin"]{ --li:#0A66C2; --li-ink:#0A66C2; --li-on:#fff; }
.li-head{
  display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s4);margin-bottom:var(--s2);
  background:var(--surface-2);border:1px solid color-mix(in srgb,var(--li-ink) 22%,var(--line));border-radius:var(--r-md);
}
.li-head .lead-avatar-ring{width:48px;height:48px;min-width:48px}
.li-who{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
.li-name{font-size:var(--fs-lg);font-weight:660;color:var(--ink);min-width:0;overflow-wrap:anywhere}
.li-name a{color:inherit;text-decoration:none}
.li-name a:hover{color:var(--li-ink);text-decoration:underline;text-underline-offset:2px}
.li-headline{font-size:var(--fs-sm);color:var(--ink2)}
.li-co{font-size:var(--fs-xs);color:var(--muted)}
.li-from{margin:0 0 var(--s3);font-size:var(--fs-xs);color:var(--muted)}
.li-from strong{color:var(--ink2);font-weight:600}
.li-note{
  position:relative;margin-bottom:var(--s3);padding:var(--s3) var(--s4) var(--s2);
  background:var(--surface);border:1px solid color-mix(in srgb,var(--li-ink) 22%,var(--line-2));border-radius:var(--r-md);
}
.li-note:focus-within{border-color:var(--li-ink);box-shadow:0 0 0 3px color-mix(in srgb,var(--li-ink) 22%,transparent)}
.li-note-lbl{display:block;margin:0;font-size:var(--fs-xs);font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.li-note textarea{
  margin:2px 0 0;padding:0;min-height:104px;width:100%;background:transparent;border:0;border-radius:0;
  box-shadow:none;line-height:1.5;color:var(--ink);resize:vertical;
}
.li-note textarea:hover:not(:disabled),.li-note textarea:focus{background:transparent;border:0;outline:none;box-shadow:none}
/* :focus stripped the ring with nothing in its place, so the note composer could not be located by
   keyboard. Same inset ring .sig-edit and .oq-body-rte already use -- :focus-visible so a mouse click
   still gets the clean borderless look the rule above is for. */
.li-note textarea:focus-visible{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--accent) 26%,transparent)}
.li-note-foot{display:flex;align-items:center;gap:var(--s3);margin-top:var(--s2);padding-top:var(--s2);border-top:1px solid var(--line)}
.li-note-hint{font-size:var(--fs-xs);color:var(--muted);flex:1;min-width:0}
.li-count{
  padding:2px 9px;border-radius:var(--r-full);white-space:nowrap;font-size:var(--fs-xs);font-weight:600;
  font-variant-numeric:tabular-nums;color:var(--muted);background:var(--surface-2);border:1px solid var(--line);
}
.li-note.over{border-color:var(--critical)}
.li-note.over .li-count{color:var(--critical);background:var(--critical-soft);border-color:color-mix(in srgb,var(--critical) 34%,transparent)}
.li-over{margin:var(--s2) 0 0;font-size:var(--fs-xs);color:var(--critical)}
.li-over[hidden]{display:none}
.oq-item[data-chan="linkedin"] .oq-acts button,
.oq-item[data-chan="linkedin"] .oq-acts a.small{border-radius:var(--r-full);padding:6px 16px;min-height:32px;font-weight:600}
.oq-item[data-chan="linkedin"] .oq-acts button:not(.ghost){background:var(--li);border-color:var(--li);color:var(--li-on);box-shadow:none}
.oq-item[data-chan="linkedin"] .oq-acts button:not(.ghost):hover:not(:disabled){
  background:color-mix(in srgb,var(--li) 86%,var(--ink));border-color:color-mix(in srgb,var(--li) 86%,var(--ink));
}
.oq-item[data-chan="linkedin"] .oq-acts button.ghost,
.oq-item[data-chan="linkedin"] .oq-acts a.ghost{
  background:transparent;color:var(--li-ink);font-weight:600;box-shadow:none;
  border-color:color-mix(in srgb,var(--li-ink) 42%,transparent);
}

/* — WhatsApp: the thread it will become. Backdrop is a CSS weave, never an image file. — */
.wa-chat{
  --wa-green:#25D366; --wa-read:#53BDEB;
  --wa-out:color-mix(in srgb,var(--wa-green) 20%,var(--surface));
  --wa-paper:color-mix(in srgb,var(--wa-green) 5%,var(--surface-2));
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;margin-bottom:var(--s3);
}
.wa-head{display:flex;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3);background:var(--surface-2);border-bottom:1px solid var(--line)}
.wa-head .lead-avatar-ring{width:32px;height:32px;min-width:32px}
.wa-who{display:flex;flex-direction:column;min-width:0;flex:1}
.wa-who strong{font-size:var(--fs-sm);font-weight:640;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wa-who>span{font-size:var(--fs-xs);color:var(--muted);font-variant-numeric:tabular-nums}
.wa-from{font-size:var(--fs-2xs);color:var(--muted);white-space:nowrap;text-transform:uppercase;letter-spacing:.04em;flex-shrink:0}
.wa-thread{
  display:flex;flex-direction:column;align-items:flex-end;gap:var(--s2);
  padding:var(--s4) var(--s4) var(--s4) var(--s3);min-height:96px;background-color:var(--wa-paper);
  background-image:
    radial-gradient(circle at 50% 50%, color-mix(in srgb,var(--ink) 6%,transparent) 0 1px, transparent 1.5px),
    repeating-linear-gradient(45deg, transparent 0 10px, color-mix(in srgb,var(--ink) 3%,transparent) 10px 11px),
    repeating-linear-gradient(135deg, transparent 0 10px, color-mix(in srgb,var(--ink) 3%,transparent) 10px 11px);
  background-size:20px 20px,auto,auto;
}
.wa-day{
  align-self:center;padding:3px 10px;border-radius:var(--r-full);font-size:var(--fs-2xs);font-weight:640;
  color:var(--ink2);background:color-mix(in srgb,var(--surface) 84%,transparent);
}
.wa-bubble{
  position:relative;max-width:min(560px,86%);padding:6px 10px;background:var(--wa-out);color:var(--ink);
  border-radius:var(--r-md) 0 var(--r-md) var(--r-md);font-size:var(--fs-md);line-height:1.42;
}
.wa-bubble::after{content:"";position:absolute;top:0;right:-7px;width:0;height:0;border-left:8px solid var(--wa-out);border-bottom:9px solid transparent}
.wa-text{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}
/* Reserves the tail of the last line so the stamp tucks INSIDE the bubble instead of colliding
   with the final word. Sized for "12:00 PM" plus a double tick plus breathing room. */
.wa-text::after{content:"";display:inline-block;width:80px}
.wa-text:empty::before{content:"No message yet";opacity:.55;font-style:italic}
.wa-stamp{
  position:absolute;right:9px;bottom:4px;display:inline-flex;align-items:center;gap:4px;
  font-size:var(--fs-2xs);font-variant-numeric:tabular-nums;user-select:none;color:color-mix(in srgb,var(--ink) 50%,transparent);
}
.wa-ticks{display:inline-flex;align-items:center}
.wa-ticks svg{width:16px;height:11px;display:block}
.wa-ticks svg.wa-clock{width:11px;height:11px}
.wa-ticks.wa-read{color:var(--wa-read)}
.wa-ticks.wa-fail{color:var(--critical)}
.wa-ticks.wa-wait{opacity:.7}
.wa-composer{display:flex;align-items:flex-end;gap:var(--s2);padding:var(--s2) var(--s3);background:var(--surface-2);border-top:1px solid var(--line)}
.wa-input{
  flex:1;width:auto;min-width:0;margin:0;min-height:38px;max-height:160px;resize:none;overflow-y:auto;
  padding:9px var(--s4);border-radius:var(--r-full);background:var(--surface);border:1px solid var(--line-2);line-height:1.4;
}
.wa-input:focus{border-color:var(--wa-green);box-shadow:0 0 0 3px color-mix(in srgb,var(--wa-green) 22%,transparent)}
.wa-send{flex:0 0 auto;width:38px;height:38px;min-height:38px;padding:0;border:0;border-radius:var(--r-full);background:var(--wa-green);color:#fff}
.wa-send svg{width:17px;height:17px;transform:translateX(-1px)}
.wa-send:hover:not(:disabled){background:color-mix(in srgb,var(--wa-green) 84%,var(--ink))}
.wa-send:disabled{background:var(--surface-3);color:var(--muted);opacity:1}
@media (max-width:560px){ .wa-from{display:none} .wa-bubble{max-width:92%} }

/* Dark mode for all three. Each brand hue is carried toward the ink rather than swapped for another
   colour: #0F6CBD and #0A66C2 cannot hold text on a dark surface, and WhatsApp's own dark bubble is
   a deep teal-green, not a pale mint. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .oc{--oc-blue:color-mix(in srgb,#0F6CBD 46%,var(--ink))}
  :root:not([data-theme="light"]) .oc-in:hover:not(:disabled){background:color-mix(in srgb,var(--ink) 6%,transparent)}
  :root:not([data-theme="light"]) .oc-pill{background:color-mix(in srgb,var(--oc-blue) 18%,transparent)}
  :root:not([data-theme="light"]) .oq-item[data-chan="linkedin"]{
    --li:color-mix(in srgb,#0A66C2 46%,#fff); --li-ink:color-mix(in srgb,#0A66C2 46%,#fff); --li-on:var(--ink-invert);
  }
  :root:not([data-theme="light"]) .wa-chat{
    --wa-out:color-mix(in srgb,var(--wa-green) 30%,#04120e);
    --wa-paper:color-mix(in srgb,var(--wa-green) 4%,var(--surface-2));
  }
  :root:not([data-theme="light"]) .wa-stamp{color:color-mix(in srgb,var(--ink) 62%,transparent)}
  :root:not([data-theme="light"]) .wa-day{background:color-mix(in srgb,var(--surface-3) 88%,transparent)}
}
:root[data-theme="dark"] .oc{--oc-blue:color-mix(in srgb,#0F6CBD 46%,var(--ink))}
:root[data-theme="dark"] .oc-in:hover:not(:disabled){background:color-mix(in srgb,var(--ink) 6%,transparent)}
:root[data-theme="dark"] .oc-pill{background:color-mix(in srgb,var(--oc-blue) 18%,transparent)}
:root[data-theme="dark"] .oq-item[data-chan="linkedin"]{
  --li:color-mix(in srgb,#0A66C2 46%,#fff); --li-ink:color-mix(in srgb,#0A66C2 46%,#fff); --li-on:var(--ink-invert);
}
:root[data-theme="dark"] .wa-chat{
  --wa-out:color-mix(in srgb,var(--wa-green) 30%,#04120e);
  --wa-paper:color-mix(in srgb,var(--wa-green) 4%,var(--surface-2));
}
:root[data-theme="dark"] .wa-stamp{color:color-mix(in srgb,var(--ink) 62%,transparent)}
:root[data-theme="dark"] .wa-day{background:color-mix(in srgb,var(--surface-3) 88%,transparent)}

/* A toast that carries an action needs room for it, and the button has to read as clickable against
   the toast's inverted surface. */
#toast.has-action{display:inline-flex;align-items:center;gap:var(--s3)}
.toast-act{
  flex-shrink:0;padding:3px 10px;min-height:26px;font-size:var(--fs-sm);font-weight:650;
  color:var(--page);background:color-mix(in srgb,var(--page) 22%,transparent);
  border:1px solid color-mix(in srgb,var(--page) 45%,transparent);border-radius:var(--r-full);
}
.toast-act:hover{background:color-mix(in srgb,var(--page) 34%,transparent);border-color:var(--page)}
.toast-act:focus-visible{outline:2px solid var(--page);outline-offset:2px}

/* =============================================================================
   A/B/C variants — badge, matrix, and the picker's extra button
   ========================================================================== */
.vbadge{
  display:inline-flex;align-items:center;justify-content:center;min-width:19px;height:19px;padding:0 5px;
  border-radius:var(--r-xs);font-size:var(--fs-2xs);font-weight:800;letter-spacing:.02em;flex-shrink:0;
  background:var(--surface-3);color:var(--ink2);border:1px solid var(--line);
}
/* Three distinct hues so a variant is recognisable at a glance in a long queue — from the sequential
   ramp, not new colours, so they stay in the platform's palette and follow the theme. */
.vbadge.vA{background:color-mix(in srgb,var(--seq-5) 20%,transparent);color:var(--seq-7);border-color:color-mix(in srgb,var(--seq-5) 38%,transparent)}
.vbadge.vB{background:color-mix(in srgb,var(--warning) 18%,transparent);color:color-mix(in srgb,var(--warning) 72%,var(--ink));border-color:color-mix(in srgb,var(--warning) 34%,transparent)}
.vbadge.vC{background:color-mix(in srgb,var(--good) 18%,transparent);color:color-mix(in srgb,var(--good) 72%,var(--ink));border-color:color-mix(in srgb,var(--good) 34%,transparent)}
.badge.replied{background:var(--good-soft);color:color-mix(in srgb,var(--good) 72%,var(--ink));border-color:color-mix(in srgb,var(--good) 32%,transparent)}
.badge.replied::before{background:var(--good)}
/* the Mark-replied button, once it is on */
.oq-acts .btn-ic.on{background:var(--good-soft);border-color:color-mix(in srgb,var(--good) 34%,transparent);color:color-mix(in srgb,var(--good) 74%,var(--ink))}

.abc-card{margin-bottom:var(--s4)}
.abc-grid{display:grid;grid-template-columns:minmax(0,1fr) 56px 66px 62px minmax(120px,1.1fr);gap:var(--s2) var(--s3);align-items:center}
/* Seven columns once clicks + meetings joined the matrix. */
.abc-grid.abc-grid-6{grid-template-columns:minmax(0,1fr) 48px 74px 74px 56px 64px minmax(110px,1fr)}
.abc-h{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-weight:650}
.abc-h.abc-rate,.abc-grid>.abc-n{text-align:right}
.abc-name{display:flex;align-items:center;gap:var(--s2);min-width:0;font-size:var(--fs-sm)}
.abc-name span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.abc-n{font-variant-numeric:tabular-nums;font-size:var(--fs-sm);color:var(--ink2)}
.abc-rate{display:flex;align-items:center;justify-content:flex-end;gap:var(--s2);font-variant-numeric:tabular-nums}
.abc-bar{flex:1;max-width:110px;height:6px;border-radius:var(--r-full);background:var(--surface-3);overflow:hidden}
.abc-bar>i{display:block;height:100%;background:var(--seq-5);border-radius:inherit}
/* 184px of fixed columns plus a 120px floor on the rate column puts a hard 352px minimum on this
   grid, which overflows its card on a phone. Tighten the numbers and drop the bar — the percentage
   beside it says the same thing, and a number that fits beats a bar that does not. */
@media (max-width:560px){
  .abc-grid{grid-template-columns:minmax(0,1fr) 40px 50px 48px minmax(52px,auto);gap:var(--s2)}
  .abc-grid.abc-grid-6{grid-template-columns:minmax(0,1fr) 36px 52px 52px 44px 48px minmax(48px,auto)}
  .abc-bar{display:none}
}
/* Only the CONFIRMED winner is coloured as one — an early leader is drawn in the neutral ramp. */
.abc-bar>i.win{background:var(--good)}
.abc-verdict{margin:var(--s3) 0 0;font-size:var(--fs-sm)}
/* Header row on each approach's editor card: the letter chip ties the wording to its row in the
   matrix above, and the mini stat line keeps the numbers next to what's being edited. */
.abc-ed-h{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s2);flex-wrap:wrap}
.abc-ed-stats{font-size:var(--fs-xs);font-variant-numeric:tabular-nums}
.abc-empty p{margin:var(--s2) 0 0;font-size:var(--fs-sm);line-height:1.5}

/* Meeting-link switch — same strip treatment as Rephrase below it, on every channel. The URL is
   allowed to shrink and ellipsis rather than wrap: a booking address is long, and a two-line link
   pushes the actions around every time a draft repaints. */
.bk-bar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2);margin:var(--s3) 0 0;padding-top:var(--s3);border-top:1px dashed var(--line)}
.bk-tick{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-sm);font-weight:600;cursor:pointer;flex-shrink:0}
.bk-tick input{cursor:pointer;accent-color:var(--accent)}
.bk-tick:has(input:disabled){cursor:default;color:var(--muted)}
.bk-tick:has(input:disabled) input{cursor:default}
.bk-url{flex:1 1 0;min-width:0;font-size:var(--fs-xs);font-family:var(--mono,ui-monospace,monospace);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bk-off{flex:1 1 0;min-width:0;font-size:var(--fs-xs)}
.bk-note{flex:1 1 100%;font-size:var(--fs-xs);margin-top:2px}
.bk-note.err{color:var(--critical);font-weight:600}

/* Rephrase strip — sits between the message and the actions, on every channel. */
.rp-bar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2);margin:var(--s3) 0 0;padding-top:var(--s3);border-top:1px dashed var(--line)}
.rp-lbl{font-size:var(--fs-xs);color:var(--muted);font-weight:650;text-transform:uppercase;letter-spacing:.04em}
.rp-btn{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-sm)}
.rp-btn.on{border-color:var(--accent);background:var(--accent-soft);color:var(--tint-ink)}
.rp-note{flex:1 1 100%;font-size:var(--fs-xs);margin-top:2px}
.rp-note.err{color:var(--critical);font-weight:600}

/* =============================================================================
   Meetings — month grid + next-up list
   ========================================================================== */
.mt-card{margin-bottom:var(--s4)}
.mt-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden}
/* Weekday strip in the house dark teal-green with white type — it reads as the grid's header rather
   than as a first, empty row of cells. Same token in dark mode: it is already a dark surface, and a
   lighter header there would invert the hierarchy. */
.mt-head{background:color-mix(in srgb, var(--accent) 7%, var(--surface));padding:6px 8px;font-size:var(--fs-2xs);font-weight:800;text-transform:uppercase;letter-spacing:.05em;color:var(--ink);text-align:center;border-bottom:2px solid color-mix(in srgb, var(--accent) 45%, var(--line))}
.mt-cell{background:var(--surface);min-height:92px;padding:5px 6px;display:flex;flex-direction:column;gap:3px;min-width:0}
/* Days from the neighbouring months are present for shape, not for reading. */
.mt-cell.other{background:var(--surface-2)}
.mt-cell.other .mt-daynum{color:var(--n400)}
.mt-cell.today{box-shadow:inset 0 0 0 2px var(--accent)}
/* Today's number sits in a filled red disc — the rectangle around the cell stays, it answers
   "which cell" while the disc answers "which date" at a glance. Fixed size so a 1-digit and a
   2-digit date are the same circle. */
.mt-cell.today .mt-daynum{align-self:flex-start;display:inline-flex;align-items:center;justify-content:center;
  min-width:18px;height:18px;padding:0 4px;border-radius:999px;background:var(--critical);color:#fff;font-weight:800}
.mt-daynum{font-size:var(--fs-2xs);font-weight:650;color:var(--ink2);line-height:1}
.mt-chip{
  display:block;width:100%;text-align:left;padding:2px 6px;border-radius:var(--r-xs);
  font-size:var(--fs-2xs);line-height:1.35;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  background:var(--accent-soft);color:var(--tint-ink);border:1px solid transparent;min-height:0;
}
.mt-chip .mt-t{font-variant-numeric:tabular-nums;font-weight:700;opacity:.75;margin-right:3px}
.mt-chip.pending{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 72%,var(--ink))}
/* Events calendar rides the same grid. Owner's rule: starred is ORANGE, not-starred is GREEN — so
   both states are colored and the pair reads as a status, not as "one is switched off". Same
   --good/--warning tokens (and the same color-mix ink recipe) the badges use, so both flip with the
   theme; the ★ prefix in the markup stays because colour alone must not carry it. Past events dim
   the same way .ev-past table rows do, on either colour. */
.mt-chip.ev{background:var(--good-soft);color:color-mix(in srgb,var(--good) 72%,var(--ink));border-color:color-mix(in srgb,var(--good) 30%,transparent)}
.mt-chip.ev.int{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 72%,var(--ink));border-color:var(--warning)}
/* Registered: still favourited, so still ORANGE — the ✓ beside the ★ says it is handled, and the
   outline relaxes back to the soft one so the starred events still needing action are the ones
   wearing the hard border. No third colour. */
.mt-chip.ev.int.done{border-color:color-mix(in srgb,var(--warning) 30%,transparent)}
.mt-chip.ev.past{opacity:.55}
/* A cancelled booking stays visible and struck through — it explains a gap in the day. */
.mt-chip.cancelled{background:var(--surface-3);color:var(--muted);text-decoration:line-through}
/* Hover, LAST so it beats every variant above it. A chip is a <button>, but the global
   button:hover (0,1,1) loses to a .mt-chip.pending/.ev (0,2,0) background — so the fill stayed pale
   while this rule turned the label white, and the text vanished. It paints the fill itself now. */
.mt-grid .mt-chip:hover{background:var(--accent-dk);border-color:var(--accent);color:#fff}
.mt-more{font-size:var(--fs-2xs)}
@media (max-width:820px){
  .mt-cell{min-height:64px}
  .mt-chip .mt-t{display:none}
}

/* =============================================================================
   Settings — 12-hour send-window range, and the anti-spam channel cards
   ========================================================================== */
.timerange{display:flex;align-items:flex-end;gap:var(--s3);flex-wrap:wrap}
.tr-part{flex:1 1 160px;min-width:0;margin:0}
.tr-arrow{padding-bottom:10px;color:var(--muted);font-size:var(--fs-lg)}
.tr-sel{min-width:130px}
/* Which weekdays sending is allowed — pill checkboxes, one per day. */
.dayrow{display:flex;gap:var(--s2);flex-wrap:wrap;margin:var(--s3) 0}
.daychk{
  display:inline-flex;align-items:center;gap:6px;margin:0;cursor:pointer;
  font-size:var(--fs-sm);font-weight:560;color:var(--ink2);
  border:1px solid var(--line);border-radius:var(--r-full);padding:4px 12px;background:var(--surface);
}
.daychk:hover{border-color:var(--accent)}
.daychk:has(input:checked){background:var(--accent-soft);border-color:color-mix(in srgb,var(--accent) 40%,transparent);color:var(--tint-ink)}
.daychk input{margin:0;accent-color:var(--accent)}

/* The whole sending policy in one line above the channel cards. */
.as-summary{margin:0 0 var(--s3);font-size:var(--fs-sm)}
.as-chan-h{display:flex;align-items:center;gap:var(--s2)}
.as-chan-dot{width:9px;height:9px;border-radius:50%;background:var(--oc,var(--muted));flex:0 0 auto}
.as-caps{margin-bottom:var(--s3)}
.as-idh{margin:var(--s4) 0 var(--s2);font-size:var(--fs-sm);text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
/* "Daily limits" / "Pacing" / "Warm-up" inside a channel card. Same label treatment as .as-idh above
   it, because they are the same kind of thing — a divider inside one card, not a heading over a new
   one. A third weight here would read as a third level of nesting that does not exist. */
.as-sub{margin:var(--s4) 0 var(--s2);font-size:var(--fs-sm);text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
.as-sub:first-of-type{margin-top:0}
/* The rotation pool itself: one editable row per sending identity, its cap, what it has used today
   and an un-park button when it is sitting in cooldown. */
.as-pool{margin:0 0 var(--s3)}
.as-pool .data-table input:not([type="checkbox"]){width:100%;min-width:110px}
.as-pool .data-table td{vertical-align:middle}
.as-pool-acts{display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;margin-top:var(--s2)}
.as-rules{list-style:none;margin:0 0 var(--s2);padding:0;display:flex;flex-direction:column;gap:1px;
  border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden;background:var(--line)}
.as-rule{display:flex;align-items:flex-start;gap:var(--s2);padding:8px var(--s3);background:var(--surface);font-size:var(--fs-sm)}
.as-rule-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;margin-top:6px;background:var(--muted)}
.as-rule.on .as-rule-dot{background:var(--good)}
.as-rule.part .as-rule-dot{background:var(--warning)}
.as-rule.off .as-rule-dot{background:var(--n400)}
.as-rule-txt{flex:1;min-width:0;line-height:1.45}
/* Why a rule is not enforced. The whole value of listing it is saying this out loud — a toggle that
   looks active while nothing checks it is worse than not showing the rule. */
.as-rule-why{display:block;font-size:var(--fs-xs);color:var(--muted);margin-top:2px}
.as-rule-state{flex-shrink:0;font-size:var(--fs-2xs);font-weight:700;text-transform:uppercase;letter-spacing:.04em;
  padding:2px 8px;border-radius:var(--r-full);background:var(--surface-2);color:var(--muted);white-space:nowrap}
.as-rule.on .as-rule-state{background:var(--good-soft);color:color-mix(in srgb,var(--good) 72%,var(--ink))}
.as-rule.part .as-rule-state{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 74%,var(--ink))}
@media (max-width:640px){
  .as-rule{flex-wrap:wrap}
  .as-rule-state{margin-left:16px}
}

/* =============================================================================
   Notification pop-up — three tiers, nothing folded away
   ========================================================================== */
.notif-tier-h{
  display:flex;align-items:center;gap:6px;
  padding:8px 12px 4px;font-size:var(--fs-2xs);font-weight:750;
  text-transform:uppercase;letter-spacing:.05em;color:var(--muted);
  position:sticky;top:0;background:var(--surface);z-index:1;
}
.notif-tier-n{
  margin-left:auto;font-variant-numeric:tabular-nums;padding:1px 7px;border-radius:var(--r-full);
  background:var(--surface-3);color:var(--ink2);font-weight:700;
}
.notif-tier-h.alarm{color:var(--critical)}
.notif-tier-h.alarm .notif-tier-n{background:var(--critical-soft);color:var(--critical)}
.notif-tier-h.warning{color:color-mix(in srgb,var(--warning) 78%,var(--ink))}
.notif-tier-h.warning .notif-tier-n{background:var(--warning-soft);color:color-mix(in srgb,var(--warning) 78%,var(--ink))}
.notif-tier-h.reminder{color:var(--accent)}
.notif-tier-h.reminder .notif-tier-n{background:var(--accent-soft);color:var(--accent)}

/* The left rail is the tier, so severity reads before the words do. */
.notif-item.n-alarm{border-left-color:var(--critical);background:color-mix(in srgb,var(--critical) 5%,var(--surface))}
.notif-item.n-warning{border-left-color:var(--warning)}
.notif-item.n-reminder{border-left-color:var(--accent)}
/* Only the alarm tier moves. If everything pulses, nothing is urgent. */
.notif-row.tier-alarm .notif-dot{animation:notifPulse 1.6s ease-in-out infinite}
@keyframes notifPulse{ 0%,100%{transform:scale(1);opacity:1} 50%{transform:scale(1.18);opacity:.65} }
/* Long text is summarised to fit; the full sentence is on the row's tooltip. */
.notif-text{display:block;line-height:1.4;overflow-wrap:anywhere}
.notif-pop-body{max-height:min(70vh,560px);overflow-y:auto}

/* Locked sender rows — someone else's personal identity, shown but not editable. */

/* Micro-interactions (compact pass): a felt press on every button, a soft lift on interactive
   cards. Both honour prefers-reduced-motion via the global override further up. */
button:not(:disabled):active,.btn:not(:disabled):active{transform:translateY(1px)}
.conn-card{transition:border-color var(--t),box-shadow var(--t),transform var(--t)}
.conn-card:hover{border-color:color-mix(in srgb, var(--accent) 30%, var(--line));box-shadow:var(--shadow-2);transform:translateY(-1px)}

/* — Sources pages polish (2026-08-15) — */
/* Past events stay for context but read as history. */
.ev-past td{opacity:.55}
.ev-past:hover td{opacity:.8}
/* Group divider between upcoming and past — a label, not a row, so it must not read as data. */
.ev-divider td{
  padding:14px 12px 6px;font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);border-bottom:1px solid var(--line);background:transparent;
}
/* Two-line prose clamp with the full text on the title tooltip — used by dense table cells. */
.clamp2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* Due-date chips shared by Events and Tenders. */
.tag.due-warn{background:var(--warning-soft);color:var(--warning)}
.tag.due-past{background:var(--surface-3);color:var(--muted)}
/* Data Enrich: form and result side by side once there's room — the result used to land
   below the fold, which read as "nothing happened". */
.en-layout{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s5);align-items:start}
@media (min-width:1080px){.en-layout{grid-template-columns:minmax(0,7fr) minmax(0,5fr)}}
.en-layout>.card,.en-result>.card{margin-bottom:0}
.en-result{position:sticky;top:calc(var(--topbar-h) + var(--s5))}
.en-placeholder{border-style:dashed;background:transparent;box-shadow:none}
.en-placeholder p{margin:0;font-size:var(--fs-sm);line-height:var(--lh)}
.en-copy{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:copy;text-align:right}
.en-copy:hover{color:var(--accent);text-decoration:underline;text-underline-offset:2px}

/* — Draft-outreach channel buttons: one per platform, in the platform's own colour — */
.outreach-chan-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s3)}
@media (max-width:560px){.outreach-chan-grid{grid-template-columns:minmax(0,1fr)}}
.outreach-chan{
  --oc:var(--accent);
  display:flex;flex-direction:column;align-items:center;gap:6px;min-height:0;
  padding:var(--s4) var(--s3);border-radius:var(--r-md);cursor:pointer;text-align:center;
  background:color-mix(in srgb, var(--oc) 9%, var(--surface));
  border:1px solid color-mix(in srgb, var(--oc) 32%, var(--line));
  color:var(--ink);font:inherit;white-space:normal;
  transition:background var(--t),border-color var(--t),transform var(--t);
}
.outreach-chan:hover:not(:disabled){background:color-mix(in srgb, var(--oc) 16%, var(--surface));border-color:var(--oc);transform:translateY(-1px)}
.outreach-chan:disabled{opacity:.45;cursor:not-allowed}
.outreach-chan b{font-size:var(--fs-md)}
.outreach-chan .oc-ic{display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;
  border-radius:var(--r-sm);background:color-mix(in srgb, var(--oc) 16%, transparent);color:var(--oc)}
.outreach-chan .oc-ic svg{width:17px;height:17px}
.outreach-chan .oc-why{font-size:var(--fs-xs);color:var(--muted);overflow-wrap:anywhere}
/* Channel colours are one vocabulary app-wide: the picker buttons and the anti-spam channel
   headers read the same --oc, so the classes are generic rather than scoped to the picker. */
/* Playbook: sticky jump-nav + section anchors. scroll-margin keeps a jumped-to heading clear of
   the sticky bar instead of underneath it. */
.pb-nav{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  padding:10px 12px;margin:0 0 var(--s3);background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-md);box-shadow:var(--shadow-1);
  /* 26 sections is 26 chips. Sticky + unbounded would cover most of a phone screen, so cap it and
     let the chips scroll inside their own bar. */
  max-height:38vh;overflow:auto}
.pb-nav .pb-find{min-height:30px;width:200px;flex:1 1 160px}
.pb-jump{cursor:pointer;text-decoration:none}
.pb-jump:hover{border-color:var(--accent);color:var(--accent)}
.pb-sec{scroll-margin-top:64px;display:block;border-top:1px solid var(--line)}
.pb-sec:first-of-type{border-top:0}
.pb-sec>summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:8px}
.pb-sec>summary::-webkit-details-marker{display:none}
.pb-sec>summary::after{content:"";width:7px;height:7px;border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:rotate(-45deg);transition:transform .15s ease;margin-left:auto;flex:none}
.pb-sec[open]>summary::after{transform:rotate(45deg)}
.pb-sec>summary:hover h3{color:var(--accent)}
/* Spinning state for icon buttons that are waiting on a round-trip (calendar re-sync). Only the
   svg turns — spinning the whole button would wobble its border. */
.icon-btn.spin svg{animation:icospin 1s linear infinite}
@keyframes icospin{to{transform:rotate(360deg)}}
/* Composer autosave flag — visible only for a beat after a save lands. */
.oq-savedflag{opacity:0;transition:opacity .25s ease;color:var(--good);border-color:color-mix(in srgb,var(--good) 40%,transparent)}
.oq-savedflag.show{opacity:1}
/* Approve/Skip confirmation: the message being acted on, as a compact card. */
.conf-msg{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;margin:0 0 12px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-sm)}
.conf-msg .conf-chan{flex:0 0 auto;margin-top:1px;color:var(--oc);border-color:color-mix(in srgb,var(--oc) 40%,transparent)}
.conf-msg-who{min-width:0}
.conf-subj{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;margin-top:2px}
.conf-what{font-size:13.5px;line-height:1.55;color:var(--ink2);margin:0 0 10px}
.conf-again{margin:0 0 4px;font-size:12.5px}
.oc-email{--oc:var(--accent)}
.oc-wa{--oc:#25d366}
.oc-li{--oc:#0a66c2}
/* LinkedIn's brand blue is too dark to read on a dark surface — lighten it there. */
:root[data-theme="dark"] .oc-li{--oc:#5c9dd8}
@media (prefers-color-scheme:dark){ :root:not([data-theme="light"]) .oc-li{--oc:#5c9dd8} }

/* — signature editor: Word-style image selection + corner resize handle — */
.sig-img-sel{outline:2px solid var(--accent);outline-offset:1px}
.sig-img-handle{
  position:absolute;width:14px;height:14px;z-index:6;cursor:nwse-resize;
  background:var(--surface);border:2px solid var(--accent);border-radius:3px;
  box-shadow:var(--shadow-1);
}
.sig-tools .sig-b svg{width:15px;height:15px;display:block}
.sig-sel-size{width:64px;min-width:64px}

/* =============================================================================
   24 · PHONE PASS (≤640px / ≤480px) — 2026-08-15
   The phone user triages — approve/skip drafts, check leads, glance at the
   dashboard — more than they author. Everything here is additive on top of the
   980px drawer and the 720/560px blocks above, so desktop cannot regress.
   ========================================================================== */
@media (max-width:640px){
  /* The page itself must never pan sideways. Anything that accidentally overflows
     is clipped at the root; wide content is expected to scroll inside its own
     box (.table-wrap, .board, .oq-tabs) instead. clip, not hidden: hidden would
     turn the body into a scroll container and break position:sticky above it. */
  html,body{overflow-x:clip}

  /* Tables scroll INSIDE the wrap rather than crushing six-plus columns into
     375px. The floor forces the wrap (already overflow-x:auto) to take over.
     #netTable is excluded — it restacks into cards at ≤760px above, and a
     min-width would drag those cards back out to a scroll. */
  .table-wrap{-webkit-overflow-scrolling:touch}
  .table-wrap>table:not(#netTable){min-width:600px}
  /* The select-all/row checkboxes stay put while the row scrolls under them, so
     bulk triage doesn't mean scrolling back for every tick. Solid backgrounds
     because the row slides beneath; the id also outranks the row-hover tint,
     which this one cell deliberately gives up. */
  #leadTable th:first-child{left:0;z-index:calc(var(--z-sticky) + 1)}
  #leadTable td:first-child{position:sticky;left:0;z-index:1;background:var(--surface)}

  /* Touch targets in the triage flows only: 40px where a thumb does the work.
     Dense-info chips (.act-chip, 20px) are deliberately left alone — they are
     read, not pressed, and the one live draft chip opens fine via its row. */
  .pagehead .actions button,.pagehead .actions a,
  .filterbar input,.filterbar select,.filterbar button,
  .filters input,.filters select,
  .oq-acts button,.oq-acts a.small,.oq-acts a.ghost,
  .bulk-actions button,.bulk-bar select,.bulk-bar input,
  .wiz-foot button{min-height:40px}
  /* the queue's Approve / Skip / Send share the row's width instead of huddling left */
  .oq-acts>button,.oq-acts>a{flex:1 1 auto}
  /* status pill and row icons get a real target without leaving the leads table's
     budget — the table scrolls now, so the extra pixels cost nothing */
  .status-select{height:34px;min-height:34px;max-height:34px;line-height:32px}
  #leadTable .reach{width:32px;height:32px;min-height:32px}
  .rowacts .icon-btn{min-width:34px;min-height:34px}

  /* Task rows (2026-08-25 phone sweep): five action buttons cannot share one 375px line
     with the title — flex nowrap crushed .task-main (min-width:0) to a one-character-per-
     line column. The title takes the full first row; the actions wrap onto their own row
     below and share its width, each a real 40px thumb target. */
  .task-item{flex-wrap:wrap}
  .task-item .task-main{flex:1 1 100%}
  .task-item>button{flex:1 0 auto;min-height:40px}
  /* delete stays an icon-sized target, not a full-width red bar a thumb hits by accident */
  .task-item>button.danger{flex:0 0 auto;min-width:44px}

  /* My connections / onboarding channel rows (same sweep): the chip + "can't send until…"
     reason shared what was left of the line beside a 104px channel label and the Connect
     button, so the reason wrapped one word — sometimes one letter — per line. The state
     moves to its own full-width row under channel + button (the row already wraps). */
  .onb-state{flex-basis:100%;order:3}
}

@media (max-width:480px){
  /* Metric tiles: two per row, always. auto-fit already lands on two at 375px,
     but one long label could collapse it to a single unreadable column — pin it. */
  .tiles{grid-template-columns:repeat(2,minmax(0,1fr))}

  /* Filter bars: each control takes the full row. Half-width selects beside a
     half-width search read as broken at this width, and full-width is the
     native-form convention a thumb expects. */
  .filters input,.filters select,
  .filterbar input,.filterbar select{width:100%}
  .filterbar input[type=search]{min-width:0}
  .filterbar .dd,.filterbar .dd summary{width:100%}
  .filterbar .dd summary{justify-content:space-between}

  /* Dialogs and wizards go full-bleed: at 375px the 4vw gutter around a dialog
     is dead space that costs form width, and edge-to-edge is what a phone sheet
     looks like. Height is capped with the internal scroll the base rule already
     has; .wiz-foot stays sticky inside it, so the buttons stay reachable. */
  dialog{width:100vw;max-width:100vw;max-height:100dvh;border-radius:0;border-left:0;border-right:0}
  dialog.wizard,dialog.wizard.wide{width:100vw}
  dialog:not(.wizard) .wiz-foot{border-radius:0}
}

/* Newsletter membership for one lead. Rows are labels wrapping their own checkbox so the whole row
   is the hit target; a locked row keeps its checked state visible but greys out, because "already
   sent" is information the reader needs, not a row to hide. */

/* Five-minute time picker: an hour list and a minute list that only holds 00, 05 … 55. Two selects
   rather than one 288-entry list of every time in the day — nobody scrolls that.
   display:flex (not inline-flex): it sits inside a <label> next to text ("At (optional)"), and as an
   inline box the two selects rode the text baseline while `label select{margin-top:5px}` pushed only
   them — not the colon — down. As a block it drops below the label text like every other control,
   and flex:1 1 0 makes the two selects exactly equal width, filling the grid column the same way the
   date input beside it does. */
.time-pick{display:flex;align-items:center;gap:var(--s1);margin-top:5px}
.time-pick select{flex:1 1 0;width:auto;min-width:0;margin-top:0}
.tp-sep{color:var(--muted);font-weight:600;flex:none}

/* The bell's count badge. The shared .nav-badge is sized for a sidebar row; on a 34px icon button it
   covered a third of the bell and read as a blob rather than a count. Smaller, and nudged to the
   corner with a ring in the topbar colour so it still separates from the icon behind it. */
.tb-icon .nav-badge{
  top:-3px;right:-3px;min-width:15px;height:15px;padding:0 4px;font-size:10px;line-height:1;
  box-shadow:0 0 0 2px var(--surface);
}

/* — Team KPIs (admin dashboard): numeric columns read as columns, not ragged text — */
.team-kpi th.num,.team-kpi td.num{text-align:right;font-variant-numeric:tabular-nums}
.team-kpi td:first-child{white-space:nowrap}
.team-kpi td:first-child .av-face{vertical-align:middle;margin-right:6px}
/* Overdue is the one column that is a problem, not a stat — but only when it is non-zero. */
.team-kpi td.warn-cell{color:var(--critical);font-weight:650}

/* — Quick Bug Report: the floating button and its popover (all users), + the owner's feedback page — */
.bug-fab{
  position:fixed;right:18px;bottom:18px;z-index:var(--z-pop);
  width:44px;height:44px;min-height:44px;padding:0;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--critical-soft);color:var(--critical);border:1px solid color-mix(in srgb,var(--critical) 30%,transparent);
  box-shadow:var(--shadow-2);cursor:pointer;
  transition:background var(--t-fast),color var(--t-fast);
}
.bug-fab svg{width:20px;height:20px}
.bug-fab:hover{background:color-mix(in srgb,var(--critical) 16%,var(--surface))}
/* open state: same button becomes the close control — the glyphs cross-fade, the button never moves */
.bug-fab .bug-fab-x{position:absolute;font-size:17px;font-weight:600;opacity:0;transition:opacity var(--t-fast)}
.bug-fab.open svg{opacity:0}
.bug-fab.open .bug-fab-x{opacity:1}
.bug-fab.open{background:var(--surface-3);color:var(--ink);border-color:var(--line)}
.bug-pop{
  position:fixed;right:18px;bottom:72px;z-index:var(--z-pop);
  width:min(360px,calc(100vw - 24px));
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-3);overflow:hidden;
  transform-origin:bottom right;
  animation:bugPopIn var(--t-slow) var(--ease-spring) both;
}
@keyframes bugPopIn{
  from{opacity:0;transform:translateY(10px) scale(.9)}
  60%{opacity:1}
  to{opacity:1;transform:none}
}
.bug-pop-h{
  display:flex;align-items:center;gap:8px;padding:10px 12px;
  background:var(--critical-soft);border-bottom:1px solid var(--line);color:var(--ink);
}
.bug-pop-h svg{width:17px;height:17px;color:var(--critical);flex-shrink:0}
.bug-pop-h .icon-btn{margin-left:auto}
.bug-pop-b{display:flex;flex-direction:column;gap:10px;padding:12px}
.bug-page-row{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:var(--fs-xs)}
.bug-pop-b textarea{resize:vertical;min-height:88px}
.bug-send-row{display:flex;align-items:center;justify-content:space-between;gap:10px}
.bug-send-row .net-seg button{display:inline-flex;align-items:center;gap:5px}
.bug-note{font-size:var(--fs-xs);margin:0}
.bug-dot{width:8px;height:8px;border-radius:50%;display:inline-block;flex-shrink:0}
.bug-dot.low{background:var(--good,#12805c)}
.bug-dot.medium{background:var(--warning,#b98a00)}
.bug-dot.high{background:var(--critical)}
/* the owner's list */
.bug-list{display:flex;flex-direction:column}
.bug-row{padding:12px 4px;border-bottom:1px solid var(--line)}
.bug-row:last-child{border-bottom:0}
.bug-row.cleared{opacity:.62}
.bug-row-top{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.bug-row-top .small{margin-left:auto}
.bug-who{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-sm);font-weight:600}
.bug-who .av-face{margin-left:0}
.bug-when{font-size:var(--fs-xs)}
.bug-msg{margin:7px 0 0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--fs-base)}
.bug-sev-high{color:var(--critical)}
/* phones: clear the bottom mobile nav and let the panel breathe */
@media (max-width:640px){
  .bug-fab{bottom:74px;right:12px}
  .bug-pop{right:12px;bottom:126px}
}

/* bug popover: the page select shares the row with its auto-detected hint */
.bug-page-row select{flex:1;min-width:0}

/* — Meeting booking link (My profile): three option cards, the live one marked — */
.bk-now{margin:0 0 12px;font-size:var(--fs-sm)}
.bk-now.bad strong{color:var(--critical)}
.bk-opts{display:flex;flex-direction:column;gap:10px}
.bk-opt{border:1px solid var(--line);border-radius:var(--r-md);padding:12px;background:var(--surface)}
.bk-opt.on{border-color:color-mix(in srgb,var(--accent) 55%,var(--line));box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 14%,transparent)}
.bk-opt-h{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}
.bk-opt-h>div{display:flex;flex-direction:column;gap:2px;min-width:0}
.bk-opt-sub{font-size:var(--fs-xs);font-weight:400}
.bk-live{margin-left:auto;background:var(--accent-soft);color:var(--tint-ink);flex-shrink:0}
.bk-reqs{list-style:none;margin:8px 0 0;padding:0;display:flex;flex-direction:column;gap:4px}
.bk-req{display:flex;align-items:baseline;gap:7px;font-size:var(--fs-xs)}
.bk-req span{font-weight:700;flex-shrink:0}
.bk-req.ok span{color:var(--good)}
.bk-req.no span{color:var(--critical)}
.bk-req.no{color:var(--ink2)}
.bk-hours{margin-top:10px}

/* newsletter add-leads picker: collection select + search share one bar */

/* — Newsletter weekly engine: idea cards + the calendar's plan chip — */
/* The audience's search bar and pager (owner, 2026-09-09: 50 a page, searchable). `.filterbar`
   already handles the search input; this only stops the bar reserving the full width it does on a
   page-level filter row, since it holds one field inside a settings card. */
.nl-aud-bar{margin-bottom:var(--s3)}
.nl-aud-bar input[type=search]{max-width:340px}
/* The count and the pager on one line, and they SEPARATE — the count is the answer to "how many is
   this", read left to right, and the pager is a control, which belongs on the edge a thumb reaches. */
.nl-aud-foot{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;
  margin-top:var(--s2);padding-top:var(--s2);border-top:1px solid var(--line-soft);font-size:var(--fs-xs);
}
.nl-aud-pager{display:flex;align-items:center;gap:var(--s2)}
/* A disabled arrow stays in place rather than disappearing at the ends of the list: a pager whose
   buttons come and go moves the one next to it under the cursor mid-click. */
.nl-aud-pager button:disabled{opacity:.45}
/* Mid-fetch. Dimmed, never emptied — a table that blanks between pages reads as a list that lost its
   contents, and the rows on screen are still the truth until the new ones arrive. */
#nlAudBox[aria-busy="true"]{opacity:.55;transition:opacity var(--t-fast)}
.nl-ideas .tablehead{margin-bottom:8px}
/* The head now carries the switch that turns the whole feature on as well as Fresh ideas — the
   Weekly setup card that used to hold the switch went on 2026-09-09. Wraps, because on a narrow
   column a switch plus a button plus the heading does not fit one line. */
.nl-ideas-acts{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;justify-content:flex-end}
.nl-ideas-acts .switch-row{margin:0;white-space:nowrap;font-size:var(--fs-xs)}
.nl-idea-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
.nl-idea{border:1px solid var(--line);border-radius:var(--r-md);padding:12px;background:var(--surface);display:flex;flex-direction:column;gap:6px}
.nl-idea strong{line-height:1.35}
.nl-idea p{margin:0;font-size:var(--fs-xs);flex:1}
.nl-idea-meta{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:var(--fs-2xs)}
.nl-idea .small{align-self:flex-start}
/* The week's score, 0-100 in tens, top-right of each idea card. Three bands, colour AND number:
   green (>=70) this week's trend squarely meets a service we sell, amber (40-60) one half holds,
   red (<40) neither — the owner's rule, 2026-09-10. Tokens only, both themes. */
.nl-idea{position:relative}
.nl-idea .nl-score{
  position:absolute;top:10px;right:10px;width:32px;height:32px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  font-size:var(--fs-xs);font-weight:760;line-height:1;cursor:help;
}
.nl-idea strong{padding-right:40px}
.nl-score.hi{background:var(--good-soft);color:var(--good);border:1px solid var(--good)}
.nl-score.mid{background:var(--warning-soft);color:var(--warning);border:1px solid var(--warning)}
.nl-score.lo{background:var(--critical-soft);color:var(--critical);border:1px solid var(--critical)}
/* The auto-send switch in the page head, level with the buttons beside it. */
.pagehead .nl-auto{margin:0;font-size:var(--fs-sm);white-space:nowrap}
.pagehead .nl-from{display:inline-flex;align-items:center;gap:6px;margin:0;font-size:var(--fs-sm);color:var(--muted);white-space:nowrap}
.pagehead .nl-from .inline-sel{max-width:240px}
/* Auto pilot's one-sentence status on the ideas card: what it picked, what it made, where that is now. */
.nl-auto-status{margin:0 0 var(--s3);padding:8px 12px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);color:var(--ink2);font-size:var(--fs-sm)}
.nl-auto-status.warn{border-color:var(--warning);background:var(--warning-soft);color:var(--ink)}
.nl-auto-status a{font-weight:600}
.mt-chip.nl-plan{border-style:dashed;color:var(--muted);background:transparent}
.mt-chip.nl-plan:hover{color:var(--tint-ink);background:var(--accent-soft);border-color:var(--accent)}

/* System Feedback: the select-all bar and per-row checkboxes */
.bug-selbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding-bottom:10px;border-bottom:1px solid var(--line);margin-bottom:4px}
.bug-selbar .chk{display:inline-flex;align-items:center;gap:7px;font-size:var(--fs-sm);cursor:pointer}
.bug-selacts{display:inline-flex;align-items:center;gap:8px;margin-left:auto}
.bug-row-top .bug-sel{margin:0;flex-shrink:0}

/* — hover highlight everywhere a row or cell is a thing you can act on (owner, 2026-08-25) — */
/* calendar cells (Meetings, Events, Newsletter share the grid) — the cell lifts, its chips already do */
.mt-cell:not(.mt-out):hover{background:color-mix(in srgb, var(--accent) 5%, transparent)}
/* System Feedback rows — same accent wash as table rows, so "hoverable" reads the same everywhere */
.bug-row:hover{background:color-mix(in srgb, var(--accent) 5%, transparent)}
.bug-row{border-radius:var(--r-sm);padding-left:8px;padding-right:8px;margin:0 -8px;transition:background var(--t-fast)}

/* =============================================================================
   25 · MICRO-MOTION + FOCUS + SCROLLBAR AUDIT (2026-08-25 trend pass)
   Three closing sweeps, all additive:
   · every hover state that changed colour instantly now eases over var(--t-fast) —
     buttons/links got it in their base rules (03/BASE); this block covers the
     menu rows, chip-labels, list options and calendar cells that are not buttons.
   · chip-style <label>s wrapping a hidden/small checkbox get the accent ring on
     the CHIP when the inner input has keyboard focus (same device .sig-chip and
     .lm-file already used), and the inner 16px ring is retired so there is one.
   · inner scrollers advertise thin scrollbars in Firefox too — scrollbar-width
     does not inherit from the html rule, so each scroll container names it.
   ========================================================================== */
.pop-item,.cmdk-item,.ac-item,.dd-opt,.exp-row,.diallist li,
.conn-suggest-row,.crumbs a,.tb-brand,.daychk,.oq-sum,.dd summary,.mt-cell,.mt-chip,
.sig-colour,.lm-file .btn-like,.nl-block,summary{
  /* box-shadow/transform stay listed so the button-based rows here (.pop-item, .mt-chip)
     keep the press feedback their base button rule eases — this rule wins the cascade. */
  transition:background var(--t-fast) var(--ease),color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease),
             box-shadow var(--t-fast) var(--ease),transform var(--t-fast) var(--ease);
}
/* keyboard focus lands on the pill, not on the 16px box hidden inside it */
.daychk:has(input:focus-visible),.dd-opt:has(input:focus-visible),.exp-row:has(input:focus-visible),
.chk:has(input:focus-visible),
.sig-colour:has(input:focus-visible){outline:2px solid var(--accent);outline-offset:2px}
.daychk input:focus-visible,.dd-opt input:focus-visible,.exp-row input:focus-visible,
.chk input:focus-visible{outline:none;box-shadow:none}
/* the sortable header cells sit on the dark brand band, where the accent ring is ~1.9:1 —
   same white-ring treatment .tablehead controls already wear. Under a .tablehead the column
   row is light (one dark band per section), so there the accent ring comes back. */
th.sortable:focus-visible{outline-color:#fff;box-shadow:0 0 0 4px rgba(255,255,255,.28)}
.tablehead + .table-wrap th.sortable:focus-visible{outline-color:var(--accent);box-shadow:var(--ring-focus)}
/* thin scrollbars on every inner scroller (Firefox; WebKit is styled globally in 02 · BASE) */
.table-wrap,.pop,.notif-pop-body,.act-pop-in,.dd-panel,.cmdk-list,.bp-body,.board-col-body,
.diallist,.exp-list,.flw-pick-list,.chip-list,.lead-notes-feed,.sig-edit,
.oq-body-rte,.dossier-body,.wiz-body,.pb-nav{scrollbar-width:thin}
/* and a slimmer WebKit rail inside popover/list bodies, where 12px reads chunky */
.notif-pop-body::-webkit-scrollbar,.act-pop-in::-webkit-scrollbar,.dd-panel::-webkit-scrollbar,
.cmdk-list::-webkit-scrollbar,.bp-body::-webkit-scrollbar,.board-col-body::-webkit-scrollbar,
.pop::-webkit-scrollbar,.table-wrap::-webkit-scrollbar,.diallist::-webkit-scrollbar,
.exp-list::-webkit-scrollbar,.flw-pick-list::-webkit-scrollbar{width:9px;height:9px}
/* the entry hall arrives, it doesn't teleport — same flwIn the wizard steps use */
.login-card{animation:flwIn var(--t-slow) var(--ease) both}

/* — Tasks page: aligned toolbar, chipped rows (owner, 2026-08-25) — */
/* inside a filterbar, a label sits BESIDE its control — the column layout left "Owner"/"Type"
   floating above their selects while the segmented control centred, and nothing lined up */
.filterbar label.inline{flex-direction:row;align-items:center;gap:7px}
.task-chips{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:3px}
.task-co{color:inherit;text-decoration:none}
a.task-co:hover b{color:var(--accent);text-decoration:underline}
.task-owner{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-xs)}
.task-owner .av-face{margin-left:0;width:18px;height:18px;font-size:8px}
.task-due{
  display:inline-flex;align-items:center;gap:5px;font-size:var(--fs-xs);font-weight:600;
  padding:2px 8px;border-radius:var(--r-full);background:var(--surface-2);color:var(--ink2);
  border:1px solid var(--line);
}
.task-due svg{width:12px;height:12px}
.task-due.late{background:var(--critical-soft);color:var(--critical);border-color:color-mix(in srgb,var(--critical) 30%,transparent)}
.task-due.soon{background:color-mix(in srgb,var(--warning) 14%,transparent);color:color-mix(in srgb,var(--warning) 70%,var(--ink));border-color:color-mix(in srgb,var(--warning) 32%,transparent)}
.task-note{
  margin-top:4px;font-size:var(--fs-xs);
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.task-shown{font-size:var(--fs-xs);white-space:nowrap}

/* Leads table pager. Sits under the table card, not inside it: the card is the data, this is the
   control that changes which slice of it you are looking at. Wraps to two lines on a narrow screen
   rather than scrolling sideways — the page body must never scroll horizontally. */
.lead-pager{display:flex;flex-direction:column;align-items:center;gap:6px;
  margin:var(--s3) 0 var(--s4);font-size:var(--fs-base)}
.lead-pager-range{font-size:12px}
.lead-pager-btns{display:flex;align-items:center;gap:4px;flex-wrap:wrap}
.lead-pager-btns button{min-width:34px}
.lead-pager-btns .lead-page-n[aria-current="page"]{font-weight:640}
.lead-pager-btns button[disabled]{opacity:.45;cursor:default}

/* Core-vs-personal, on the connector card. Sits between the description and the footer controls
   because it governs what those controls are allowed to do. */
.conn-scope{display:flex;align-items:center;gap:8px;margin:var(--s2) 0 0;font-size:12px}
.conn-scope .muted{line-height:1.3}

/* Duplicate-lead review. A list of choices, not a data table: each row is one radio the reader
   moves, so the hit area is the whole row and the columns stay readable when the email is long. */
.dup-banner{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;
  border-left:3px solid var(--critical)}
.dup-list{max-height:52vh;overflow:auto;margin-top:var(--s2)}
.dup-group{border:1px solid var(--line);border-radius:var(--r2,8px);padding:8px 10px;margin-bottom:10px}
.dup-why{font-size:12px;margin-bottom:6px}
.dup-row{display:flex;align-items:baseline;gap:8px;padding:5px 4px;border-radius:6px;cursor:pointer;flex-wrap:wrap}
.dup-row:hover{background:var(--surface)}
.dup-row .dup-main{flex:1 1 220px;min-width:0;overflow-wrap:anywhere}
.dup-row .dup-main .muted{margin-left:6px}
.dup-row .dup-meta{font-size:12px;flex:0 1 auto}

/* The CSV import report's two row lists. Folded by default because the numbers above them are the
   answer and the rows are the evidence — and because a 40,000-row import must not paint 100 rows
   nobody opened. <details>, so the fold costs no script. */
.imp-fold{margin-top:var(--s2);border-top:1px solid var(--line);padding-top:var(--s2)}
.imp-fold>summary{
  list-style:none;cursor:pointer;font-size:var(--fs-sm,13px);font-weight:620;color:var(--ink2);
  display:inline-flex;align-items:center;gap:6px;padding:2px 4px;user-select:none;
}
.imp-fold>summary::-webkit-details-marker{display:none}
.imp-fold>summary::before{content:"\25B8";transition:transform var(--t) var(--ease)}
.imp-fold[open]>summary::before{transform:rotate(90deg)}
.imp-fold>summary:hover{color:var(--ink)}
.imp-fold .dup-list{max-height:34vh}

/* "All 50 on this page are selected — select all 1,240 matching" — the honest version of a header
   checkbox that can only ever tick the rows the DOM is holding. */
.lead-selall{display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap;
  padding:8px 12px;border-bottom:1px solid var(--line);font-size:13px;background:var(--surface)}

/* "Choose an account" on a connector card: accounts are connected, none is picked. Neither the green
   of connected nor the grey of not-set-up — the state needs an action, so it reads as attention. */
.badge.warn{background:var(--warning-soft);color:var(--warning);
  border-color:color-mix(in srgb,var(--warning) 30%,transparent)}

/* Admin "what the team is doing" panel. Tiles, sparklines and a per-person table — its own block
   under the dashboard rather than more compartments inside tiles every rep also sees. */
.tk-bar{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;margin-bottom:var(--s2)}
.tk-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:var(--s2);margin-bottom:var(--s3)}
.tk-tile{border:1px solid var(--line);border-radius:var(--r2,8px);padding:10px 12px;background:var(--surface)}
.tk-n{font-size:1.4rem;font-weight:640;line-height:1.1}
.tk-l{font-size:12px;margin-top:2px}
.tk-s{font-size:11px;margin-top:2px}
.tk-sparks{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--s3);margin-bottom:var(--s3)}
.tk-spark{font-size:12px}
.tk-bars{display:flex;align-items:flex-end;gap:2px;height:38px;margin-top:4px}
.tk-bars i{flex:1 1 0;min-width:2px;background:var(--accent,#2a4756);border-radius:2px 2px 0 0;opacity:.75}
.tk-bars i:hover{opacity:1}

/* Determinate progress: a ring that fills to a real percentage, with the measured time remaining
   beside it. Shown INSTEAD of the sliding .pp-bar, never as well — two indicators for one operation
   is two things to read. Work that cannot count itself keeps the bar and shows no number, because a
   ring that crawls to 90% and stops teaches people the number means nothing. */
.pp-ring{position:relative;flex:0 0 22px;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center}
/* An explicit display beats the [hidden] attribute, so both need saying outright — without this the
   ring and the bar are both on screen for every operation. */
.pp-ring[hidden],.pp-bar[hidden]{display:none}
.pp-ring svg{width:22px;height:22px;transform:rotate(-90deg)}
.pp-ring circle{fill:none;stroke-width:3}
.pp-ring-bg{stroke:color-mix(in srgb,var(--accent) 18%,transparent)}
.pp-ring-fg{stroke:var(--accent);stroke-linecap:round;transition:stroke-dashoffset var(--t,.2s) linear}
.pp-pct{position:absolute;font-size:8px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--ink2);line-height:1}
.pp-eta{flex:0 0 auto;white-space:nowrap;font-variant-numeric:tabular-nums}
@media (max-width:640px){ .pp-eta{display:none} }
@media (prefers-reduced-motion:reduce){ .pp-ring-fg{transition:none} }

/* Outreach progress on a collection card. One stacked track, not three more meters: these are slices
   of one hundred percent, and the untouched remainder is the part worth seeing at a glance. Replied
   is drawn inside sent because a reply implies a send. */
.coll-track{margin:var(--s2) 0 var(--s1)}
.ct-head{display:flex;align-items:baseline;justify-content:space-between;gap:6px;font-size:var(--fs-2xs);
  letter-spacing:.04em;text-transform:uppercase;color:var(--muted);font-weight:640}
.ct-pct{font-variant-numeric:tabular-nums;color:var(--ink2);font-size:var(--fs-xs);letter-spacing:0}
.ct-bar{display:flex;height:6px;border-radius:99px;overflow:hidden;margin-top:3px;
  background:color-mix(in srgb,var(--ink) 8%,transparent)}
.ct-bar i{display:block;height:100%;position:relative;transition:width var(--t,.2s) var(--ease,ease)}
.ct-sent{background:var(--good,#2e9e5b)}
.ct-queued{background:color-mix(in srgb,var(--accent) 45%,transparent)}
/* A lighter band riding on the sent segment — a reply is the best outcome on this bar, so it reads
   as a highlight within what was sent rather than a competing colour beside it. */
.ct-replied{position:absolute;inset:0 auto 0 0;height:100%;display:block;
  background:color-mix(in srgb,#fff 55%,var(--good,#2e9e5b));border-radius:99px}
.ct-legend{font-size:var(--fs-2xs);margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ct-rep-k{font-weight:660;color:var(--good,#2e9e5b)}

/* Status-column chips are doors (owner, 2026-08-25): the empty and done states became buttons —
   same 22px box, quiet skin, plus a pointer and a hover lift so "clickable" is discoverable. */
button.act-chip{min-height:0;min-width:0;box-shadow:none;transform:none;padding:0;margin:0;cursor:pointer;font:inherit;line-height:1;appearance:none}
.act-chip.act-door:hover{border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 14%,transparent)}
.act-chip.off.act-door:hover{color:var(--accent);background:var(--accent-soft)}

/* ── LinkedIn outreach: the conversation it will become ──────────────────────
   The sibling of .wa-chat, and deliberately NOT the same shape in different colours.
   LinkedIn's own surface is white panels on a light grey ground, the headline under the name, and
   no coloured bubbles at all — its outgoing message is a plain card with a hairline rule. A green
   bubble painted blue would read as WhatsApp in a costume. */
.li-chat{
  --li-blue:#0A66C2;
  --li-paper:color-mix(in srgb,var(--li-blue) 4%,var(--surface-2));
  border:1px solid color-mix(in srgb,var(--li-blue) 22%,var(--line));
  border-radius:var(--r-md);overflow:hidden;background:var(--surface);
}
.li-chat .li-head{
  display:flex;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3);
  background:var(--surface);border:0;border-bottom:1px solid var(--line);border-radius:0;margin:0;
}
.li-chat .li-head .lead-avatar-ring{width:40px;height:40px;min-width:40px}
.li-chat .li-name{font-size:var(--fs-md)}
.li-from-chip{font-size:var(--fs-2xs);color:var(--muted);white-space:nowrap;text-transform:uppercase;
  letter-spacing:.04em;flex-shrink:0}
.li-thread{padding:var(--s4) var(--s3);background:var(--li-paper);display:flex;flex-direction:column;
  align-items:flex-end;gap:var(--s2);min-height:96px}
.li-day{align-self:center;font-size:var(--fs-2xs);color:var(--muted);background:var(--surface);
  border:1px solid var(--line);border-radius:99px;padding:2px 10px}
/* A panel, not a bubble — this is what LinkedIn actually draws. */
.li-msg{max-width:min(560px,92%);background:var(--surface);border:1px solid var(--line);
  border-left:3px solid var(--li-blue);border-radius:var(--r2,8px);padding:8px 12px;
  box-shadow:0 1px 2px rgba(16,24,40,.04)}
.li-msg-who{display:flex;align-items:center;gap:6px;font-size:var(--fs-2xs);font-weight:660;
  color:var(--ink2);margin-bottom:3px}
.li-msg-badge{font-weight:600;text-transform:uppercase;letter-spacing:.04em;font-size:9px;
  color:var(--li-blue);background:color-mix(in srgb,var(--li-blue) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--li-blue) 26%,transparent);border-radius:99px;padding:1px 6px}
.li-text{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--fs-sm);color:var(--ink)}
.li-text:empty::before{content:"No note yet";opacity:.55;font-style:italic}
.li-stamp{display:flex;align-items:center;justify-content:flex-end;gap:4px;margin-top:4px;
  font-size:var(--fs-2xs);color:var(--muted);font-variant-numeric:tabular-nums}
.li-ticks svg{display:block;width:15px;height:15px}
.li-ticks.li-read{color:var(--li-blue)}
.li-ticks.li-fail{color:var(--critical)}
.li-ticks.li-wait{opacity:.55}
.li-composer{display:flex;align-items:flex-end;gap:var(--s2);padding:var(--s2) var(--s3);
  background:var(--surface);border-top:1px solid var(--line)}
.li-input{flex:1;min-width:0;resize:none;border:1px solid var(--line);border-radius:18px;
  padding:8px 12px;font:inherit;font-size:var(--fs-sm);background:var(--surface-2);color:var(--ink);
  max-height:160px;overflow-y:auto}
.li-input:focus{outline:none;border-color:var(--li-blue);background:var(--surface);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--li-blue) 18%,transparent)}
.li-send{flex:0 0 auto;width:34px;height:34px;border-radius:50%;border:0;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;background:var(--li-blue);color:#fff}
.li-send:hover:not(:disabled){filter:brightness(1.08)}
.li-send:disabled{opacity:.45;cursor:not-allowed}
.li-send svg{width:16px;height:16px}
/* The counter rides in the composer, where it is read while typing rather than after. */
.li-composer .li-count{flex:0 0 auto;align-self:center;font-size:var(--fs-2xs);
  font-variant-numeric:tabular-nums;color:var(--muted);white-space:nowrap}
.li-chat.over .li-count{color:var(--critical);font-weight:660}
.li-chat.over .li-input{border-color:var(--critical)}
.li-chat .li-note-hint{margin:0;padding:0 var(--s3) var(--s2);font-size:var(--fs-2xs);color:var(--muted)}
.li-chat .li-over{margin:0;padding:0 var(--s3) var(--s2)}
@media (max-width:640px){ .li-from-chip{display:none} }


/* — the in-flight queue under the progress strip (owner, 2026-08-26) — */
.pp-chev{min-height:0;width:24px;height:24px;padding:0;margin-left:4px;border:0;background:transparent;color:var(--muted);cursor:pointer;border-radius:var(--r-sm);font-size:11px}
.pp-chev:hover{background:var(--surface-3);color:var(--ink)}
.pp-chev[hidden]{display:none}
.pp-queue{
  position:fixed;top:calc(var(--topbar-h) + 34px);right:14px;z-index:var(--z-pop);
  width:min(380px,calc(100vw - 28px));padding:10px 12px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--shadow-3);
  display:flex;flex-direction:column;gap:8px;
  transform-origin:top right;animation:notifPopIn var(--t-slow) var(--ease-spring) both;
}
.pp-queue[hidden]{display:none}
.ppq-row{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(70px,110px) auto;gap:8px;align-items:center;font-size:var(--fs-xs)}
.ppq-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink2)}
.ppq-eta{color:var(--muted);font-variant-numeric:tabular-nums}
.ppq-pct{font-weight:650;font-variant-numeric:tabular-nums}
.ppq-bar{height:5px;border-radius:3px;background:color-mix(in srgb,var(--accent) 14%,transparent);overflow:hidden}
.ppq-bar i{display:block;height:100%;background:var(--accent);border-radius:3px;transition:width var(--t-fast)}
.ppq-bar.ind{grid-column:2 / -1}
.ppq-bar.ind i{width:34%;animation:ppqSlide 1.2s ease-in-out infinite alternate}
@keyframes ppqSlide{from{transform:translateX(-30%)}to{transform:translateX(220%)}}

/* The call-to-action list on a strategy card. Each row is a tick plus the exact instruction the AI
   is given — shown in full rather than behind a toggle, because the instruction is the thing that
   actually changes the draft and every default one carries an anti-hallucination clause worth
   reading before it is rewritten. */
.cta-head{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.04em;font-weight:640;
  margin:var(--s3) 0 var(--s2)}
.cta-list{display:flex;flex-direction:column;gap:6px}
.cta-row{display:flex;gap:9px;align-items:flex-start;padding:8px 10px;border:1px solid var(--line);
  border-radius:var(--r2,8px);background:var(--surface-2);cursor:pointer}
.cta-row.on{background:var(--surface);border-color:color-mix(in srgb,var(--accent) 34%,var(--line))}
.cta-row>input[type=checkbox]{margin-top:5px;flex:0 0 auto}
.cta-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}
.cta-label{font-weight:620;border:0;background:transparent;padding:2px 0;font-size:var(--fs-sm);
  color:var(--ink);width:100%}
.cta-label:focus{outline:none;border-bottom:1px solid var(--accent)}
/* Dimmed until ticked: an instruction for a CTA that will never fire is noise, but it must stay
   readable so somebody can see what they would be turning on. */
.cta-row:not(.on) .cta-inst{opacity:.55}
.cta-inst{width:100%;font-size:12px;resize:vertical;border:1px solid var(--line);border-radius:6px;
  padding:5px 7px;background:var(--surface);color:var(--ink2);font-family:inherit}

/* ---------- AI consumption chart ----------
   Stacked columns in flexbox rather than SVG or a charting library: the shapes are rectangles and
   the CSP forbids a CDN, so a dependency here would buy nothing but a <script> tag. Heights are
   percentages of the tallest column, which is also what makes it responsive with no resize handler.
   Every colour comes from a token or from avHue (the roster hue a person already has app-wide), so
   both themes are covered without a second palette. */
.ai-chart{margin-top:var(--s3)}
.ai-chart-h{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s2);
  font-size:var(--fs-xs);color:var(--ink2);margin-bottom:var(--s1)}
.ai-chart-empty{padding:var(--s5) var(--s3);text-align:center;border:1px dashed var(--line);
  border-radius:var(--r-sm);font-size:var(--fs-sm)}
.ai-cols{display:flex;align-items:flex-end;gap:2px;height:150px;
  padding:var(--s1) 0;border-bottom:1px solid var(--line)}
/* Each column is a full-height button so the hover target is the whole strip, not just the bar —
   a two-pixel bar on a quiet day is unhittable otherwise, and a quiet day is the interesting one. */
.ai-col{flex:1 1 0;min-width:3px;height:100%;display:flex;align-items:flex-end;
  background:transparent;border:0;padding:0;cursor:pointer;border-radius:var(--r-xs) var(--r-xs) 0 0}
.ai-col:hover,.ai-col:focus-visible{background:var(--surface-3);outline:none}
.ai-col:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}
.ai-col-bar{display:flex;flex-direction:column-reverse;width:100%;min-height:1px;
  border-radius:var(--r-xs) var(--r-xs) 0 0;overflow:hidden}
.ai-col-bar i{display:block;width:100%;min-height:1px}
.ai-axis{display:flex;gap:2px;margin-top:var(--s1)}
/* Labels are wider than the columns they sit under, so they SPILL rather than clip — a truncated
   "Aug 1" that is really Aug 13 is worse than no label. aiChartHtml thins them to about seven
   across the axis, which is what keeps the spill from reaching the next one. */
.ai-axis span{flex:1 1 0;min-width:0;font-size:var(--fs-2xs);color:var(--muted);
  white-space:nowrap;overflow:visible}
.ai-legend{display:flex;flex-wrap:wrap;gap:var(--s1) var(--s3);margin-top:var(--s2)}
.ai-key{display:inline-flex;align-items:center;gap:5px;font-size:var(--fs-xs);color:var(--ink2)}
.ai-key i{width:9px;height:9px;border-radius:2px;flex:0 0 auto}
.ai-filters{align-items:end;gap:var(--s2)}
/* Free vs paid, stated as a band above the chart rather than only as a grouping option: it is the
   question that gets asked without anyone changing a select, and on free models every $ is 0 so a
   chart alone would draw the answer as an empty column. */
.ai-tier-split{display:flex;flex-wrap:wrap;gap:var(--s1) var(--s4);margin:var(--s2) 0;
  padding:var(--s2) var(--s3);border:1px solid var(--line);border-radius:var(--r2);background:var(--surface-2,transparent);
  font-size:var(--fs-xs);color:var(--ink2)}
.ai-tier-split>p{flex:1 0 100%}
/* Fixed, not absolute: the card scrolls and the panel must not scroll with it or clip inside it. */
/* Sized by its content up to a cap, and clipped AT the cap: the cells are nowrap, so a table told
   to fill 100% of a narrower box simply overflowed it and the Calls column rendered outside. */
.ai-tip{position:fixed;z-index:80;pointer-events:none;max-width:min(440px,92vw);overflow:hidden;
  background:var(--surface);color:var(--ink);border:1px solid var(--line-2);
  border-radius:var(--r-sm);box-shadow:var(--shadow-2);padding:var(--s2) var(--s3);font-size:var(--fs-xs)}
.ai-tip[hidden]{display:none}
.ai-tip-h{font-weight:640;margin-bottom:var(--s1)}
.ai-tip-t{border-collapse:collapse;font-variant-numeric:tabular-nums}
/* The app's table styles paint a dark header band. Inside a small popover that reads as a selected
   row rather than a header, so this one opts out explicitly. */
.ai-tip-t thead tr,.ai-tip-t tfoot tr{background:transparent}
.ai-tip-t th{background:transparent;text-transform:none;letter-spacing:0;
  font-weight:600;color:var(--muted);text-align:right;padding:1px 0 3px 10px;font-size:var(--fs-2xs)}
.ai-tip-t th:first-child{text-align:left;padding-left:0}
.ai-tip-t td{padding:2px 0 2px 10px;text-align:right;white-space:nowrap}
.ai-tip-t td:first-child{text-align:left;padding-left:0;color:var(--ink2)}
.ai-tip-t td i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:5px}
.ai-tip-t tfoot td{border-top:1px solid var(--line);font-weight:640;padding-top:4px}
/* Meeting-prep sheet: five short sections a rep reads once before dialling. */
.mp h4{margin:12px 0 4px;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;color:var(--muted)}
.mp h4:first-child{margin-top:0}
.mp h4.mp-sub{margin-top:6px;color:var(--ink2);letter-spacing:.04em;font-size:var(--fs-xs);font-weight:600}
.mp ul{margin:0;padding-left:18px}
.mp li{margin:3px 0;font-size:var(--fs-sm);line-height:var(--lh);color:var(--ink)}
.mp li.mp-qa{list-style:none;margin-left:-18px;padding:6px 10px;border-left:2px solid var(--line);margin-bottom:6px}
.mp .mp-q{display:block;font-weight:600}
.mp .mp-a{display:block;color:var(--ink2)}
.mp .mp-note{font-size:var(--fs-xs);margin:10px 0 0}
