:root{
    --ground:#F2F4F8; --panel:#FFFFFF; --panel-2:#F7F9FC;
    --ink:#161A21; --muted:#586170; --faint:#8791A0; --line:#E5E9F0; --line-2:#EEF1F6;
    --accent:#5A57D8; --accent-soft:#ECECFB; --accent-ink:#4744C6;
    --warm:#C2761F; --warm-soft:#FAEFDD; --good:#1E9B57; --good-soft:#E4F4EB;
    --info:#3A78D0; --info-soft:#E5EFFB; --warn:#BC760D; --warn-soft:#F9EFDB;
    --crit:#D44343; --crit-soft:#FAE7E7; --bb:#7A8593; --bb-soft:#EDF0F4;
    --staff:#9A6232;   /* a colleague's name — the brown underline (.mail-staff) */
    --shadow:0 1px 2px rgba(16,22,40,.04),0 3px 14px rgba(16,22,40,.05);
    --shadow-lg:0 16px 50px rgba(16,22,40,.16); --r:13px; --r-sm:9px;
    --font-ui:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    --font-num:ui-monospace,"SF Mono","Cascadia Mono","Roboto Mono",Menlo,monospace;
    /* Handwriting faces. BOTH stay loaded (index.html) even though only one is in use: Birthstone is
       kept deliberately, for other surfaces later, and so that swapping the signature back to it is
       two lines here rather than a round trip through the font request.

       Every fallback is a real cursive face shipping with Windows or macOS, so the effect survives
       the web font not arriving — a signature that fails to a sans-serif stops reading as a
       signature at all. */
    --font-sig-fallback:"Segoe Script","Brush Script MT","Snell Roundhand",cursive;
    --font-sig-alexbrush:"Alex Brush",var(--font-sig-fallback);
    --font-sig-birthstone:"Birthstone",var(--font-sig-fallback);
    /* Size travels WITH the face, because the two are not interchangeable at one value: Alex Brush
       is heavier and slanted and settles at 18px, Birthstone is lighter and more upright and needs
       22px to carry the same weight beside the pill. Switching faces means switching both lines. */
    --font-sig:var(--font-sig-alexbrush);
    --font-sig-size:18px;              /* 22px if switching to --font-sig-birthstone */
  }
  @media (prefers-color-scheme:dark){:root{
    --ground:#0D1016; --panel:#151922; --panel-2:#1A1F29; --ink:#E6EAF2; --muted:#96A0AF; --faint:#68717F;
    --line:#242A36; --line-2:#1E232E; --accent:#8A8BF2; --accent-soft:#1D2038; --accent-ink:#B3B3F8;
    --warm:#DA9A4B; --warm-soft:#2A2012; --good:#3EC079; --good-soft:#11271A; --info:#5D9AE7; --info-soft:#131E2D;
    --warn:#D89A3B; --warn-soft:#2A2012; --crit:#E96D6D; --crit-soft:#2C1717; --bb:#808A98; --bb-soft:#1C212B; --staff:#C8925F;
    --shadow:0 1px 2px rgba(0,0,0,.3),0 6px 20px rgba(0,0,0,.3); --shadow-lg:0 20px 60px rgba(0,0,0,.55);
  }}
  :root[data-theme="light"]{--ground:#F2F4F8;--panel:#FFFFFF;--panel-2:#F7F9FC;--ink:#161A21;--muted:#586170;--faint:#8791A0;--line:#E5E9F0;--line-2:#EEF1F6;--accent:#5A57D8;--accent-soft:#ECECFB;--accent-ink:#4744C6;--warm:#C2761F;--warm-soft:#FAEFDD;--good:#1E9B57;--good-soft:#E4F4EB;--info:#3A78D0;--info-soft:#E5EFFB;--warn:#BC760D;--warn-soft:#F9EFDB;--crit:#D44343;--crit-soft:#FAE7E7;--bb:#7A8593;--bb-soft:#EDF0F4;--staff:#9A6232;--shadow:0 1px 2px rgba(16,22,40,.04),0 3px 14px rgba(16,22,40,.05);--shadow-lg:0 16px 50px rgba(16,22,40,.16);}
  :root[data-theme="dark"]{--ground:#0D1016;--panel:#151922;--panel-2:#1A1F29;--ink:#E6EAF2;--muted:#96A0AF;--faint:#68717F;--line:#242A36;--line-2:#1E232E;--accent:#8A8BF2;--accent-soft:#1D2038;--accent-ink:#B3B3F8;--warm:#DA9A4B;--warm-soft:#2A2012;--good:#3EC079;--good-soft:#11271A;--info:#5D9AE7;--info-soft:#131E2D;--warn:#D89A3B;--warn-soft:#2A2012;--crit:#E96D6D;--crit-soft:#2C1717;--bb:#808A98;--bb-soft:#1C212B;--staff:#C8925F;--shadow:0 1px 2px rgba(0,0,0,.3),0 6px 20px rgba(0,0,0,.3);--shadow-lg:0 20px 60px rgba(0,0,0,.55);}

  *{box-sizing:border-box}
  /* ---- Count badges (.badge) — one primitive, see DESIGN-SYSTEM.md §Count badges ----
     Two rules, both learned the hard way:
       1. A badge NEVER relies on space outside its host box. Every rail and bar in this app is a
          scroll container (#nav and #main are overflow:auto), so anything hanging outside a button
          or a nav item gets sliced by the nearest one. Corner badges are pinned inside the host.
       2. The ring that separates a badge from the icon beneath it is a box-shadow, NOT a border.
          `*{box-sizing:border-box}` means a 2px border eats 4px of a 16px pill, leaving 12px of
          interior for a taller line box — the digits then paint onto the ring and read as cut off.
          Glyphs are centred by grid + line-height:1 so the text box can never exceed the pill. */
  .badge{--badge-h:16px;display:inline-grid;place-items:center;min-width:var(--badge-h);height:var(--badge-h);padding:0 5px;
    background:var(--crit);color:#fff;border-radius:999px;font-size:10.5px;font-weight:700;line-height:1;
    font-variant-numeric:tabular-nums;white-space:nowrap}
  body{margin:0;background:var(--ground);color:var(--ink);font-family:var(--font-ui);font-size:14px;line-height:1.45;-webkit-font-smoothing:antialiased}
  .num{font-family:var(--font-num);font-variant-numeric:tabular-nums}
  .eyebrow{font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
  h1,h2,h3{margin:0;text-wrap:balance}
  a{color:inherit;text-decoration:none} button{font-family:inherit;cursor:pointer}
  :focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}

  .app{display:grid;grid-template-columns:228px 1fr;min-height:100vh}
  .app.collapsed{grid-template-columns:62px 1fr}
  .sidebar{background:var(--panel);border-right:1px solid var(--line);display:flex;flex-direction:column;position:sticky;top:0;height:100vh;padding:14px 12px}
  .brand{display:flex;align-items:center;gap:9px;padding:6px 8px 12px;font-weight:700;font-size:18px}
  .brand .mark{width:26px;height:26px;border-radius:8px;flex:none;display:grid;place-items:center;background:linear-gradient(135deg,var(--accent),#847fee);color:#fff;font-weight:800}
  .brand .word{font-style:italic}
  .app.collapsed .brand .word,.app.collapsed .nav-label,.app.collapsed .nav-group-label,.app.collapsed .u-meta{display:none}
  .nav{display:flex;flex-direction:column;gap:1px;margin-top:2px;overflow-y:auto}
  .nav-group-label{font-size:10px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;color:var(--faint);padding:12px 10px 5px}
  .nav a{display:flex;align-items:center;gap:11px;padding:8px 10px;border-radius:var(--r-sm);color:var(--muted);font-weight:500;position:relative}
  .nav a .ic{width:18px;height:18px;flex:none;stroke:currentColor;stroke-width:1.8;fill:none;opacity:.9}
  .nav a:hover{background:var(--line-2);color:var(--ink)}
  .nav a.active{background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
  .nav a .badge{margin-left:auto;flex:none}
  /* The label yields before the badge does — a long nav label must ellipsis, never push the count
     out past the rail's edge (.nav is a scroll container, so anything pushed out is sliced off). */
  .nav a .nav-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .app.collapsed .nav a{justify-content:center;padding:9px}
  /* Collapsed rail: there is no room beside the icon (37px item), so the count stops being a
     flow item and becomes a corner marker pinned INSIDE the item — see §Count badges. */
  .app.collapsed .nav a .badge{position:absolute;top:1px;right:1px;margin:0;--badge-h:15px;font-size:9px;padding:0 4px;box-shadow:0 0 0 2px var(--panel)}
  .app.collapsed .nav a.active .badge{box-shadow:0 0 0 2px var(--accent-soft)}
  .side-foot{margin-top:auto;border-top:1px solid var(--line);padding-top:10px;display:flex;align-items:center;gap:9px;padding-left:6px}
  /* position/overflow so a borrowed headshot can sit OVER the initials (js/engine.js uAv, phase 3):
     the letters show while the image loads, and stay if it fails — onerror removes the img, so a
     bridge outage degrades to the plain coloured circle rather than an empty disc. */
  .avatar{width:28px;height:28px;border-radius:50%;flex:none;display:grid;place-items:center;font-size:11px;font-weight:700;color:#fff;position:relative;overflow:hidden}
  .avatar img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block}
  .u-meta{min-width:0;line-height:1.25}.u-meta .n{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.u-meta .r{font-size:11px;color:var(--faint)}
  .um-item{padding:8px 10px;border-radius:7px;font-size:13px;color:var(--ink)}.um-item:hover{background:var(--panel-2)}

  .main{min-width:0;display:flex;flex-direction:column;max-height:100vh;overflow-y:auto}
  .topbar{position:sticky;top:var(--banner-h,0px);z-index:20;background:color-mix(in srgb,var(--ground) 84%,transparent);backdrop-filter:blur(10px);border-bottom:1px solid var(--line);display:flex;align-items:center;gap:10px;padding:10px 22px}
  .icon-btn{width:34px;height:34px;border-radius:9px;border:1px solid var(--line);background:var(--panel);display:grid;place-items:center;color:var(--muted)}
  .icon-btn:hover{color:var(--ink);border-color:var(--faint)}.icon-btn .ic{width:17px;height:17px;stroke:currentColor;stroke-width:1.8;fill:none}
  /* Compact variant for repeated per-row actions in table cells (see DESIGN-SYSTEM.md §Icon actions) —
     a common, self-explanatory action (refresh, cancel, copy…) repeated down a column should be a
     small icon + tooltip, not a text button re-reading the same word N times down the page. */
  .icon-btn.sm{width:26px;height:26px;border-radius:7px;display:inline-grid}
  .icon-btn.sm .ic{width:14px;height:14px}
  .icon-btn.crit{color:var(--crit)}.icon-btn.crit:hover{border-color:var(--crit)}
  .search{flex:1;max-width:380px;display:flex;align-items:center;gap:8px;background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:7px 12px;color:var(--faint)}
  .search .ic{width:16px;height:16px;stroke:currentColor;stroke-width:1.9;fill:none}
  .search input{border:0;background:transparent;color:var(--ink);flex:1;outline:none;font-size:13px}
  .spacer{flex:1}
  .scope{display:inline-flex;background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:3px}
  .scope button{border:0;background:transparent;color:var(--muted);font-weight:600;font-size:12.5px;padding:5px 13px;border-radius:7px;display:flex;align-items:center;gap:6px}
  .scope button.on{background:var(--accent);color:#fff}
  .scope .dot{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.75}
  /* ── Nav scope toggle (sidebar) ── */
  .nav-scope{padding:2px 2px 6px}
  /* inline-flex keeps the pill at natural width — not stretched to the full sidebar */
  .nav-scope .scope{display:inline-flex}
  .nav-scope .scope button{flex:1;justify-content:center;padding:5px 10px;font-size:12px}
  /* single-letter abbreviations (M/F) only shown in the collapsed rail */
  .scope-short{display:none}
  /* Section separators — between Practice/Personal and Personal/Admin */
  .nav-sep{height:1px;background:var(--line);margin:8px 4px 4px}
  /* Collapsed rail: scope toggle hidden — it's a setting, change it when expanded */
  .app.collapsed .nav-scope{display:none}
  /* Separators visible in collapsed rail with slightly tighter horizontal margin */
  .app.collapsed .nav-sep{margin:6px 8px 4px}
  select.viewas{border:1px solid var(--line);background:var(--panel);color:var(--ink);border-radius:9px;padding:7px 9px;font-size:12.5px;font-weight:600;max-width:150px}
  .sync{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--muted);padding:6px 10px;border-radius:9px;border:1px solid var(--line);background:var(--panel);white-space:nowrap}
  .sync .live{width:7px;height:7px;border-radius:50%;background:var(--good)}
  .btn{background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:9px;padding:7px 12px;font-weight:600;font-size:12.5px;display:inline-flex;align-items:center;gap:7px}
  .btn:hover{border-color:var(--faint)}.btn .ic{width:15px;height:15px;stroke:currentColor;stroke-width:1.9;fill:none}
  .btn.accent{background:var(--accent);color:#fff;border-color:transparent}

  /* Full available width — the surface fills the viewport beside the sidebar (see DESIGN-SYSTEM.md
     §Surface panes). Wide tables get the room; no artificial column of dead space on the right. */
  .page{padding:20px 22px 70px;width:100%}
  .page-head{display:flex;align-items:flex-end;gap:14px;margin-bottom:18px;flex-wrap:wrap}
  .page-head h1{font-size:24px;letter-spacing:-.02em}.page-head .sub{color:var(--muted);font-size:13.5px;margin-top:3px}
  .surface{display:none}.surface.active{display:block;animation:fade .22s ease}
  @keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
  @media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

  /* ---- HOME's four bands -------------------------------------------------------------------
     The page is a stack of labelled sections rather than a grid of cards, because the labels are
     what stop it reading as a control panel: each band says what it is and who it is for before it
     shows a single number. Generous vertical rhythm for the same reason — the old page's problem
     was density, not content. */
  .hband{margin:0 0 26px}
  .hband-h{display:flex;align-items:baseline;gap:10px;margin:0 0 11px}
  .hband-h h3{font-size:13px;font-weight:700;letter-spacing:.01em;color:var(--ink)}
  .hband-s{font-size:11.5px;color:var(--faint);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .hband-h .link{margin-left:auto;font-size:12px;color:var(--accent-ink);font-weight:600;flex:none}
  .hband .tiles{margin-bottom:0}

  /* "On you" — bigger type than the KPI tiles below it, because this band is the answer to the
     question the reader actually arrived with, and it should win the first glance. */
  .hb-figs{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:13px}
  .hb-fig{display:flex;flex-direction:column;align-items:flex-start;gap:3px;text-align:left;
    background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:15px 16px;
    box-shadow:var(--shadow);cursor:pointer;color:var(--ink);font-family:var(--font-ui)}
  .hb-fig:hover{border-color:var(--accent)}
  .hb-fig .n{font-family:var(--font-num);font-variant-numeric:tabular-nums;font-size:30px;font-weight:700;line-height:1;letter-spacing:-.02em}
  .hb-fig .l{font-size:12.5px;font-weight:600;margin-top:5px}
  .hb-fig .h{font-size:11px;color:var(--faint)}
  /* A zero is still shown — "nothing is on you" is worth knowing — but it stops inviting a click. */
  .hb-fig.zero{cursor:default;box-shadow:none;background:var(--panel-2)}
  .hb-fig.zero:hover{border-color:var(--line)}
  .hb-fig.zero .n{color:var(--faint)}
  .hb-warn{margin-left:auto;flex:none;font-size:11.5px;font-weight:700;color:var(--crit);
    background:var(--crit-soft);border:1px solid color-mix(in srgb,var(--crit) 25%,transparent);
    border-radius:999px;padding:4px 11px;cursor:pointer}
  .hb-warn:hover{border-color:var(--crit)}

  /* The practice calendar and the book, side by side. The calendar is the wider of the two: it is a
     bucket rail plus rows, where the book is four compact cards. */
  .hsplit{display:grid;grid-template-columns:1.25fr 1fr;gap:16px;align-items:start}
  .hsplit .dlc{margin-bottom:0}
  /* Pulse's charts, at half the page width — a 2x2 rather than the full-width row they had. */
  .hbook{display:grid;grid-template-columns:repeat(2,1fr);gap:13px}
  .hbook .card{margin:0}
  @media (max-width:1080px){.hsplit{grid-template-columns:1fr}.hbook{grid-template-columns:repeat(2,1fr)}}
  @media (max-width:700px){.hbook{grid-template-columns:1fr}}

  .tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:13px;margin-bottom:18px}
  .tile{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:14px 15px;box-shadow:var(--shadow)}
  .tile .lab{font-size:12px;color:var(--muted);font-weight:500}
  .tile .big{font-size:28px;font-weight:700;letter-spacing:-.02em;margin-top:7px;line-height:1}
  .tile .foot{font-size:11.5px;color:var(--faint);margin-top:6px}
  .tile.crit .big{color:var(--crit)}.tile.warm .big{color:var(--warm)}.tile.good .big{color:var(--good)}

  .split{display:grid;grid-template-columns:1fr 330px;gap:16px;align-items:start}
  .card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);overflow:hidden}
  /* Me-scope indicator: subtle dashed border on cards/tiles in Me-scope-aware surfaces (Practice section only) */
  #app.scope-is-me [data-me-scope] .card,
  #app.scope-is-me [data-me-scope] .tile{border:1px dashed color-mix(in srgb,var(--accent) 40%,var(--line))}
  .void-pending-row:hover td{background:var(--panel-2)}
  /* Warming-up state (a surface withholding figures until its data has landed). Reuses the rbspin
     keyframes the refresh buttons already use — one spin animation for the whole app. */
  .warm-spin{width:26px;height:26px;border:2.5px solid var(--line-2);border-top-color:var(--accent);border-radius:50%;display:inline-block;animation:rbspin .85s linear infinite}
  .warm-pulse{animation:dtPulse 1.6s ease-in-out infinite}
  .card-h{display:flex;align-items:center;gap:10px;padding:13px 15px 10px}
  .card-h h3{font-size:14px}.card-h .count{font-family:var(--font-num);font-size:12px;color:var(--faint);background:var(--line-2);border-radius:20px;padding:1px 8px}
  .card-h .link{margin-left:auto;font-size:12.5px;color:var(--accent-ink);font-weight:600}

  .grp-lab{display:flex;align-items:center;gap:8px;padding:11px 15px 5px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
  .grp-lab.over{color:var(--crit)}.grp-lab .n{font-family:var(--font-num);color:var(--faint)}
  /* A truncated work group says how much it is hiding and opens the rest (openHomeDrill) — the
     cockpit lists a readable head of each bucket, it never silently swallows the tail. */
  .grp-lab .link{margin-left:auto;text-transform:none;letter-spacing:0;font-size:11.5px;color:var(--accent-ink);cursor:pointer}
  .grp-more{border-top:1px solid var(--line-2);padding:9px 15px;font-size:12px;color:var(--accent-ink);font-weight:600;cursor:pointer}
  .grp-more:hover{background:var(--panel-2)}
  .wrow{display:grid;grid-template-columns:4px 1fr auto;align-items:stretch;border-top:1px solid var(--line-2)}
  .wrow:hover{background:var(--panel-2);cursor:pointer}
  .wrow .stripe{border-radius:2px;margin:11px 0}
  .wrow .body{padding:10px 8px 10px 13px;min-width:0}
  /* No fixed character cap — the title uses the whole 1fr column and ellipsises only when it
     genuinely runs out of room (.wrow .body is min-width:0, so the grid track does the clamping). */
  .wrow .title{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .wrow .metaline{display:flex;align-items:center;gap:7px;margin-top:4px;flex-wrap:wrap}
  /* Inside a client's own drawer the title WRAPS instead of ellipsising. The drawer is narrow and the
     firm's job titles are long and front-loaded with the client name, so every row clipped at
     "Fineas Nedelea - Relevant Contracts T…" — the period, which is the only thing distinguishing one
     row from the next, was always the part cut off. Nothing else competes for the width there now
     (jobRow's inClient drops the duplicated client name and the owner avatars), so two lines of title
     costs nothing. Vertically centred against the taller row so the right-hand column still lines up. */
  .wrow-full .title{white-space:normal;overflow:visible;text-overflow:clip;line-height:1.35}
  .wrow-full .right{align-items:center}
  .tag{font-size:10.5px;font-weight:600;color:var(--muted);background:var(--line-2);border-radius:6px;padding:1px 7px;white-space:nowrap}
  .tag.draft-pending{color:var(--accent-ink);background:var(--accent-soft)}
  .client{font-size:11.5px;color:var(--faint)}
  .wrow .right{padding:10px 15px;display:flex;align-items:center;gap:11px;justify-content:flex-end}
  .owners{display:flex}.owners .avatar{width:22px;height:22px;font-size:9px;border:2px solid var(--panel);margin-left:-6px}.owners .avatar:first-child{margin-left:0}
  .due{font-family:var(--font-num);font-size:11.5px;color:var(--muted);text-align:right;min-width:74px}
  .due.over{color:var(--crit);font-weight:600}.due small{display:block;color:var(--faint);font-weight:400;font-size:10px}
  .pill{font-size:10.5px;font-weight:600;padding:2px 8px;border-radius:20px;white-space:nowrap;display:inline-flex;align-items:center;gap:5px}
  .pill .d{width:6px;height:6px;border-radius:50%;background:currentColor}
  .pill.good{color:var(--good);background:var(--good-soft)}.pill.info{color:var(--info);background:var(--info-soft)}
  .pill.warn{color:var(--warn);background:var(--warn-soft)}.pill.crit{color:var(--crit);background:var(--crit-soft)}
  .pill.bb{color:var(--bb);background:var(--bb-soft)}.pill.accent{color:var(--accent-ink);background:var(--accent-soft)}

  .rail{display:flex;flex-direction:column;gap:14px}
  .att{padding:10px 15px;border-top:1px solid var(--line-2);display:flex;gap:10px;align-items:flex-start}
  .att:hover{background:var(--panel-2)}.att .t{font-weight:600;font-size:12.5px}.att .s{font-size:11px;color:var(--faint);margin-top:2px}
  .att .when{margin-left:auto;font-family:var(--font-num);font-size:10px;color:var(--faint);white-space:nowrap}
  .presence{padding:6px}.pr{display:flex;align-items:center;gap:9px;padding:6px 9px;border-radius:8px}.pr:hover{background:var(--panel-2)}
  .pr .live{width:7px;height:7px;border-radius:50%;flex:none}.pr .nm{font-size:12.5px}.pr .fo{margin-left:auto;font-size:11px;color:var(--faint)}
  /* What someone is clocked on to (presenceRows, js/engine.js). Accent so it reads as live activity
     rather than another line of metadata; the job name is allowed to ellipsis — the row is narrow and
     the person's name must never be squeezed for it. */
  .pr .pr-clk{display:flex;align-items:center;gap:4px;margin-top:2px;font-size:10.5px;color:var(--accent-ink);min-width:0}
  .pr .pr-clk.paused{color:var(--bb)}
  .pr .pr-clk svg{width:11px;height:11px;flex:none;stroke:currentColor;stroke-width:2;fill:none}
  .pr .pr-clk span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

  .subtabs{display:inline-flex;gap:2px;background:var(--line-2);border-radius:10px;padding:3px}
  .subtabs button{border:0;background:transparent;color:var(--muted);font-weight:600;font-size:12.5px;padding:6px 13px;border-radius:7px}
  .subtabs button.on{background:var(--panel);color:var(--ink);box-shadow:var(--shadow)}
  /* Triage's tab bar (js/mail.js renderMail) — several .subtabs pills side by side, ONE functional
     data-tabgroup on this wrapper (see the comment at the call site for why: the app's hash-routing
     walk only ever looks at the first [data-tabgroup] on a surface). Purely visual grouping — a gap
     wide enough to read as separate clusters (Emails/Triage/Insights vs. App Notifications vs.
     Follow-ups), not one long bar. Deliberately NO flex-wrap: inline-flex + wrap is a real Chromium
     sizing trap — the shrink-to-fit width collapses to just the WIDEST single child (verified live:
     three ~100-200px pills stacked vertically instead of sitting in a row, each starting flush left)
     because a wrap-enabled container's intrinsic width is computed as if only one item needs to fit
     per line. Three short pill groups never need to wrap in this header at any real window width. */
  .mail-tabgroups{display:inline-flex;gap:14px}
  /* The email-related cluster (Emails/Triage/Email Insights) tinted apart from the plain-grey
     App Notifications / Follow-ups pills beside it — asked for directly once the three groups had
     a gap between them, so the one that's actually "the email area" reads as visually distinct, not
     just spaced apart. Accent tokens, same family the rest of the app already uses for "this is the
     active/primary thing" (nav .active, .btn.accent) — not a new one-off colour. */
  .subtabs.mail-group{background:var(--accent-soft)}
  .subtabs.mail-group button{color:var(--accent-ink)}
  .subtabs.mail-group button.on{background:var(--panel);color:var(--accent-ink)}
  /* Full-page (client/job) tab bar: icon + label, matching the drawer rail. Scoped to .pg-iconbar so
     every other .subtabs control is unchanged. Icons follow the button colour (currentColor). */
  .subtabs.pg-iconbar{flex-wrap:wrap}
  .subtabs.pg-iconbar button{display:inline-flex;align-items:center;gap:6px}
  .subtabs.pg-iconbar button svg{width:15px;height:15px;flex:none;display:block}
  .tabpane{display:none}.tabpane.on{display:block}

  .toolbar{display:flex;align-items:center;gap:9px;margin-bottom:14px;flex-wrap:wrap}
  /* display:inline-flex, NOT the inline it was for years — and this was a real rendering bug, not
     tidying. Vertical padding on an INLINE element does not contribute to the line box: a .chip is
     30px tall while its line advances about 20px, so the moment a row of chips WRAPS, the second
     line cuts 10px into the first. Measured: -10px overlap before, +3.6px gap after.
     Single-line rows of chips never showed it, which is why it survived so long.

     inline-flex alone only gets the lines flush — borders touching, still ugly — because a line box
     then advances by exactly the chip's height. The 2px vertical margin is what supplies the gap
     (4px between wrapped lines). It adds 4px to a single row of chips too, which reads as slightly
     better spacing rather than as a regression.

     `align-items:center` also centres the one chip that has a child — dataTable's toggle filter,
     a checkbox plus a label — which previously sat on the text baseline. */
  .chip{display:inline-flex;align-items:center;vertical-align:middle;margin:2px 0;
    border:1px solid var(--line);background:var(--panel);color:var(--muted);font-weight:600;
    font-size:12px;padding:6px 11px;border-radius:20px}
  .chip.on{background:var(--accent-soft);border-color:transparent;color:var(--accent-ink)}
  select.f,input.f{border:1px solid var(--line);background:var(--panel);color:var(--ink);border-radius:9px;padding:7px 10px;font-size:12.5px;outline:none}
  input.f{min-width:200px}
  /* Client drawer → Jobs / Deadlines tab control bars. Sticky so they stay put while the (possibly
     long) list scrolls under. Full-bleed via negative margins that cancel .dwr-body's 16px/20px
     padding — otherwise a row scrolls into the padding gap ABOVE a top:0 sticky bar. */
  /* top:-16px (not 0) intentionally: the sticky anchor is .dwr-body's content-box top, which sits
     16px below the visual top because of .dwr-body's padding-top:16px. A -16px offset pulls the bar
     back up flush; the matching 16px padding-top keeps its controls clear of the clip edge. */
  .cjobs-tb,.cdls-tb,.mail-docs-tb{position:sticky;top:-16px;z-index:3;display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:-16px -20px 8px;padding:16px 20px 12px;background:var(--panel);border-bottom:1px solid var(--line-2)}
  .cjobs-tb input.f,.cdls-tb input.f,.mail-docs-tb input.f{flex:1 1 180px;min-width:150px}
  .cjobs-tb select.f,.cdls-tb select.f,.mail-docs-tb select.f{padding:7px 9px;font-size:12px}
  .cjobs-tb label,.cdls-tb label,.mail-docs-tb label{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--muted)}
  .cjobs-tb .lnk,.cdls-tb .lnk{margin-left:auto;display:flex;gap:2px}
  .cjobs-tb .lnk button,.cdls-tb .lnk button{border:0;background:transparent;color:var(--accent-ink);font-weight:600;font-size:11.5px;cursor:pointer;padding:4px 6px;border-radius:6px}
  .cjobs-tb .lnk button:hover,.cdls-tb .lnk button:hover{background:var(--accent-soft)}
  /* The Deadlines tab carries BOTH Expand/Collapse and Regenerate; the first takes the auto margin,
     so the second needs its own gap rather than fighting it for the right-hand edge. */
  .cdls-tb .lnk+#cdls-regen{margin-left:8px!important}
  /* Collapsible group headers in the grouped jobs list. */
  /* The "Showing 15 of 150" row both client tabs put under their toolbar (cFilterNote, engine.js).
     Subtle on purpose: it is orientation, not a finding, and it sits between a toolbar and a list
     that are both busier than it is. */
  .cfilter-note{padding:7px 15px;font-size:11.5px;color:var(--muted);border-bottom:1px solid var(--line-2);display:flex;align-items:center;gap:4px;flex-wrap:wrap}
  .cfilter-note b{color:var(--ink);font-family:var(--font-num)}
  .cfilter-lnk{background:none;border:0;padding:0;margin-left:4px;color:var(--accent-ink);cursor:pointer;font:inherit;text-decoration:underline}
  .cfilter-note .cfilter-lnk:last-child{margin-left:auto;text-decoration:none}
  .cfilter-note .cfilter-lnk:hover{text-decoration:underline}
  .cjobs-grp,.cdls-grp{cursor:pointer;user-select:none}
  .cjobs-grp .chev,.cdls-grp .chev{width:11px;height:11px;stroke:currentColor;stroke-width:2.4;fill:none;flex:none;transition:transform .15s;color:var(--faint)}
  .cjobs-grp.collapsed .chev,.cdls-grp.collapsed .chev{transform:rotate(-90deg)}
  /* A collapsible .dl-sec is still a section heading, but it now has a chevron to line up with. */
  .dl-sec.cdls-grp{display:flex;align-items:center;gap:8px}
  .mini{display:flex;align-items:center;gap:7px;background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:6px 10px;color:var(--faint)}
  .mini .ic{width:15px;height:15px;stroke:currentColor;stroke-width:1.9;fill:none}.mini input{border:0;background:transparent;color:var(--ink);outline:none;font-size:12.5px;min-width:180px}

  .pulse-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:15px}.pulse-grid.three{grid-template-columns:repeat(3,1fr)}.pulse-grid.four{grid-template-columns:repeat(4,1fr)}.pulse-grid.five{grid-template-columns:repeat(5,1fr)}
  .stat{padding:13px 15px}.stat .lab{font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--faint);font-weight:600}
  .stat .v{font-size:25px;font-weight:700;margin-top:5px;letter-spacing:-.02em}.stat .h{font-size:11.5px;color:var(--muted);margin-top:4px}
  .bars{padding:5px 15px 13px}.bar{display:flex;align-items:center;gap:9px;padding:6px 0;border-top:1px solid var(--line-2)}.bar:first-child{border-top:0}
  .bar .bl{font-size:12px;min-width:120px;display:flex;align-items:center;gap:7px;text-align:left}
  .bar .track{flex:1;height:7px;border-radius:4px;background:var(--line-2);overflow:hidden;display:block}
  /* display:block is load-bearing. .fill is a <span>, so without it the element is INLINE and both
     width and height are ignored — which is why these bars have never drawn a fill, on Pulse or
     anywhere else. All anyone ever saw was the empty grey track. */
  .bar .fill{display:block;height:100%;border-radius:4px;min-width:2px}
  /* A drillable row is a button: same layout, but it says so on hover and takes focus. */
  .bar.clickable{border:0;background:none;font-family:var(--font-ui);color:var(--ink);cursor:pointer;border-radius:6px;padding:6px;margin:0 -6px;width:calc(100% + 12px)}
  .bar.clickable:hover{background:var(--panel-2)}
  .bar.clickable:hover .bv{color:var(--accent-ink)}
  .bar .bv{font-family:var(--font-num);font-size:12px;font-weight:600;min-width:38px;text-align:right}
  .swatch{width:9px;height:9px;border-radius:3px;flex:none}

  .tbl{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);overflow:hidden;box-shadow:var(--shadow)}
  .tbl thead th{font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--faint);font-weight:600;text-align:left;padding:10px 13px;border-bottom:1px solid var(--line);background:var(--panel-2);cursor:pointer;white-space:nowrap}
  .tbl td{padding:10px 13px;border-bottom:1px solid var(--line-2);font-size:12.5px;vertical-align:middle}
  .tbl tbody tr:last-child td{border-bottom:0}.tbl tbody tr:hover{background:var(--panel-2);cursor:pointer}
  .tbl .jn{font-weight:600}.tbl .cl{font-size:11px;color:var(--faint)}.tbl .fee{font-family:var(--font-num);font-weight:600;text-align:right}
  .table-wrap{overflow-x:auto}.pagi{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:10px 0;font-size:12px;color:var(--muted)}

  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(268px,1fr));gap:13px}
  .ccard{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);padding:14px;cursor:pointer}
  .ccard:hover{border-color:var(--accent)}
  .ccard .top{display:flex;align-items:center;gap:10px}.ccard .nm{font-weight:700;font-size:14px;line-height:1.2}.ccard .ty{font-size:11px;color:var(--faint);font-weight:600}
  .ccard .svcs{display:flex;gap:5px;flex-wrap:wrap;margin:11px 0 12px}.ccard .foot{display:flex;align-items:center;gap:10px;border-top:1px solid var(--line-2);padding-top:10px}
  .hdot{width:8px;height:8px;border-radius:50%;flex:none}

  .cal{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);overflow:hidden}
  .cal-h{display:flex;align-items:center;gap:12px;padding:12px 15px;border-bottom:1px solid var(--line)}.cal-h .mo{font-weight:700;font-size:15px}
  .cal-grid{display:grid;grid-template-columns:repeat(7,1fr)}
  .cal-dow{font-size:10.5px;font-weight:600;color:var(--faint);text-transform:uppercase;padding:8px 11px;border-bottom:1px solid var(--line)}
  .cal-cell{min-height:96px;border-right:1px solid var(--line-2);border-bottom:1px solid var(--line-2);padding:6px 7px;cursor:pointer}.cal-cell:nth-child(7n){border-right:0}
  .cal-cell:hover{background:var(--panel-2)}.cal-cell.out{background:var(--panel-2);opacity:.5;cursor:default}.cal-cell.out:hover{background:var(--panel-2)}
  .cal-cell.dim{background:var(--panel-2)}.cal-cell.today{outline:2px solid var(--accent);outline-offset:-2px}
  .cal-cell .dn{font-family:var(--font-num);font-size:11.5px;color:var(--muted);font-weight:600;display:flex;align-items:center;gap:5px}
  .cal-cell .dn .td{font-family:var(--font-sans);font-size:9px;font-weight:700;color:var(--accent-ink);text-transform:uppercase;letter-spacing:.04em}
  .cal-cell .dn .cnt{margin-left:auto;font-size:9.5px;background:var(--line);color:var(--muted);border-radius:8px;padding:0 5px;font-weight:700}
  .ev{font-size:10px;font-weight:600;padding:2px 5px;border-radius:4px;margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
  .ev .xtag{font-size:8px;text-transform:uppercase;opacity:.8;font-weight:700}
  .ev.g{color:var(--good);background:var(--good-soft)}.ev.r{color:var(--crit);background:var(--crit-soft)}.ev.b{color:var(--info);background:var(--info-soft)}.ev.a{color:var(--warn);background:var(--warn-soft)}
  .more{font-size:10px;color:var(--faint);margin-top:2px}
  /* calendar chrome — month nav + view toggle */
  .cal-nav{display:flex;align-items:center;gap:8px}.cal-nav .mo{font-weight:700;font-size:15px;min-width:130px;text-align:center}
  .cal-od{font-size:11px;font-weight:700;color:var(--crit);background:var(--crit-soft);border-radius:20px;padding:2px 9px;margin-left:6px}
  .calseg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
  .calseg button{border:0;background:var(--panel);color:var(--muted);font-size:12px;font-weight:600;padding:5px 13px;cursor:pointer}
  .calseg button.on{background:var(--ink);color:var(--panel)}
  .cal-empty{margin:12px;border-color:var(--warn);color:var(--ink)}
  /* Calendar → meetings & personal events (js/calendar-personal.js). A meeting is the person's own time, so it
     takes the one accent; a personal item is neutral with a dashed edge — "yours alone" — because a status
     colour has to mean a status and a dentist appointment is not one. Tasks read as to-dos, not as places to be. */
  .cpl-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 15px;border-bottom:1px solid var(--line)}.cpl-bar .spacer{flex:1}.cpl-bar .btn,.cpl-tog{white-space:nowrap}
  .cpl-tog{border:1px solid var(--line);background:var(--panel);color:var(--muted);font-size:12px;font-weight:600;padding:4px 11px;border-radius:999px;cursor:pointer}
  .cpl-tog.on{background:var(--accent-soft);color:var(--accent-ink);border-color:color-mix(in srgb,var(--accent) 35%,transparent)}
  .cpl-note{padding:8px 15px;font-size:12px;color:var(--muted);background:var(--panel-2);border-bottom:1px solid var(--line)}.cpl-note .btn{margin-left:6px}
  .ev.m{color:var(--accent-ink);background:var(--accent-soft)}
  .ev.p,.ev.t{color:var(--ink);background:var(--bb-soft);border:1px dashed var(--line)}
  .ev.p.done,.ev.t.done{text-decoration:line-through;opacity:.6}
  .swatch.cpl-sw-m{background:var(--accent-soft);border:1px solid var(--accent)}.swatch.cpl-sw-p{background:var(--bb-soft);border:1px dashed var(--faint)}.swatch.cpl-sw-t{background:var(--panel);border:1px solid var(--faint)}
  .cpl-dayhead{display:flex;align-items:center;gap:8px;padding:9px 16px;font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);background:var(--panel-2);border-top:1px solid var(--line-2)}.cpl-dayhead:first-child{border-top:0}
  .cpl-dayhead .spacer{flex:1}.cpl-dayhead .btn{text-transform:none;letter-spacing:0}
  .cpl-dot{width:8px;height:8px;border-radius:50%;flex:none;margin-top:5px}.cpl-dot.m{background:var(--accent)}.cpl-dot.p{background:var(--bb-soft);border:1px dashed var(--faint)}
  .cpl-tick{margin-top:3px;flex:none;cursor:pointer}.cpl-row.done .nm{text-decoration:line-through;color:var(--muted)}
  /* Bank holidays / firm closures tint the day and name it; the person's own leave is green — "away" is a good state, not a warning. */
  .cal-cell.cpl-off{background:var(--bb-soft)}.cal-cell.cpl-off:hover{background:var(--panel-2)}
  .cpl-bh{font-family:var(--font-sans);font-size:9.5px;font-weight:700;color:var(--muted);letter-spacing:.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
  .ev.h{color:var(--good);background:var(--good-soft)}
  .swatch.cpl-sw-h{background:var(--good-soft);border:1px solid var(--good)}.swatch.cpl-sw-b{background:var(--bb-soft);border:1px solid var(--faint)}
  .cpl-dot.h{background:var(--good)}
  /* Calendar → reviews & meet requests (js/calendar-reviews.js). OUTLINED, with a mark, where deadlines and Outlook meetings are
     filled: the shape says "a request between colleagues", the mark says which. Meet request = the accent (a meeting is the
     person's own time, as an Outlook one is) with a two-people mark; review = amber, the Reviews page's "To review" colour, with a
     tick-box mark; an overdue review turns red, the app's one overdue colour, and says "Overdue" in its text too. */
  .ev.crq{background:var(--panel);border:1px solid;display:flex;align-items:center;gap:3px;padding:1px 5px}
  .ev.crq .crq-ic{width:10px;height:10px;flex:none;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
  .ev.crq.mq{color:var(--accent-ink);border-color:var(--accent)}
  .ev.crq.rq{color:var(--warn);border-color:var(--warn)}
  .ev.crq.rq.over{color:var(--crit);border-color:var(--crit);background:var(--crit-soft)}
  .ev.crq .xtag{margin-left:auto}
  .swatch.crq-sw-mq{background:var(--panel);border:1.5px solid var(--accent)}.swatch.crq-sw-rq{background:var(--panel);border:1.5px solid var(--warn)}.swatch.crq-sw-over{background:var(--crit-soft);border:1.5px solid var(--crit)}
  .crq-dot{width:18px;height:18px;border-radius:5px;border:1px solid;display:grid;place-items:center;flex:none;background:var(--panel)}
  .crq-dot .crq-ic{width:11px;height:11px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
  .crq-dot.mq{color:var(--accent-ink);border-color:var(--accent)}.crq-dot.rq{color:var(--warn);border-color:var(--warn)}.crq-dot.rq.over{color:var(--crit);border-color:var(--crit);background:var(--crit-soft)}
  /* The date/time on a request row, drawer and form (js/reviews.js). */
  .rv-when{font-family:var(--font-num);font-variant-numeric:tabular-nums}.rv-when.over{color:var(--crit);font-weight:600}
  .rv-when-f{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end}.rv-when-f[hidden]{display:none}.rv-when-f label{display:flex;flex-direction:column;gap:3px;font-size:11.5px;color:var(--muted)}
  .rv-when-warn{font-size:11.5px;color:var(--warn);margin-top:5px;min-height:0}.rv-when-warn:empty{display:none}
  .rv-teams{display:inline-flex;align-items:center;gap:4px}.rv-teams a{color:var(--accent-ink);font-weight:600}
  .cpl-bhbar{padding:9px 16px;font-size:12.5px;background:var(--bb-soft);color:var(--ink);border-bottom:1px solid var(--line-2)}
  .cpl-priv{color:var(--faint)}.cpl-empty{padding:14px 16px;color:var(--faint);font-size:12.5px}
  .cpl-form{display:flex;flex-direction:column;gap:11px}.cpl-form .cpl-kind{align-self:flex-start}.cpl-form .rs-f>.f[style*="width:100%"]{max-width:none}.cpl-grid{display:flex;gap:10px;flex-wrap:wrap}.cpl-grid .rs-f .f{min-width:0}
  .cpl-privnote{font-size:12px;color:var(--muted);background:var(--bb-soft);border-radius:8px;padding:8px 10px}.cpl-err{color:var(--crit);font-size:12px;min-height:16px}
  /* spans view — execution windows as laned bars per week */
  .cal-spans{display:flex;flex-direction:column}
  .cal-dowrow{display:grid;grid-template-columns:repeat(7,1fr)}
  .sp-week{border-bottom:1px solid var(--line-2)}.sp-week:last-child{border-bottom:0}
  .sp-hdr{display:grid;grid-template-columns:repeat(7,1fr)}
  .sp-dn{font-family:var(--font-num);font-size:10.5px;color:var(--muted);padding:4px 8px;border-right:1px solid var(--line-2)}.sp-dn:nth-child(7n){border-right:0}
  .sp-dn.out{opacity:.4}.sp-dn.today{color:var(--accent-ink);font-weight:700}
  .sp-lanes{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;padding:2px 0 7px;position:relative}
  .sp-bar{border-radius:5px;font-size:10px;font-weight:600;padding:2px 7px;overflow:hidden;display:flex;align-items:center;cursor:pointer;min-width:0}
  .sp-bar .sp-lbl{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .sp-bar.info{background:var(--info-soft);color:var(--info)}.sp-bar.warn{background:var(--warn-soft);color:var(--warn)}.sp-bar.crit{background:var(--crit-soft);color:var(--crit)}.sp-bar.good{background:var(--good-soft);color:var(--good)}
  .sp-bar.temp{outline:1.5px solid currentColor;outline-offset:-1.5px}
  .sp-bar.breach{background:var(--crit);color:#fff;font-weight:700}
  .sp-bar.ghost{background:transparent;border:1px dashed var(--line);color:var(--faint);opacity:.75;cursor:default;font-style:italic}
  .sp-wall{align-self:stretch;border-left:2px dashed var(--crit);justify-self:end;width:0;pointer-events:none}
  /* reschedule dialog */
  .rs-job .nm{font-weight:700;font-size:14px}.rs-job .rs-meta{font-size:11.5px;color:var(--muted);margin-top:3px}
  .rs-quick{display:flex;gap:7px;flex-wrap:wrap}
  .rs-f{display:flex;flex-direction:column;gap:5px;font-size:12px;font-weight:600;color:var(--muted)}.rs-f .f{max-width:200px}
  .rs-preview{font-size:12.5px;color:var(--ink)}
  .rs-ok{font-size:12px;color:var(--good);background:var(--good-soft);border-radius:8px;padding:8px 11px}
  .rs-breach{font-size:12px;color:var(--crit);background:var(--crit-soft);border:1px solid var(--crit);border-radius:8px;padding:9px 11px;line-height:1.45}
  .sp-none{grid-column:1 / 8;font-size:10px;color:var(--faint);padding:0 8px}
  .swatch.wall{width:0;height:12px;border-left:2px dashed var(--crit);background:none}
  .legend{display:flex;gap:15px;flex-wrap:wrap;margin-top:12px;font-size:12px;color:var(--muted)}.legend span{display:flex;align-items:center;gap:6px}
  .dl-sec{font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--faint);padding:11px 15px 4px}.dl-sec.over{color:var(--crit)}
  .dl{display:flex;align-items:center;gap:11px;padding:10px 15px;border-top:1px solid var(--line-2)}.dl:hover{background:var(--panel-2);cursor:pointer}
  .dl .dt{font-family:var(--font-num);font-size:11.5px;color:var(--muted);min-width:64px}.dl .nm{font-weight:600;font-size:13px}.dl .clx{font-size:11px;color:var(--faint)}
  .dl .due{font-family:var(--font-num);font-size:11px;color:var(--muted)}.dl .due.over{color:var(--crit);font-weight:700}
  /* Calendar → Deadlines (js/calendar-deadlines.js): explicit scope bar, bucket tabs, capped block */
  .cdl-scope{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
  .cdl-scope button{border:0;background:var(--panel);color:var(--muted);font-size:12px;font-weight:600;padding:5px 12px;cursor:pointer}
  .cdl-scope button b{font-family:var(--font-num);font-variant-numeric:tabular-nums;margin-left:4px}
  .cdl-scope button.on{background:var(--ink);color:var(--panel)}
  .cdl-scope button:disabled{opacity:.45;cursor:default}
  .cdl-say{padding:10px 15px 0;font-size:11.5px;color:var(--muted);line-height:1.5}
  /* Calendar -> ROS. dataTable brings no outer padding of its own (.dt-scroll is a self-contained
     panel with its own border), so a table dropped straight into a .card runs to the card edge —
     which is exactly how the ROS tabs first shipped. The sides match .cdl-say/.cdl-tabs above so
     the prose, the tabs and the table share one left edge. */
  .ros-tablewrap{padding:4px 15px 15px}
  .ros-empty{padding:14px 15px 18px}
  /* Tax-head chips in a table cell. The display fix that stops wrapped rows overlapping is on
     `.chip` itself now (see above); what is left here is the flex parent that supplies the row gap,
     and a tighter size — five of these sit in one cell, where the standalone padding is too much. */
  /* Row gap 0: `.chip` carries its own 2px vertical margin now, and setting both here doubled
     the spacing to 8.5px between wrapped lines. One source of truth for it. */
  .ros-heads{display:flex;flex-wrap:wrap;gap:0 5px;align-items:center;min-width:0}
  /* The coverage report — what Revenue says we act on, against what Insight is set up to do. */
  .ros-cov{display:flex;flex-direction:column;gap:8px}
  .ros-cov-row{border:1px solid var(--line);border-radius:8px;padding:9px 11px;background:var(--panel-2)}
  .ros-cov-h{display:flex;align-items:center;gap:8px;font-size:13px}
  .ros-cov-need{margin-top:6px;padding-left:10px;border-left:2px solid var(--line)}
  .ros-cov-name{display:block;font-size:12.5px;font-weight:600}
  .ros-cov-why{display:block;font-size:11px;color:var(--muted);line-height:1.45;margin-top:2px}
  .ros-cov-off{margin-top:7px;font-size:11px;color:var(--faint)}
  .ros-cov-note{margin-top:10px;font-size:11.5px;color:var(--muted);line-height:1.5;
    border-top:1px solid var(--line-2);padding-top:9px}
  /* De-linking and deregistering. */
  /* The surface header: counts, an explanation behind an icon, and the action at the end. One row
     instead of the six lines of prose it replaced — the reasoning all still exists, in
     rosRegsInfo/rosInboxInfo; it just stopped charging rent above the table. */
  .ros-hdr{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-top:10px}
  .ros-hdr-counts{font-size:12.5px;color:var(--muted)}
  .ros-hdr-counts b{color:var(--ink)}
  .ros-hdr-gap{flex:1 1 auto}
  .ros-hdr-info{flex-shrink:0}
  .ros-info-legend{margin-top:10px}
  .ros-info-list{margin:6px 0 0;padding-left:18px;display:flex;flex-direction:column;gap:5px;
    font-size:12.5px;line-height:1.5}
  .ros-standing-strip{padding:9px 15px 0}

  /* ---- the client and job REVENUE tabs (js/client-revenue.js) --------------------------------
     The measurement, lifted off the Record tab so the matters there are visible again. The whole
     layout is built to answer "how is this client's filing running" before anybody scrolls. */
  .crv-sec{padding:14px 15px 4px;border-top:1px solid var(--line-2)}
  .crv-sec:first-child{border-top:0}
  .crv-sec h4{margin:0 0 4px;font-size:13px;display:flex;align-items:center;gap:8px}
  .crv-n{font-size:11px;color:var(--muted);font-weight:400}
  /* The headline figures. Same shape as the .cov cards elsewhere, deliberately: a reader who has
     seen Coverage or Grading already knows how to read a row of these. */
  .crv-head{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 12px}
  .crv-stat{flex:1 1 120px;min-width:120px;border:1px solid var(--line);border-radius:10px;
    padding:9px 11px;background:var(--panel-2)}
  .crv-stat-n{font-size:16px;font-weight:650;line-height:1.25}
  .crv-stat-l{font-size:11px;color:var(--muted);margin-top:2px;line-height:1.4}
  .crv-stat.good .crv-stat-n{color:var(--good)}
  .crv-stat.warn .crv-stat-n{color:var(--warn)}
  .crv-stat.crit .crv-stat-n{color:var(--crit)}
  /* One service. A LEFT STRIPE rather than a fill for the late ones: these rows carry a lot of text
     and a tinted block behind all of it reads as an error state for the service as a whole, which
     over-states what one late period means. */
  .crv-svcs{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}
  .crv-svc,.crv-jobper{border:1px solid var(--line);border-left:3px solid var(--line);
    border-radius:9px;padding:9px 11px;background:var(--panel)}
  .crv-svc.late,.crv-jobper.late{border-left-color:var(--crit)}
  .crv-jobper.early{border-left-color:var(--good)}
  .crv-svc-h{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px}
  .crv-svc-n{font-size:11px;color:var(--muted);margin-left:auto}
  .crv-svc-b{font-size:12px;color:var(--muted);margin-top:4px;line-height:1.5}
  .crv-svc-b b{color:var(--ink)}
  /* A one-off service lists its periods instead of averaging them -- see fpByService. */
  .crv-svc-list{margin-top:8px;border-top:1px dashed var(--line-2);padding-top:6px;
    display:flex;flex-direction:column;gap:5px}
  .crv-per{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px}
  .crv-per-l{font-weight:600}
  .crv-per-d{color:var(--muted);font-size:11px}
  .crv-job{cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px;font-size:11px}
  /* The job tab's one period, laid out as facts rather than prose. */
  .crv-grid{display:flex;flex-direction:column;gap:4px;margin-top:8px}
  .crv-kv{display:flex;gap:10px;font-size:12px;align-items:baseline}
  .crv-kv-k{width:74px;flex:0 0 74px;color:var(--muted)}
  .crv-kv-v{min-width:0}
  /* The explanation, or the fact that one is owed. Never silence on a late filing. */
  /* Obligation by obligation, under the period line. Indented and quiet: it is a breakdown of the
     statement above it, not a second statement. */
  .crv-obs{margin:6px 0 0 10px;padding-left:9px;border-left:2px solid var(--line-2);
    display:flex;flex-direction:column;gap:4px}
  .crv-ob{display:flex;align-items:center;gap:7px;flex-wrap:wrap;font-size:11.5px}
  .crv-ob-n{font-weight:600;min-width:132px}
  .crv-ob-d{color:var(--muted)}
  .crv-late-note{margin-top:9px;padding:8px 10px;border-radius:8px;background:var(--panel-2);
    border:1px solid var(--line);font-size:12px;line-height:1.5}
  .crv-late-note div{color:var(--muted);margin-top:3px}
  .crv-late-ask{margin-top:9px;padding:8px 10px;border-radius:8px;background:var(--crit-soft);
    border:1px solid var(--crit);color:var(--crit);font-size:12px;display:flex;align-items:center;
    gap:10px;flex-wrap:wrap}
  .crv-late-ask .btn{margin-left:auto}
  /* What Revenue actually sent, per client or per job. */
  .crv-docs{display:flex;flex-direction:column;gap:0;margin:8px 0 12px}
  .crv-doc{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:12px;
    padding:6px 0;border-top:1px solid var(--line-2)}
  .crv-doc-d{width:86px;flex:0 0 86px;color:var(--muted);font-size:11px}
  .crv-doc-t{min-width:0;flex:1 1 auto}
  .crv-doc-nojob{color:var(--faint);font-size:11px}
  .ros-sync-row{margin-top:7px}
  .ros-sync-line{font-size:11px;color:var(--faint);line-height:1.5}
  .ros-sync-line .bad,.ros-sync-line.bad{color:var(--crit)}
  .ros-standing-chip{display:inline-flex;align-items:center;gap:5px;margin-right:4px}
  .ros-standing-chip b{font-family:var(--font-num);font-variant-numeric:tabular-nums;color:var(--muted)}
  /* A head under a deregistration instruction: struck through on the row, never hidden — the
     instruction is a standing one and has to be legible from the table. */
  /* THE THREE COVERAGE STATES on a tax head. Colour carries the meaning, so each one also gets a
     tooltip and the legend above the table spells them out — colour alone is not an interface.
     `unchecked` is deliberately absent: it keeps the plain chip, because a head Insight has no
     service for is not a gap and must not look like one. */
  .ros-heads .chip.cov-ok{color:var(--good);border-color:var(--good-soft);background:var(--good-soft)}
  .ros-heads .chip.cov-gap{color:var(--warn);border-color:var(--warn);background:var(--warn-soft);font-weight:700}
  /* CEASED — a service that exists and has stopped. Its own colour, not the gap yellow: both need
     an answer but they are different questions, and one buried in the other is how this one spent
     months looking green. Struck through because the cover is over rather than absent. */
  /* The ceased-head count on the registrations header — the same red as the pills it counts, so
     the number and the thing it points at are obviously the same subject. */
  .ros-hdr-ceased{color:var(--crit)}
  .ros-heads .chip.cov-ceased{color:var(--crit);border-color:var(--crit);background:var(--crit-soft);
    font-weight:700;text-decoration:line-through;text-decoration-thickness:1px}
  .ros-heads .chip.cov-waived{color:var(--info);border-color:var(--info-soft);background:var(--info-soft)}
  /* Only the two actionable states invite a click, and they say so. */
  .ros-heads .chip.clickable{cursor:pointer}
  /* The mirror finding, under a rule: services we run that Revenue has no registration for. Filled
     the same yellow as a gap above, because it is the same order of problem -- with no registration
     there is no route to file or pay the head at all (see rosExtraHeadPills). The DASHED border is
     what tells the two apart at a glance without ranking one below the other, and the separator
     does the rest. */
  .ros-heads-sep{flex:0 0 100%;height:0;margin:3px 0;border-top:1px dashed var(--line)}
  .ros-heads .chip.ros-head-extra{color:var(--warn);border-color:var(--warn);
    border-style:dashed;background:var(--warn-soft)}
  .ros-heads .chip.clickable:hover{filter:brightness(0.94)}
  /* A head WE are stepping back from, as distinct from one Revenue is ending. Dereg strikes the
     head through, because the obligation itself goes; a de-link does not -- the tax still exists
     and still has to be filed, just not by us. So this one is dashed and dimmed rather than struck:
     the head is still real, we are simply no longer on it. */
  .ros-heads .chip.hdelink-proposed{border-style:dashed;border-color:var(--info);color:var(--info)}
  .ros-heads .chip.hdelink-done{border-style:dashed;border-color:var(--bb);color:var(--bb);
    background:var(--bb-soft);opacity:.85}
  .ros-heads .chip.dereg-proposed{color:var(--warn);border-color:var(--warn);
    text-decoration:line-through;text-decoration-thickness:1px}
  .ros-heads .chip.dereg-done{color:var(--faint);text-decoration:line-through;
    text-decoration-thickness:1px;opacity:.75}
  /* ---- A HEAD THAT IS BOTH: covered, AND on its way out ---------------------------------------
     Asked for exactly this way: "half green, half yellow or orange ... maybe half and half sort of
     a coloring where each part is unique and distinct, and a tooltip which explains what is
     happening." The left half is the COVERAGE answer, the right half is the EXIT instruction, and
     they are genuinely two independent facts about one head (js/ros-coverage.js rosHeadPillParts).

     Built from two custom properties rather than one rule per combination: four coverage states
     times four exit states is sixteen rules to keep in step, and the fifteenth is the one that
     would be wrong. Each class sets its own tint; the split rule is written once.

     SOFT TINTS ON BOTH SIDES, with the text in --ink. Two saturated halves under one line of text
     is unreadable on whichever half the descender happens to fall.

     The existing dereg/hdelink rules still apply on TOP of this and are what tell the two exits
     apart in a way colour alone cannot: a deregistration strikes the head through because the
     obligation itself ends, a de-link only dashes the border because the tax still has to be
     filed, just not by us. */
  .ros-heads .chip.cov-ok{--cov-tint:var(--good-soft)}
  .ros-heads .chip.cov-ceased{--cov-tint:var(--crit-soft)}
  .ros-heads .chip.cov-gap{--cov-tint:var(--warn-soft)}
  .ros-heads .chip.cov-waived{--cov-tint:var(--info-soft)}
  .ros-heads .chip.exit-delink-proposed,.ros-heads .chip.exit-delink-done{
    --exit-tint:var(--info-soft);--exit-line:var(--info)}
  .ros-heads .chip.exit-dereg-proposed,.ros-heads .chip.exit-dereg-done{
    --exit-tint:var(--warn-soft);--exit-line:var(--warn)}
  /* A finished instruction is the same two facts, quieter: it is history rather than a decision
     waiting on somebody. */
  .ros-heads .chip.exit-delink-done,.ros-heads .chip.exit-dereg-done{opacity:.8}
  .ros-heads .chip.head-split{
    background:linear-gradient(90deg,var(--cov-tint) 0 50%,var(--exit-tint) 50% 100%);
    border-color:var(--exit-line);color:var(--ink);font-weight:700}
  /* The seam, so the two halves read as two halves rather than as a gradient. */
  .ros-heads .chip.head-split{position:relative}
  .ros-heads .chip.head-split::before{content:"";position:absolute;left:50%;top:0;bottom:0;
    width:1px;background:var(--exit-line);opacity:.45}
  /* A head carrying the CLIENTS decision rather than one of its own (rosHeadInstruction,
     inherited:true). Dotted rather than dashed, so at a glance it reads as the same kind of thing
     one step removed — and the tooltip says whose decision it is. */
  .ros-heads .chip.exit-inherited{border-style:dotted}
  .ros-st-body{display:flex;flex-direction:column;gap:12px}
  .ros-st-sec{border:1px solid var(--line);border-radius:9px;padding:11px 12px}
  .ros-st-h{font-weight:650;font-size:13px}
  .ros-st-hint{font-size:11.5px;color:var(--muted);line-height:1.45;margin-top:3px}
  .ros-st-note{font-size:11.5px;color:var(--muted);line-height:1.5;border:1px solid var(--line);
    border-left:3px solid var(--bb);border-radius:7px;padding:8px 10px;background:var(--panel-2)}
  .ros-st-note.bad{border-left-color:var(--crit)}
  .ros-st-note.good{border-left-color:var(--good)}
  .ros-st-state{display:flex;align-items:center;gap:8px;margin-top:9px;font-size:12.5px}
  .ros-st-trail{font-size:11px;color:var(--faint);line-height:1.45;margin-top:5px}
  .ros-st-act{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
  .ros-st-heads{display:flex;flex-direction:column;gap:2px;margin:9px 0 4px}
  .ros-st-head{display:flex;align-items:center;gap:7px;font-size:12.5px;padding:4px 2px;cursor:pointer}
  .ros-st-head-n{font-weight:600}
  .ros-st-head.done .ros-st-head-n,.ros-st-head.proposed .ros-st-head-n{text-decoration:line-through}
  .ros-st-head-w{margin-left:auto;font-size:10.5px;color:var(--faint)}
  .ros-heads .chip{padding:2px 8px;font-size:11px;line-height:1.5;white-space:nowrap}

  /* ---- filing a Revenue document away -------------------------------------------------------
     The dialog is wider than a normal appModal because its middle is a table: one line per client,
     tax head and period, since a single Revenue letter can be about several of each. */
  .ros-fa-body{display:flex;flex-direction:column;gap:11px}
  .ros-fa-doc{border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--panel-2)}
  .ros-fa-doc-t{font-weight:650;font-size:13.5px}
  .ros-fa-doc-m{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:5px;font-size:11.5px;color:var(--muted)}
  .ros-fa-why{font-size:11.5px;color:var(--muted);line-height:1.5}
  .ros-fa-hint{font-size:11px;color:var(--faint);line-height:1.45}
  .ros-fa-none{font-size:12px;color:var(--faint);padding:12px;border:1px dashed var(--line);border-radius:8px;line-height:1.5}
  /* The table scrolls sideways inside its own box rather than widening the dialog: a client name
     column that pushes the Remove button off the edge is how a row becomes unremovable. */
  /* NO OVERFLOW HERE. It used to be `overflow-x:auto` to stop a long client name pushing the
     Remove button off the edge — but CSS forces the other axis to `auto` the moment one axis is not
     `visible`, so the box clipped VERTICALLY too and ate the job dropdown. Measured: the wrapper
     ended at 516px while the open list reached 681px.

     The columns are narrow enough not to need it in an 880px dialog, and a clipped picker is a far
     worse failure than a cramped column. */
  .ros-fa-tw{border:1px solid var(--line);border-radius:8px}
  .ros-fa-t{width:100%;border-collapse:collapse;font-size:12px}
  .ros-fa-t th{text-align:left;font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
    color:var(--faint);padding:7px 9px;border-bottom:1px solid var(--line);white-space:nowrap}
  .ros-fa-t td{padding:6px 9px;border-bottom:1px solid var(--line-2);vertical-align:middle}
  .ros-fa-t tr:last-child td{border-bottom:0}
  .ros-fa-t td:first-child{min-width:200px}
  .ros-fa-t td:last-child{width:1%;text-align:right}
  .ros-fa-t .f{width:100%;min-width:110px}
  .ros-fa-t .ss-wrap{min-width:190px}
  /* The notice issue date (js/ros-file-away.js). Narrow and tabular: it is the one control in this
     table with a fixed width, and the four-working-day clock runs from what it says. */
  .ros-fa-t .ros-fa-date{min-width:140px;width:140px;font-family:var(--font-num);font-variant-numeric:tabular-nums}
  .cdl-tabs{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 15px 2px}
  /* The service chips, directly under the bucket tabs — same control as the Future year picker
     (.cdl-years), because they are the same kind of question. Twenty of them wrap, so they get
     their own band rather than a place inside the tab row. */
  .cdl-svcs{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:9px 15px 2px}
  .cdl-svcs .lab{font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--faint)}
  .cdl-svcs button{border:1px solid var(--line);background:var(--panel);color:var(--muted);font-size:11.5px;font-weight:600;padding:3px 9px;border-radius:20px;cursor:pointer}
  .cdl-svcs button b{font-family:var(--font-num);font-variant-numeric:tabular-nums;margin-left:3px;color:var(--faint)}
  .cdl-svcs button.on{background:var(--ink);color:var(--panel);border-color:var(--ink)}
  .cdl-svcs button.on b{color:var(--panel)}
  /* Nothing in the alert window for it — still offered (it may have rows on Future), just quieter. */
  .cdl-svcs button.empty{opacity:.55}
  .cdl-svcs button.empty.on{opacity:1}
  .cdl-svchint{flex:1 1 240px;min-width:180px;font-size:11px;color:var(--faint)}
  /* All tab — the running total under its two controls, and the quieter look of a closed row. */
  .cdl-allsum{padding:9px 15px 0;font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
  .cdl-years button:disabled{opacity:1;cursor:default}
  .dl .due.done{color:var(--good);font-weight:600}
  .cdl-tabs .subtabs button b{font-family:var(--font-num);font-variant-numeric:tabular-nums;margin-left:4px}
  .cdl-sech{font-weight:600;letter-spacing:0;text-transform:none;color:var(--faint)}
  .cdl-more{display:flex;align-items:center;gap:9px;padding:10px 15px;font-size:11.5px;color:var(--muted);border-top:1px solid var(--line-2)}
  .cdl-none{padding:16px 15px;font-size:12.5px;color:var(--faint)}
  /* The Calendar's head control: the scope strip over the two tabs. One column so the strip reads as
     governing everything under it, right-aligned to sit under the page title's own right edge. */
  .cal-head-ctl{display:flex;flex-direction:column;align-items:flex-end;gap:8px}
  /* WHO IS WATCHING THIS SURFACE — the designation strip (js/designations.js, surfaceDesignationStrip).
     Sits above the scope bar because it is a statement about the surface, not a control on it: it
     never changes what you see, so it must not look like the buttons underneath. Hence a label plus
     initials, no button affordance, and the quieter ground. */
  .desig-strip{display:inline-flex;align-items:center;gap:5px;font-size:11px;color:var(--faint)}
  .desig-strip + .desig-strip{margin-left:12px}
  .desig-lab{text-transform:uppercase;letter-spacing:.04em;font-weight:600}
  .desig-chip{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;
    padding:0 4px;border-radius:999px;background:var(--panel-2);border:1px solid var(--line-2);
    color:var(--ink);font-size:10.5px;font-weight:700;letter-spacing:.02em}
  .cdl-scope{flex-wrap:wrap}
  /* Two halves, shaded apart: what is yours reads on the panel, the firm's on the quieter ground —
     the same pairing Home's matrix uses for its bands, so the grouping means one thing app-wide. */
  .cdl-scope button.yours{font-weight:700;background:var(--panel);color:var(--ink)}
  .cdl-scope button.firm{background:var(--panel-2);color:var(--muted)}
  .cdl-scope button.edge{border-left:2px solid var(--line)}
  .cdl-scope button.on,.cdl-scope button.firm.on{background:var(--ink);color:var(--panel)}
  /* Future tab: the year multi-select. Chips rather than a <select>, because the whole point is that
     more than one year can be on at once and you can see which. */
  .cdl-years{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:11px 15px 4px}
  .cdl-years .lab{font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--faint)}
  .cdl-years button{border:1px solid var(--line);background:var(--panel);color:var(--muted);font-family:var(--font-num);font-variant-numeric:tabular-nums;font-size:11.5px;font-weight:600;padding:3px 9px;border-radius:20px;cursor:pointer}
  .cdl-years button.on{background:var(--ink);color:var(--panel);border-color:var(--ink)}
  .cdl-yhint{font-size:11px;color:var(--faint)}
  /* day-detail overlay rows */
  .btn.sm{padding:4px 9px;font-size:11px;border-radius:7px}
  .cald-body{max-height:min(70vh,560px);overflow:auto;display:block;padding:0}
  .cald-row{display:flex;align-items:flex-start;gap:11px;padding:11px 16px;border-top:1px solid var(--line-2)}.cald-row:first-child{border-top:0}
  .cald-row .hdot{margin-top:4px}.cald-main{min-width:0;flex:1}.cald-main .nm{font-weight:600;font-size:13.5px}.cald-main .sub{font-size:12px;color:var(--muted)}.cald-main .meta{font-size:10.5px;color:var(--faint);margin-top:2px}
  .cald-side{display:flex;flex-direction:column;align-items:flex-end;gap:3px;flex:none}
  .cald-side .due{font-family:var(--font-num);font-size:12px;font-weight:600}.cald-side .due.over{color:var(--crit)}
  .cald-side .ld{font-size:10.5px;color:var(--faint)}
  /* Not faint italic: "no job exists for this deadline" is a finding, not a footnote. Sized to sit
     beside the Reschedule button without pretending to be one. */
  .cald-side .nolink{font-size:10.5px;color:var(--warn);font-weight:600;white-space:nowrap;background:var(--warn-soft);border:1px solid color-mix(in srgb,var(--warn) 28%,transparent);border-radius:999px;padding:3px 8px;cursor:help}
  .cald-acts{display:flex;gap:6px;align-items:center;margin-top:3px}
  /* Admin → People → Permissions → Feature switches toggles */
  .pf-row{display:flex;align-items:center;gap:14px;padding:12px 15px;border-top:1px solid var(--line-2)}
  .pf-row .pf-t{font-weight:600;font-size:13px}.pf-row .pf-d{font-size:11.5px;color:var(--muted);margin-top:2px}
  .pf-switch{flex:none;width:42px;height:24px;border-radius:20px;border:1px solid var(--line);background:var(--panel-2);position:relative;cursor:pointer;padding:0;transition:background .15s}
  .pf-switch .pf-knob{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:var(--muted);transition:transform .15s,background .15s}
  .pf-switch.on{background:var(--warn);border-color:var(--warn)}.pf-switch.on .pf-knob{transform:translateX(18px);background:#fff}
  .pf-switch[disabled]{opacity:.5;cursor:default}

  .cov-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:13px;margin-bottom:16px}
  .cov{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);padding:14px 15px;cursor:pointer;border-left:3px solid var(--line)}
  .cov:hover{border-color:var(--accent)}.cov.on{border-color:var(--accent)}
  .cov.sev-crit{border-left-color:var(--crit)}.cov.sev-warn{border-left-color:var(--warn)}.cov.sev-ok{border-left-color:var(--good)}
  .cov .cn{font-size:24px;font-weight:700;letter-spacing:-.02em}.cov .ct{font-size:12.5px;font-weight:600;margin-top:2px}.cov .cd{font-size:11px;color:var(--faint);margin-top:4px}

  .feed .fi{display:flex;gap:11px;padding:12px 15px;border-top:1px solid var(--line-2)}.feed .fi:hover{background:var(--panel-2)}
  .feed .ft{font-size:13px;line-height:1.4}.feed .ft b{font-weight:600}.feed .ft .q{color:var(--muted)}.feed .fm{font-size:11px;color:var(--faint);margin-top:4px;display:flex;align-items:center;flex-wrap:wrap;gap:2px}.feed .fm .pill{font-size:9.5px;padding:1px 7px;margin-right:2px}
  .feed .fb{font-size:12.5px;line-height:1.45;color:var(--muted);margin-top:3px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
  .feed .feed-tgt{font-weight:600;color:var(--accent-ink);cursor:pointer}.feed .feed-tgt:hover{text-decoration:underline}
  /* Notification row — shared by Team → Inbox and the header bell dropdown */
  .nrow{display:flex;gap:11px;padding:11px 15px;border-top:1px solid var(--line-2)}.nrow:first-child{border-top:0}.nrow:hover{background:var(--panel-2)}
  .nrow .ft{font-size:12.5px;line-height:1.45;display:flex;align-items:center;flex-wrap:wrap;gap:5px}.nrow .ft .q{color:var(--muted)}
  .nrow .ft .pill{font-size:9.5px;padding:1px 7px}.nrow .feed-tgt{font-weight:600;color:var(--accent-ink);cursor:pointer}.nrow .feed-tgt:hover{text-decoration:underline}
  .nrow .fb{font-size:12px;line-height:1.45;color:var(--muted);margin-top:3px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
  .nrow .fm{font-size:10.5px;color:var(--faint);margin-top:3px}
  .nrow.compact{padding:9px 13px;gap:9px}.nrow.compact .fb{-webkit-line-clamp:1}
  .nrow.read{opacity:.62}.nrow.read:hover{opacity:1}
  /* Header bell */
  #notifBtn{position:relative}
  /* Corner marker on the 34px button. The small overhang stays inside the topbar's 10px padding,
     which is what keeps it clear of #main's clip edge when the topbar is stuck at the top; the ring
     is a box-shadow so the pill keeps its full 16px interior (see §Count badges). */
  #notifBtn .badge{position:absolute;top:-4px;right:-4px;font-size:9.5px;padding:0 4px;box-shadow:0 0 0 2px var(--panel)}
  /* What's new (✨) — the badge is a friendly "there's something new", not an alert, so accent not crit. */
  #wnBtn .badge{position:absolute;top:-4px;right:-4px;font-size:9.5px;padding:0 4px;background:var(--accent);box-shadow:0 0 0 2px var(--panel)}
  .wn-modal{width:min(500px,94vw)}
  /* Id selector, not a class — modal.js injects .am-body{display:grid;padding:…} at runtime (after
     this file), so a same-specificity class would lose to it; #wn-list outranks it either way. */
  #wn-list{display:block;gap:0;padding:6px 0}
  .wn-entry{padding:11px 18px;border-top:1px solid var(--line-2)}.wn-entry:first-child{border-top:0}
  .wn-meta{display:flex;align-items:center;gap:8px;margin-bottom:3px}
  .wn-date{font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
  /* Subtle per-entry copy button — sits at the far right of the meta row, faint until you hover the
     entry, for devs who want to paste an update straight into Teams. */
  .wn-copy{margin-left:auto;flex:none;display:inline-grid;place-items:center;width:24px;height:24px;padding:0;border:0;background:none;color:var(--faint);border-radius:6px;cursor:pointer;opacity:0;transition:opacity .12s,color .12s,background .12s}
  .wn-entry:hover .wn-copy,.wn-copy:focus-visible{opacity:1}
  .wn-copy:hover{color:var(--accent-ink);background:var(--line-2)}
  .wn-copy.copied{opacity:1;color:var(--good)}
  .wn-copy .ic{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none}
  .wn-title{font-weight:700;font-size:12.5px;color:var(--ink)}
  .wn-body{margin-top:2px;font-size:11.5px;color:var(--muted);line-height:1.45}
  .wn-body b{font-weight:600;color:var(--ink)}
  .wn-more-wrap{display:flex;justify-content:center;padding:14px 18px 6px}
  .wn-empty{padding:24px 18px;text-align:center;font-size:12.5px;color:var(--faint)}
  #notifMenu{position:absolute;top:42px;right:0;width:360px;max-width:86vw;background:var(--panel);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow-lg);z-index:60;overflow:hidden}
  .notif-h{display:flex;align-items:center;justify-content:space-between;padding:11px 14px;border-bottom:1px solid var(--line);font-weight:700;font-size:13px}.notif-h .q{font-weight:500;font-size:11px;color:var(--faint)}
  .notif-list{max-height:52vh;overflow-y:auto}
  .notif-foot{padding:10px 14px;border-top:1px solid var(--line);font-size:12px;font-weight:600;color:var(--accent-ink);cursor:pointer;text-align:center}.notif-foot:hover{background:var(--panel-2)}
  .kv{display:flex;justify-content:space-between;padding:8px 15px;border-top:1px solid var(--line-2);font-size:12.5px}.kv:first-of-type{border-top:0}.kv .k{color:var(--muted)}.kv .v{font-weight:600;font-family:var(--font-num)}

  .backdrop{position:fixed;inset:0;background:rgba(12,16,26,.42);opacity:0;pointer-events:none;transition:opacity .2s;z-index:40}.backdrop.open{opacity:1;pointer-events:auto}
  .drawer{position:fixed;top:0;right:0;height:100vh;width:min(580px,94vw);background:var(--panel);border-left:1px solid var(--line);box-shadow:var(--shadow-lg);transform:translateX(100%);transition:transform .26s cubic-bezier(.4,0,.2,1);z-index:50;display:flex;flex-direction:column}
  .drawer.open{transform:none}
  /* The drawer is a fixed-height (100vh) flex COLUMN. The header and tab rail must never shrink — only
     the body may — or a tall tab (Jobs/Deadlines/Timeline/Grading) makes the column overflow and squeezes
     the "top area" (the name clips, the rail collapses). flex:none pins header+rail; the body grows and
     scrolls (flex:1 1 0 + min-height:0 so it can actually shrink below its content and scroll internally). */
  .dwr-h{padding:17px 20px;border-bottom:1px solid var(--line);display:flex;align-items:flex-start;gap:12px;overflow:hidden;flex:0 0 auto}.dwr-h .nm{font-size:18px;font-weight:700}.dwr-h .sb{font-size:12px;color:var(--faint);margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .dwr-body{padding:16px 20px;overflow-y:auto;flex:1 1 0;min-height:0}
  /* Tab rail: a horizontal scroller (never a wrap) so 9+ tabs don't clip in the narrow side panel.
     flex:0 0 auto is load-bearing — with overflow-x:auto a flex item's automatic min-height becomes 0,
     so without it the rail collapsed to just its scrollbar when the panel body was tall. To stay compact,
     each tab is an ICON with the label revealed only when it's active (full label always in the tooltip);
     tabs with no mapped icon keep their text. Thin dividers separate them. */
  .dwr-tabs{display:flex;gap:0;padding:0 10px;border-bottom:1px solid var(--line);flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;flex:0 0 auto;scrollbar-width:thin}
  .dwr-tabs::-webkit-scrollbar{height:6px}.dwr-tabs::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:3px}
  .dwr-tabs button{display:inline-flex;align-items:center;gap:6px;border:0;background:transparent;color:var(--muted);font-weight:600;font-size:12.5px;padding:11px 13px;border-bottom:2px solid transparent;white-space:nowrap;flex:0 0 auto;cursor:pointer;transition:color .15s}
  .dwr-tabs button + button{border-left:1px solid var(--line-2)}
  .dwr-tabs button svg{width:16px;height:16px;flex:none;display:block}
  .dwr-tabs button:hover{color:var(--body)}
  .dwr-tabs button.on{color:var(--accent-ink);border-bottom-color:var(--accent)}
  .dwr-tabs button.has-ico:not(.on) .dt-lbl{display:none}   /* icon-only until active; full label lives in title */
  /* Sub-tab strip under the rail (client / job drawers): the tabs inside the active group. Empty — and
     so hidden — for a group of one and for every drawer that has no groups. Same never-wrap scroller
     rule as the rail, and flex:0 0 auto for the same reason. */
  .dwr-sub{display:flex;gap:6px;padding:8px 14px;border-bottom:1px solid var(--line);flex:0 0 auto;overflow-x:auto;overflow-y:hidden;scrollbar-width:thin}
  .dwr-sub:empty{display:none}
  .dwr-sub button{border:1px solid var(--line);background:transparent;color:var(--muted);font-weight:600;font-size:12px;padding:4px 11px;border-radius:999px;white-space:nowrap;flex:0 0 auto;cursor:pointer;transition:color .15s,background .15s}
  .dwr-sub button:hover{color:var(--body)}
  .dwr-sub button.on{background:var(--accent-soft);color:var(--accent-ink);border-color:transparent}

  .loader{position:fixed;inset:0;z-index:100;display:grid;place-items:center;background:var(--ground)}
  .loader.hidden{display:none}
  .drop{width:min(560px,90vw);background:var(--panel);border:1px solid var(--line);border-radius:18px;box-shadow:var(--shadow-lg);padding:34px;text-align:center}
  .drop .logo{width:52px;height:52px;border-radius:14px;margin:0 auto 16px;display:grid;place-items:center;background:linear-gradient(135deg,var(--accent),#847fee);color:#fff;font-weight:800;font-size:24px;font-style:italic}
  .drop h2{font-size:20px;margin-bottom:6px}.drop p{color:var(--muted);font-size:13.5px;margin:0 0 20px}
  .dz{border:2px dashed var(--line);border-radius:14px;padding:26px;color:var(--faint);font-size:13px;transition:border-color .15s,background .15s}
  .dz.hot{border-color:var(--accent);background:var(--accent-soft)}
  .drop .or{margin:14px 0;color:var(--faint);font-size:12px}
  .note{margin:20px 0 0;padding:11px 15px;border-radius:var(--r);border:1px dashed var(--line);background:var(--panel-2);font-size:12px;color:var(--muted)}
  .note.err{border:1px solid var(--crit);border-style:solid;background:var(--crit-soft);color:var(--crit)}
  /* The attention sibling of .note.err, for something the reader must DO rather than something that
     went wrong. Filled and solid-bordered so it stops the eye, but the body text stays --ink: all-amber
     text is fine for .note.err's one red line and unreadable for three lines of instruction, so only
     the bold lead takes the colour. */
  .note.warn{border:1px solid color-mix(in srgb,var(--warn) 45%,transparent);border-style:solid;background:var(--warn-soft);color:var(--ink)}
  .note.warn b{color:var(--warn)}

  /* ---- Access gate: "Continue with Microsoft" + collapsed serial-key fallback, mirrors the
     real production app's login screen (src/components/auth/login-form.tsx) ---- */
  /* Proper app sign-in (see #loginGate) — brand identity, not an explainer. */
  .login{max-width:400px;padding:40px 34px 30px}
  .login .brand-title{font-size:30px;font-weight:800;font-style:italic;letter-spacing:-.01em;margin:2px 0 6px}
  .login .brand-tag{color:var(--faint);font-size:12px;font-weight:600;letter-spacing:.02em;margin:0 0 24px;text-transform:none}
  .login .login-foot{color:var(--faint);font-size:12px;margin:14px 0 0}
  .ms-btn{width:100%;justify-content:center;height:44px;background:var(--ink);color:var(--panel);border:0;font-size:13.5px;font-weight:600;border-radius:var(--r-sm)}
  .ms-btn:hover{opacity:.92}.ms-btn:disabled{opacity:.6}
  .serial-key{margin-top:18px;text-align:left}
  .serial-key summary{cursor:pointer;list-style:none;text-align:center;font-size:12.5px;font-weight:600;color:var(--faint)}
  .serial-key summary:hover{color:var(--ink)}
  .serial-key summary::-webkit-details-marker{display:none}
  .serial-key form{margin-top:14px}
  .serial-key #serialKeyInput{width:100%;box-sizing:border-box;margin-bottom:10px}
  .serial-key #serialKeySubmit{width:100%;justify-content:center}

  /* DataTable primitive */
  .dt-toolbar{display:flex;align-items:center;gap:8px;margin-bottom:11px;flex-wrap:wrap}
  .dt-seg{display:inline-flex;background:var(--line-2);border-radius:9px;padding:3px}
  .dt-seg button{border:0;background:transparent;color:var(--muted);font-weight:600;font-size:12px;padding:5px 13px;border-radius:6px;cursor:pointer;white-space:nowrap}
  .dt-seg button.on{background:var(--panel);color:var(--ink);box-shadow:var(--shadow)}
  /* Divides options that aren't the same KIND of choice — e.g. WIP's budget buckets, which partition
     billable work, versus Internal, which is a different population entirely. */
  .dt-seg .dt-seg-div{width:1px;background:var(--line);margin:5px 4px;flex:0 0 1px}
  .dt-edit{display:inline-flex;align-items:center;gap:2px;cursor:pointer;border-radius:6px;padding:1px 3px 1px 1px;margin:-1px -3px -1px -1px}
  .dt-edit:hover{background:var(--line-2)}
  .dt-edit .dt-edit-c{width:12px;height:12px;stroke:var(--faint);stroke-width:2;fill:none;opacity:.5;flex:none}
  .dt-edit:hover .dt-edit-c{opacity:.9}
  .dt-editsel{min-width:120px;font-size:12px}
  .btn.dt-on{border-color:var(--accent);color:var(--accent-ink);background:var(--accent-soft)}
  tr.dt-grouphdr td{background:var(--panel-2);font-weight:700;font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);padding:8px 13px;border-bottom:1px solid var(--line)}
  .dt-grouphdr{cursor:pointer;user-select:none}
  .dt-grouphdr .dt-gcount,.dt-grouphdr.sect .dt-gcount{font-family:var(--font-num);color:var(--faint);background:var(--line-2);border-radius:20px;padding:0 7px;margin-left:8px;font-weight:600}
  /* Opt-in via cfg.groupCountAccent (see datatable.js) — the count itself is the signal, e.g. a
     Queue client holding more than one open bill, i.e. one that can be merged. */
  .dt-grouphdr .dt-gcount-accent,.dt-grouphdr.sect .dt-gcount-accent{color:var(--warn);background:var(--warn-soft)}
  .dt-gsum{display:inline-flex;gap:14px;margin-left:16px;font-family:var(--font-num);text-transform:none;letter-spacing:0;font-weight:600}
  .dt-gsum-item{color:var(--ink)}
  .dt-gsum-lbl{color:var(--faint);font-weight:600;margin-right:3px}
  .dt-grouped{display:flex;flex-direction:column;gap:14px}
  .dt-grouphdr.sect{font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);padding:2px 2px 8px;display:flex;align-items:center}
  .dt-grp-chev{display:inline-block;width:13px;text-align:center;margin-right:5px;color:var(--faint);font-size:10px}
  /* .qb-row.so-row, not .so-row: every level row also carries .qb-row, whose four-column grid comes
     later in this file at the same specificity and silently won — the ↑↓ pair then pushed × onto a
     line of its own. */
  .qb-row.so-row{grid-template-columns:86px 1.4fr 1fr auto auto}.so-lead{font-size:11px;color:var(--faint)}
  /* A group level's own tick sits in its "Group by / then by" label; a level ticked off stays in its
     place, dimmed, so it's plain what will come back. */
  .so-lvl{display:flex;align-items:center;gap:5px;cursor:pointer}.so-lvl input{margin:0}
  .so-lvloff .f,.so-lvloff .so-move{opacity:.5}
  .so-move{display:inline-flex;gap:3px}
  /* Sort & group builder: grouping first, then sorting (§4.11) — one section each. */
  .so-sect{padding-bottom:14px}.so-sect+.so-sect{border-top:1px solid var(--line);padding-top:14px}
  .so-switch{display:flex;align-items:center;gap:8px;font-size:12.5px;font-weight:600;color:var(--ink);cursor:pointer;margin-bottom:10px}
  .so-title{font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:10px}
  .so-rows.so-off{opacity:.5}
  .so-addbtn{margin-top:6px}
  .so-hint{font-size:11.5px;color:var(--muted);margin-top:8px;line-height:1.45}
  /* Nested grouping: a sub-level heading reads as part of the section above it — sentence case on the
     plain panel, indented by depth (set inline) — so the top level keeps its band and a job is never
     mistaken for a client. Once levels nest, each heading also leads with its field's name. */
  tr.dt-grouphdr.dt-gsub td{background:var(--panel);text-transform:none;letter-spacing:0;font-size:12px;font-weight:600;color:var(--ink)}
  .dt-grouphdr.sect.dt-gsub{text-transform:none;letter-spacing:0;font-size:12px;font-weight:600;color:var(--ink)}
  .dt-gnest{margin-left:18px}
  .dt-gfield{font-size:9.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--faint);margin-right:7px}
  .dt-depth .dt-seg-lbl{align-self:center;font-size:11px;font-weight:600;color:var(--faint);padding:0 6px 0 8px}
  .dt-grpbtns{display:inline-flex;gap:4px}
  /* The toolbar grouping chip: the main tick, then one tick per level. A level ticked off is struck
     through but keeps its place, so the chip always reads as the whole arrangement. */
  .chip.dt-grpchip{gap:7px;user-select:none}
  .chip.dt-grpchip label{display:inline-flex;align-items:center;cursor:pointer}
  .chip.dt-grpchip input{margin:0 5px 0 0}
  .chip.dt-grpchip .dt-gsep{opacity:.6}
  .chip.dt-grpchip .dt-glvl.off{opacity:.6;text-decoration:line-through}
  .mock-pill{display:inline-flex;align-items:center;gap:4px;font-size:9.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--warm);background:var(--warm-soft);border:1px dashed var(--warm);border-radius:20px;padding:1px 8px;white-space:nowrap}
  .dt-toolbar .spacer{flex:1;min-width:12px}
  .dt-badge{font-family:var(--font-num);font-size:10px;background:var(--accent);color:#fff;border-radius:20px;padding:0 5px;margin-left:5px}
  .dt-chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:-2px 0 12px}
  .dt-chip{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;background:var(--accent-soft);color:var(--accent-ink);border-radius:20px;padding:3px 5px 3px 11px}
  .dt-chip button{border:0;background:transparent;color:inherit;font-size:15px;line-height:1;cursor:pointer;padding:0 3px;opacity:.7}.dt-chip button:hover{opacity:1}
  .dt-chip.clr{background:var(--line-2);color:var(--muted);padding:3px 11px;cursor:pointer}
  /* Pagination — first/prev/next/last icon buttons + page-size select (shown above & below every table) */
  .pagi select.f{padding:5px 8px;font-size:12px;width:auto}
  .pagi .num{color:var(--ink);font-weight:600;padding:0 2px;font-family:var(--font-num)}
  .dt-pagebtn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;flex:none;border-radius:7px;border:1px solid var(--line);background:var(--panel);color:var(--muted);cursor:pointer}
  .dt-pagebtn:hover:not(:disabled){background:var(--panel-2);color:var(--ink);border-color:var(--faint)}
  .dt-pagebtn:disabled{opacity:.35;cursor:default}
  .dt-pagebtn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none}
  /* Sticky header + independent vertical scroll, with a synced horizontal scrollbar duplicated above the table */
  .dt-hscroll-top{overflow-x:auto;overflow-y:hidden;height:14px;margin-bottom:2px}
  .dt-hscroll-top-inner{height:1px}
  /* Multi-select filter: a button that opens a checkbox list. Used where a row can carry SEVERAL
     values (staff on a job, states a job's time sits in), which a single-choice select can't express
     without listing every combination as its own option. */
  .dt-multi{position:relative;display:inline-block}
  .dt-multi-menu{display:none;position:absolute;top:calc(100% + 4px);left:0;z-index:30;background:var(--panel);border:1px solid var(--line);border-radius:9px;box-shadow:var(--shadow-lg);padding:6px;min-width:210px;max-height:300px;overflow:auto}
  .dt-multi.open .dt-multi-menu{display:block}
  .dt-multi-menu label{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;font-size:12px;color:var(--ink);cursor:pointer;white-space:nowrap}
  .dt-multi-menu label:hover{background:var(--row-hover)}
  /* Distinguishes "still arriving" from "nothing here" in a table's empty state — a quiet pulse,
     since it appears on first view of a heavy collection and shouldn't read as an error. */
  /* Continuity marker injected between rows when sorted by a gapRow column (e.g. missing invoice
     numbers). Reads as a break in the sequence, not a data row — no hover, dashed rule. */
  tr.dt-gaprow td{background:repeating-linear-gradient(135deg,transparent,transparent 6px,var(--line-2) 6px,var(--line-2) 7px);padding:5px 13px;font-size:11px;color:var(--muted);border-top:1px dashed var(--line);border-bottom:1px dashed var(--line)}
  tr.dt-gaprow .gap-big{color:var(--warn);font-weight:600}
  .dt-loading{color:var(--muted);animation:dtPulse 1.4s ease-in-out infinite}
  @keyframes dtPulse{0%,100%{opacity:.55}50%{opacity:1}}
  .dt-multi-div{height:1px;background:var(--line);margin:5px 8px}
  .dt-multi-quick{display:flex;flex-wrap:wrap;gap:5px;padding:0 2px 6px;margin-bottom:4px;border-bottom:1px solid var(--line-2)}
  .dt-multi-clear,.dt-multi-preset{padding:3px 9px;border:1px solid var(--line);border-radius:999px;background:transparent;color:var(--muted);font-size:11px;cursor:pointer;font-family:inherit;white-space:nowrap}
  .dt-multi-clear:hover,.dt-multi-preset:hover{color:var(--ink);border-color:var(--accent);background:var(--row-hover)}
  .dt-multi-preset{color:var(--accent)}
  .dt-scroll-wrap{position:relative}
  .dt-scroll{overflow:auto;max-height:560px;border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);background:var(--panel)}
  .dt-scroll .tbl{border:0;box-shadow:none;border-radius:0;overflow:visible;border-collapse:separate;border-spacing:0}
  .dt-scroll thead th{position:sticky;top:0;z-index:3;background:var(--panel-2);box-shadow:0 1px 0 var(--line)}
  /* §4.18 totals row — pinned to the bottom the same way the header pins to the top, so a total
     stays visible over a long scrolled table instead of scrolling away with the last row. */
  .dt-scroll tfoot td{position:sticky;bottom:0;z-index:2;background:var(--panel-2);box-shadow:0 -1px 0 var(--line);font-weight:600;padding-top:9px;padding-bottom:9px}
  /* stickyCols (opt-in per table, cfg.stickyCols = N) — pin the N leading columns so the client/name
     (and any lead/oversight kept beside it) stay put when a wide grid is scrolled sideways (e.g.
     Practice setup's client × service matrix). The selection checkbox column, when present, pins flush
     left; each pinned data column pins to its right at --sx-<i> (cumulative width measured post-render
     in dtSyncScrollWidths). Opaque backgrounds so scrolled cells pass behind, not through; the header
     corner sits above both the sticky header (z-index 3) and the pinned body cells; one divider shadow
     marks the frozen block's right edge. Gated on .dt-sticky1 so no other table is touched. */
  .dt-sticky1 .dt-stick-sel{position:sticky;left:0;z-index:1;background:var(--panel)}
  .dt-sticky1 .dt-stick-col{position:sticky;z-index:1;background:var(--panel)}
  .dt-sticky1 .dt-sc-0{left:var(--sx-0,0)}
  .dt-sticky1 .dt-sc-1{left:var(--sx-1,0)}
  .dt-sticky1 .dt-sc-2{left:var(--sx-2,0)}
  .dt-sticky1 .dt-sc-3{left:var(--sx-3,0)}
  .dt-sticky1 .dt-sc-4{left:var(--sx-4,0)}
  .dt-sticky1 .dt-sc-5{left:var(--sx-5,0)}
  .dt-sticky1 .dt-stick-edge{box-shadow:2px 0 5px -2px rgba(0,0,0,.18)}
  .dt-sticky1 tbody tr:hover .dt-stick-sel,.dt-sticky1 tbody tr:hover .dt-stick-col{background:var(--panel-2)}
  .dt-sticky1 thead .dt-stick-sel,.dt-sticky1 thead .dt-stick-col{z-index:5;background:var(--panel-2)}
  /* The native vertical scrollbar of a sticky-header box always spans the FULL box (header included),
     which reads as a glitch: the thumb implies you've scrolled past the header when it's still
     pinned in view. So in Chromium we hide the native vertical scrollbar and draw our own thumb
     that's explicitly positioned to start below the header (top offset measured in JS — see
     dtSyncVScroll) — the real .dt-scroll element still does the actual scrolling (wheel/touch/
     keyboard all keep working); this is purely a visual + drag-to-scroll affordance on top of it.
     Firefox has no per-axis scrollbar-width control, so it keeps its native scrollbar as-is there. */
  .dt-scroll::-webkit-scrollbar{width:0;height:10px}
  .dt-scroll::-webkit-scrollbar-track{background:transparent}
  .dt-scroll::-webkit-scrollbar-thumb{background:var(--faint);border-radius:5px}
  .dt-scroll::-webkit-scrollbar-thumb:hover{background:var(--muted)}
  .dt-vtrack{position:absolute;top:0;right:2px;bottom:2px;width:6px;pointer-events:none}
  .dt-vthumb{position:absolute;left:0;right:0;border-radius:3px;background:var(--faint);opacity:.55;pointer-events:auto;cursor:pointer}
  .dt-vthumb:hover,.dt-vthumb.dragging{opacity:.9}
  .qb-hint{font-size:12px;color:var(--muted);margin-bottom:10px}
  .qb-rows{display:flex;flex-direction:column;gap:4px}
  .qb-row{display:grid;grid-template-columns:1.2fr 1fr 1.2fr auto;gap:7px;align-items:center}
  .qb-row .f{min-width:0}.qb-row .qb-del{padding:6px 11px;color:var(--muted)}
  .qb-conn{padding:1px 0}.qb-conn .f{width:auto;padding:3px 12px;font-size:11px;font-weight:800;letter-spacing:.04em;color:var(--accent-ink);background:var(--accent-soft);border-color:transparent}
  .dt-conn{font-size:10px;font-weight:800;letter-spacing:.05em;color:var(--muted);padding:0 3px}
  .am-modal.inv-modal{width:min(890px,96vw);max-height:93vh}
  .inv-body{padding:20px;background:var(--panel-2);overflow-x:auto}
  .inv-zoom-wrap{overflow:hidden;margin:0 auto}
  /* The whole A4 page stack scales as one (see invPaginate) — scaling each sheet separately
     would leave the un-scaled gaps between them. */
  .inv-zoom-wrap .inv-pages{transform-origin:top left}
  .inv-pay-bar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:10px 18px;border-bottom:1px solid var(--line);background:var(--panel-2)}
  .inv-pay-bar .note{font-size:11.5px}
  .am-modal.drill-modal{width:min(1100px,97vw);max-height:90vh}
  .drill-modal .drill-body{padding:16px 18px}
  /* Email reader popover (js/mail.js mailOpenReal) — reuses mailReadingHtml/mailComposeHtml as-is
     (built for the wide 3-pane Emails tab column), so just widen the modal and undo the couple of
     flex/height assumptions those carry from living inside .mail-shell normally. */
  .am-modal.mail-reader-modal{width:min(820px,94vw);max-height:88vh}
  .mail-reader-modal .am-h .btn{margin-left:auto;flex:none}
  .mail-reader-modal .am-h .am-x{margin-left:8px}
  .mail-reader-modal .am-body{display:block;padding:0}
  .mail-reader-modal .mail-reading{overflow:visible}
  /* The follow-up assignee picker must let its native select menu escape the modal's scroll box. */
  .am-modal.mna-modal{overflow:visible}
  .am-modal.mna-modal .mna-body{overflow:visible;position:relative}
  .am-modal.mna-modal #mnaOwner{position:relative;z-index:3}
  .mar-modal{width:min(700px,94vw);max-height:90vh}
  .mar-modal h4{margin:4px 0 0;font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
  .mar-intro{margin:0;color:var(--muted);font-size:12px;line-height:1.45}
  .mar-rules{display:grid;gap:4px;max-height:260px;overflow:auto}
  .mar-rule{display:flex;align-items:flex-start;gap:8px;padding:7px 8px;border:1px solid var(--line-2);border-radius:7px;cursor:pointer}
  .mar-rule:hover{background:var(--panel-2)}.mar-rule small{display:block;color:var(--faint);font-size:10.5px;margin-top:2px}
  .mar-customs{display:grid;gap:4px}.mar-custom{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 8px;border:1px solid var(--line-2);border-radius:7px;font-size:11.5px}.mar-custom .btn{padding:3px 8px;font-size:10.5px}.mar-empty{color:var(--faint);font-size:11.5px}
  .mar-add{display:grid;grid-template-columns:1.5fr 1fr 1.2fr auto;gap:6px;align-items:center}.mar-add .btn{white-space:nowrap}
  .aq-rules-page{padding:6px 0 34px;max-width:1100px;margin:0 auto}
  .aq-rules-head{display:flex;justify-content:space-between;align-items:flex-start;gap:24px;padding:8px 0 22px;border-bottom:1px solid var(--line)}
  .aq-rules-head h2{margin:3px 0 5px;font-size:24px}.aq-rules-head p{margin:0;max-width:720px;color:var(--muted);font-size:12.5px;line-height:1.5}.aq-rules-head .btn{flex:none;margin-top:4px}
  .aq-rule-section{padding:22px 0;border-bottom:1px solid var(--line-2)}.aq-rule-section-head{display:flex;justify-content:space-between;gap:20px;margin-bottom:12px}.aq-rule-section h3{margin:0 0 4px;font-size:15px}.aq-rule-section-head p{margin:0;color:var(--muted);font-size:12px}
  .aq-rule-list{display:grid;gap:7px}.aq-rule-row{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:12px 14px;background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow)}.aq-rule-row.custom{box-shadow:none}.aq-rule-copy{display:flex;flex-direction:column;gap:4px;min-width:0}.aq-rule-copy strong{font-size:13px}.aq-rule-copy span{font-size:11.5px;color:var(--muted)}.aq-rule-switch{display:flex;align-items:center;gap:7px;flex:none;font-size:11.5px;color:var(--muted);cursor:pointer}.aq-rule-switch input{accent-color:var(--accent)}.aq-rule-actions{display:flex;gap:6px;flex:none}.aq-rule-actions .btn{padding:4px 9px;font-size:11px}.aq-rule-setting{max-width:760px}.aq-rule-setting>label{display:grid;gap:6px;font-size:11.5px;color:var(--muted)}.aq-rule-add{display:grid;grid-template-columns:1.5fr 1fr 1.2fr auto;gap:8px;margin-top:12px;max-width:900px}.aq-rule-add .btn{white-space:nowrap}.aq-rule-empty{padding:12px 14px;color:var(--faint);font-size:12px;border:1px dashed var(--line)}.aq-rule-setting .mail-pick{display:block;max-width:680px}.aq-rule-setting .mail-pick>input{width:100%;max-width:none;font-size:12px}.aq-rule-setting .mail-pick .mail-pick-dd{position:static;left:auto;right:auto;top:auto;width:100%;min-width:0;max-width:none;max-height:240px;margin-top:6px;z-index:40}.aq-rule-setting .mail-pick .mail-ac-i{min-height:34px;padding:8px 11px}.aq-rule-setting .mail-pick .mail-ac-i input{width:auto;min-width:0;margin:0 8px 0 0}.aq-custom-form,[data-aq-custom-form]{display:grid;grid-template-columns:1fr;gap:12px;margin-top:14px;max-width:1000px;padding:16px;background:var(--panel-2);border:1px solid var(--line);border-radius:10px}.aq-custom-form-title{font-size:13px;font-weight:700}.aq-custom-form-fields{display:grid;grid-template-columns:minmax(0,1fr) minmax(180px,280px);gap:8px}@media(max-width:800px){.aq-custom-form-fields{grid-template-columns:1fr}}
  .aq-rule-copy small{display:block;color:var(--faint);font-size:11px;line-height:1.4}.aq-rule-editor{display:none;grid-template-columns:1fr;gap:7px;align-items:end;margin-top:9px;padding:10px;background:var(--panel-2);border-radius:7px}.aq-rule-editor.on{display:grid}.aq-rule-editor label{display:grid;gap:4px;font-size:10.5px;color:var(--muted)}.aq-rule-editor .f{min-width:0}.aq-rule-editor .btn{white-space:nowrap}
  .aq-rule-setting,.aq-rule-add,[data-aq-custom-form]{max-width:none;width:100%}.aq-rule-outcomes{display:grid;gap:7px;padding-top:4px}.aq-rule-outcomes-head{display:flex;justify-content:space-between;gap:12px;align-items:baseline}.aq-rule-outcomes-head span{font-size:11px;color:var(--muted)}.aq-rule-outcome-row{display:grid;grid-template-columns:150px minmax(0,1fr);gap:10px;align-items:center;padding:8px 10px;background:var(--panel);border:1px solid var(--line-2);border-radius:8px}.aq-rule-outcome-row>span{font-size:12px;font-weight:700;color:var(--ink)}.aq-rule-outcome-select{width:100%;min-width:0}@media(max-width:700px){.aq-rule-outcomes-head{display:grid}.aq-rule-outcome-row{grid-template-columns:1fr}}
  .aq-criteria-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px}.aq-criteria-head span{font-size:11px;color:var(--muted)}.aq-criteria{display:grid;gap:6px}.aq-criterion{display:grid;grid-template-columns:78px minmax(130px,1fr) minmax(130px,1fr) minmax(180px,1.4fr) auto;gap:6px;align-items:center}.aq-criterion-join{font-weight:650}.aq-criterion-join-empty{width:78px;height:1px}.aq-criterion .btn{padding:5px 8px}.aq-rule-current{padding:7px 8px;border-left:3px solid var(--accent);background:var(--panel);font-size:11px;color:var(--muted);line-height:1.4}.aq-rule-editor-actions{display:flex;align-items:center;justify-content:flex-start;gap:8px}.aq-rule-editor-actions .btn{width:auto;flex:none;justify-self:start}
  /* The rules page (js/mail-rules-page.js, 2026-10-12) */
  .rp-head-acts{display:flex;gap:8px;flex:none;margin-top:4px}
  .rp-how{margin:14px 0 16px;padding:12px 14px;border-left:3px solid var(--accent);background:var(--panel-2);border-radius:0 8px 8px 0;font-size:12.5px;line-height:1.55;color:var(--muted)}.rp-how b{color:var(--ink)}
  .rp-list{display:grid;grid-template-columns:minmax(0,1fr);gap:7px}
  .rp-row{min-width:0;background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow)}.rp-row.off .rp-row-copy{opacity:.55}.rp-row.open{border-color:var(--accent)}
  .rp-row-h{display:flex;align-items:center;gap:12px;padding:11px 14px;cursor:pointer}.rp-row-h:hover{background:var(--panel-2);border-radius:10px}
  .rp-n{font-family:var(--font-num);font-size:12px;font-weight:700;color:var(--muted);min-width:22px;text-align:right}
  .rp-switch{position:relative;flex:none;width:30px;height:17px;cursor:pointer}.rp-switch input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}.rp-switch span{position:absolute;inset:0;border-radius:99px;background:var(--line);transition:background .15s}.rp-switch span:after{content:"";position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;background:#fff;transition:left .15s}.rp-switch input:checked+span{background:var(--accent)}.rp-switch input:checked+span:after{left:15px}
  .rp-row-copy{flex:1;min-width:0}.rp-row-l1{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px}.rp-row-sum{font-size:11.5px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .rp-verb{font-size:11px;font-weight:600;border-radius:99px;padding:1px 8px;background:var(--info-soft);color:var(--info)}.rp-verb.v-no-action{background:var(--line-2);color:var(--muted)}.rp-verb.v-follow-up,.rp-verb.v-reply{background:var(--warm-soft);color:var(--warm)}.rp-verb.none{background:transparent;border:1px dashed var(--line);color:var(--muted);font-weight:500}
  .rp-badge{font-size:10.5px;border-radius:99px;padding:1px 7px;border:1px solid var(--line);color:var(--muted)}.rp-badge.mine{border-color:transparent;background:var(--accent-soft);color:var(--accent-ink)}.rp-badge.n{font-family:var(--font-num)}.rp-badge.o{border-color:transparent;background:var(--warm-soft);color:var(--warm)}
  .rp-run{flex:none;padding:4px 9px;font-size:11px}.rp-chev{color:var(--faint);font-size:12px}
  .rp-body{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);gap:0 24px;padding:4px 16px 16px 48px;border-top:1px solid var(--line-2)}@media(max-width:980px){.rp-body{grid-template-columns:1fr;padding-left:16px}}
  .rp-col h4{margin:14px 0 6px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
  .rp-steps{margin:0;padding:0 0 0 22px;font-size:12.5px;line-height:1.5}.rp-steps li{margin:3px 0}.rp-steps small{display:block;color:var(--muted);font-size:11px}.rp-join{display:inline-block;min-width:28px;margin-right:4px;font-size:10.5px;font-weight:700;text-transform:uppercase;color:var(--accent-ink)}
  .rp-grouping{margin-top:6px;font-size:11.5px;color:var(--muted)}
  .rp-then{margin:0;padding-left:18px;font-size:12.5px;line-height:1.5}.rp-then small{color:var(--muted);font-size:11px}
  .rp-fine,.rp-muted{font-size:11px;color:var(--faint)}.rp-fine{margin-top:4px}
  .rp-note{margin:0;font-size:12.5px;line-height:1.5;color:var(--ink)}
  .rp-place{font-size:12px;line-height:1.5;color:var(--muted)}
  .rp-overlaps{margin:0;padding-left:18px;font-size:12px;line-height:1.5}.rp-overlaps li{margin:3px 0}
  .rp-acts{display:flex;gap:8px;margin-top:16px}
  .rp-try{border-left:1px solid var(--line-2);padding-left:20px}@media(max-width:980px){.rp-try{border-left:none;padding-left:0}}
  .rp-try-h{font-size:12.5px}.rp-try-h small{display:block;color:var(--faint);font-size:11px}
  .rp-examples{display:grid;gap:4px;margin:8px 0}.rp-ex{display:flex;flex-direction:column;align-items:flex-start;gap:1px;text-align:left;padding:6px 9px;border:1px solid var(--line);border-radius:7px;background:var(--panel);cursor:pointer;font:inherit;color:var(--ink)}.rp-ex:hover{background:var(--panel-2)}.rp-ex.on{border-color:var(--accent);background:var(--accent-soft)}.rp-ex-s{font-size:12.5px;font-weight:600}.rp-ex-w{font-size:11px;color:var(--muted)}
  .rp-explain{margin:8px 0;padding:10px 12px;border:1px solid var(--line-2);border-radius:8px;background:var(--panel-2);font-size:12px;line-height:1.5}.rp-explain-h{margin-bottom:6px}.rp-veto{display:block;color:var(--warm);font-weight:600}
  .rp-matches{margin:4px 0;padding-left:18px}.rp-matches li{margin:3px 0}.rp-matches li small{display:block;color:var(--muted);font-size:11px}.rp-matches li.win>b{color:var(--good)}.rp-matches li.veto>b{color:var(--warm)}.rp-matches li.lose{color:var(--muted)}.rp-matches em{font-style:normal;color:var(--accent-ink);font-size:11px}
  .rp-would{margin-top:6px}.rp-checks{margin:4px 0 0;padding-left:18px}.rp-checks li{margin:2px 0}
  .rp-try-acts{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:6px 0 10px}.rp-ran{font-size:11.5px;color:var(--good);font-weight:600}
  .rp-sample{margin-top:12px;padding-top:10px;border-top:1px dashed var(--line)}.rp-sample-h{font-size:12.5px}.rp-sample-h small{display:block;color:var(--faint);font-size:11px}.rp-sample-f{display:grid;grid-template-columns:1fr;gap:5px;margin-top:6px}.rp-sample-f .btn{justify-self:start}
  .rp-editor{padding:10px 16px 16px 48px;border-top:1px solid var(--line-2);display:grid;gap:10px}@media(max-width:980px){.rp-editor{padding-left:16px}}
  .rp-ed-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,280px);gap:10px}@media(max-width:800px){.rp-ed-row{grid-template-columns:1fr}}.rp-ed-row label{display:grid;gap:4px;font-size:11px;color:var(--muted)}
  .rp-ed-h{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-top:4px}.rp-ed-h span{font-size:11px;color:var(--muted)}
  .rp-crits{display:grid;gap:6px}.rp-crit{display:grid;grid-template-columns:64px minmax(130px,.8fr) minmax(130px,.9fr) minmax(180px,1.6fr) auto;gap:6px;align-items:center}.rp-crit-first{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--accent-ink);text-align:right;padding-right:4px}.rp-crit-join{font-weight:650}
  .rp-crit .rp-sig-only{display:none}.rp-crit.sig .rp-sig-only{display:block}.rp-crit.sig .rp-text-only{display:none}
  @media(max-width:900px){.rp-crit{grid-template-columns:64px 1fr 1fr}.rp-crit [data-rp-crit-value],.rp-crit [data-rp-crit-signal]{grid-column:2 / -1}.rp-crit [data-rp-crit-remove]{grid-column:1 / -1;justify-self:start}}
  .rp-ed-acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}.rp-danger{margin-left:auto;color:var(--crit)}
  .rp-removed{margin-top:14px;padding:12px 14px;border:1px dashed var(--line);border-radius:10px;display:grid;gap:6px;font-size:12px}.rp-removed>span{color:var(--muted)}.rp-removed-row{display:flex;align-items:center;justify-content:space-between;gap:10px}.rp-removed-row .btn{padding:3px 9px;font-size:11px}
  .rp-new{border-color:var(--accent)}.rp-new .rp-row-h{cursor:default}
  @media(max-width:900px){.aq-criterion{grid-template-columns:78px 1fr 1fr}.aq-criterion [data-aq-criterion-value]{grid-column:2 / -1}.aq-criterion [data-aq-criterion-remove]{grid-column:1 / -1;justify-self:start}}
  /* THE MODAL COMPOSER SCROLLS LIKE THE PANE'S (2026-09-17). Reported of a long AI-drafted reply in
     the reader window: "I no longer can see the bottom bits of my draft in the composer — I have no
     vertical scroller either."

     The card had no cap here (max-height:none, flex:0 0 auto), so it grew to the height of the draft
     and .mail-compose-fields — its own scroll region — never had a constraint to scroll against. The
     modal's own overflow was the only scroller, which puts the footer below the draft rather than
     under it, and reads as no scrollbar at all on a long one.

     So the card is bounded here too, and the shrink lands where it does everywhere else: in the
     fields, which scroll, with Send/Save/Cancel pinned under them. The height is the modal's own cap
     less its header and the card's margins — the modal is 88vh (.am-modal.mail-reader-modal). */
  .mail-reader-modal .mail-compose{margin:14px;flex:0 1 auto;min-height:0;max-height:calc(88vh - 104px)}
  .mail-reader-modal .mail-compose-fields{flex:1 1 auto;min-height:0;overflow-y:auto}
  /* Nothing between the card and the modal may clip or scroll instead of it. */
  .mail-reader-modal .am-body{min-height:0}
  /* Triage's grouped queue (js/mail-action-queue.js). Cards grouped by WHO IS OWED, sized by what is
     worth doing, over one detail area that does not move. Size is meaning here, not decoration:
     equal cards would say all nine groups matter equally, which is false. */
  /* ONE ROW. Three stacked blocks spent most of a screen on nine numbers before anything had been
     chosen. The families survive as a label and a divider rather than a block each — owed-to-a-person
     before owed-to-the-data is the point of the page — and the captions moved into tooltips, since a
     caption explains a group you have NOT picked, and the one you have still shows its full sentence
     in the detail header below. */
  /* WRAPS, never scrolls. The first cut scrolled horizontally and clipped "No job" off the right at
     1280 — and the real page gives up another 200px to the nav. A horizontal scrollbar on the
     primary navigation of a surface is worse than a second line: it hides whole groups behind a
     gesture, and the group you cannot see is the one you stop working. */
  /* EMAIL INSIGHTS: cards grouped by what a figure is ABOUT, not one figure per box. Two wide, two
     sharing a row — the shapes are what let you find the same card again next month without reading
     the headings. */
  .mi-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
  .mi-card{border:1px solid var(--line);border-radius:var(--r-md);background:var(--panel);overflow:hidden}
  .mi-card.wide{grid-column:1 / -1}
  .mi-card-h{display:flex;align-items:center;gap:8px;padding:11px 14px;border-bottom:1px solid var(--line-2)}
  .mi-card-h h3{margin:0;font-size:12.5px;font-weight:650}
  .mi-card-q{margin-left:auto;font-size:11px;color:var(--faint)}
  .mi-ic{width:15px;height:15px;flex:none;opacity:.75}
  .mi-card-body{padding:13px 14px;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
  .mi-card-body.flush{padding:0;display:block}
  /* The headline number of the card that has one. */
  .mi-hero{font-family:var(--font-num);font-size:38px;font-weight:700;line-height:1;display:flex;
    align-items:baseline;gap:8px;flex:none}
  .mi-hero span{font-family:var(--font);font-size:11px;font-weight:500;color:var(--faint)}
  .mi-hero.good{color:var(--good)}.mi-hero.warn{color:var(--warn)}.mi-hero.crit{color:var(--crit)}
  .mi-stats{display:flex;gap:16px;flex-wrap:wrap;flex:1;min-width:0}
  .mi-stat{display:flex;flex-direction:column;gap:1px;min-width:96px;text-align:left;
    border:0;background:none;padding:0;font-family:inherit;color:var(--ink)}
  .mi-stat-n{font-family:var(--font-num);font-size:19px;font-weight:700;line-height:1.15}
  .mi-stat-l{font-size:12px;font-weight:500}
  .mi-stat-f{font-size:10.5px;color:var(--faint)}
  .mi-stat.good .mi-stat-n{color:var(--good)}
  .mi-stat.warn .mi-stat-n{color:var(--warn)}
  /* A stat you can click is a doorway to Triage — underlined on hover so it reads as one. */
  button.mi-stat{cursor:pointer}
  button.mi-stat:hover .mi-stat-l{text-decoration:underline}
  button.mi-stat:hover .mi-stat-n{color:var(--accent)}
  .mi-card .mi-roles{flex:1 0 100%;margin-top:2px}
  @media (max-width:900px){ .mi-cards{grid-template-columns:minmax(0,1fr)} }
  .aq-fams{display:flex;align-items:stretch;flex-wrap:wrap;gap:0 0;row-gap:12px;margin-bottom:16px}
  .aq-fam{display:flex;flex-direction:column;gap:5px;padding:0 11px;
    border-left:1px solid var(--line-2)}
  .aq-fam:first-child{padding-left:0;border-left:0}
  .aq-fam-tiles{flex-wrap:wrap}
  .aq-fam-lab{font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
    color:var(--faint);cursor:default}
  .aq-fam-tiles{display:flex;gap:6px}

  /* Icon, count, label on one line. The count leads because it is what the eye is scanning for. */
  .aq-card{display:flex;align-items:center;gap:7px;cursor:pointer;white-space:nowrap;
    border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel);
    padding:7px 11px;font-family:inherit;font-size:12px;color:var(--ink);
    transition:border-color .12s,box-shadow .12s}
  .aq-card:hover{border-color:var(--accent)}
  .aq-card.on{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}
  .aq-card-n{font-family:var(--font-num);font-weight:700;font-size:16px;line-height:1}
  .aq-card-t{font-weight:500}
  /* One size and one style for every tile (2026-09-15) — only the number's colour differs. Size used to
     carry meaning (a big Quick clear / Triage ready, a small dashed watch list); asked "why is assigned
     by me in a different format… and triage ready oversized?", it read as inconsistency, not meaning. */
  .aq-card.warn .aq-card-n{color:var(--warn)}
  .aq-card.info .aq-card-n{color:var(--info)}
  .aq-card.good .aq-card-n{color:var(--ok,var(--accent))}
  .aq-ic{width:14px;height:14px;flex:none;opacity:.8}
  .card-h h3 .aq-ic{margin-right:7px;vertical-align:-3px}
  .aq-detail.empty{padding:34px;text-align:center;color:var(--faint);font-size:12.5px;
    border:1px dashed var(--line);border-radius:var(--r-md)}
  /* The headline over a big filing group: how much of it can be answered without reading anything.
     Accent, because it is the difference between a task and a wall. */
  .aq-banner{margin:0 15px 10px;padding:10px 13px;border-radius:var(--r-sm);font-size:12.5px;
    background:var(--accent-soft);color:var(--accent-ink);border:1px solid var(--accent);
    display:flex;align-items:center;gap:12px}
  .aq-banner>div{flex:1}
  .aq-banner .btn{flex:none;white-space:nowrap}
  .aq-banner.quiet{background:var(--line-2);color:var(--muted);border-color:var(--line)}
  .aq-banner b{font-family:var(--font-num);font-size:15px}
  /* A suggestion wears its confidence. The two deductive ones are solid; the guess is dotted, which
     is the visual form of "this one still needs you to look". */
  /* The chip IS the button — one click attaches that job. It has to look pressable, or it reads as
     a label and the only route left is the checkbox and the bulk bar. */
  /* ---- ONE TABLE FOR EVERY GROUP (2026-09-10) ----
     The columns are the five triage questions, so a cell has to hold both an ANSWER (chips) and the
     way to give one (small buttons, two real selects) without either turning the row into a wall.
     Everything here is sized down from the app's controls rather than restyled: a cell control is
     the same thing as a bar control, met in less space. */
  .aq-em{display:flex;flex-direction:column;gap:2px;min-width:260px;max-width:460px}
  .aq-em-top{display:flex;align-items:baseline;gap:7px;flex-wrap:wrap}
  .aq-em-when{color:var(--faint);font-size:11px;font-family:var(--font-num);white-space:nowrap}
  .aq-em-subj{color:var(--ink)}
  /* The opening line of the message. Clamped rather than cut mid-word: two lines is enough to say
     what an email is about, and a third starts competing with the columns beside it. */
  .aq-em-snip{color:var(--muted);font-size:12px;line-height:1.35;display:-webkit-box;
    -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .aq-chip{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:1px 8px;
    font-size:11px;font-weight:600;background:var(--bb-soft);color:var(--bb);border:1px solid transparent;
    font-family:inherit;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  button.aq-chip{cursor:pointer}
  button.aq-chip:hover{border-color:var(--accent)}
  .aq-chip.warn{background:var(--warn-soft);color:var(--warn)}
  .aq-chip.info{background:var(--info-soft);color:var(--info)}
  .aq-chip.good{background:var(--good-soft);color:var(--good)}
  /* THE ANSWER, IN THE READING PANE'S OWN PILL (2026-09-15): "should we stick colours and design for
     these pills same as we have in emails triage, and indicate by way of green/orange borders on top
     only?" It was a solid green fill here and a soft one in the pane. Now the table draws what the
     triage bar draws (.tag.mtb-decided; dashed while it is Insight's ✦), and whether a cell is
     answered is said once — by the bar across its top. */
  .aq-chip.picked{background:var(--good-soft);color:var(--good);border:0;border-left:3px solid var(--good);border-radius:6px;font-weight:600}
  .aq-chip.picked.auto{border:1px dashed color-mix(in srgb,var(--good) 55%,transparent)}
  /* The undo on a picked answer is always visible, faintly — it is the only way back, so it must be
     findable without already knowing it is there. */
  .aq-chip.picked .aq-x{opacity:.6}
  .aq-chip.picked .aq-x:hover{opacity:1;background:rgba(0,0,0,.08)}
  /* Next step and Assigned, once answered: the pane's .mns-set pill, green where the cell is answered
     and orange where it is not — as .mtb-row.ready / .attention colour it on the triage bar. */
  .aq-cell .mns-set{font-size:11px;padding:1px 8px}
  .aq-cell[data-aq-st="ok"] .mns-set{color:var(--good);background:var(--good-soft)}
  .aq-cell[data-aq-st="bad"] .mns-set{color:var(--warn);background:var(--warn-soft)}
  .aq-cell .mns-set[data-aq-noaction],.aq-cell .mns-set[data-aq-confirmauto]{cursor:pointer}
  .mns-set .aq-x{border:0;background:transparent;color:inherit;font-family:inherit;font-size:9.5px;line-height:1;
    padding:2px 3px;border-radius:999px;opacity:.6;cursor:pointer}
  .mns-set .aq-x:hover{opacity:1;background:rgba(0,0,0,.08)}
  .aq-note{flex-basis:100%;font-size:10.5px;color:var(--faint);line-height:1.35}
  /* The people on the conversation — under the sender in Triage, under the subject when reading. */
  .aq-em-people,.mail-thread-people{display:flex;flex-wrap:wrap;align-items:center;gap:4px}
  .aq-em-people{margin:1px 0 2px}
  .mail-thread-people{padding:6px 16px 0;font-size:12px}
  .mtp-lab{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--faint);margin-right:4px}
  .mtp-chip{font-size:11px;padding:1px 7px;border-radius:999px;background:var(--panel-2);border:1px solid var(--line-2);
    color:var(--muted);cursor:copy;white-space:nowrap}
  .mtp-chip:not(.plain):hover{border-color:var(--accent);color:var(--ink)}
  /* "In this thread": a contact opens their drawer; anyone else is just a name, and does not react. */
  .mtp-chip.is-contact{cursor:pointer}
  .mtp-chip.plain{cursor:default}
  /* Between the people outside the firm and the colleagues on the same thread (mailPartyGroupsHtml). */
  .mail-party-sep{display:inline-block;width:1px;height:14px;background:var(--line);margin:0 6px;vertical-align:middle;align-self:center}
  .aq-chip.mute{background:var(--bb-soft);color:var(--bb)}
  .aq-cell{display:flex;flex-wrap:wrap;align-items:center;gap:5px;min-width:130px}
  /* The ✕ on a chip: present, and quiet until the pointer is on the chip it belongs to. */
  .aq-chip .aq-x{margin:0 -3px 0 4px;border:0;background:transparent;color:inherit;font-family:inherit;
    font-size:9.5px;line-height:1;padding:2px 3px;border-radius:999px;opacity:0;cursor:pointer;transition:opacity .12s}
  .aq-chip:hover .aq-x,.aq-chip .aq-x:focus-visible{opacity:.75}
  .aq-chip .aq-x:hover{opacity:1;background:rgba(0,0,0,.08)}
  /* Column one is the way into the email; the rest of the row is controls. */
  .aq-em[data-aq-openrow]{cursor:pointer}
  .aq-em[data-aq-openrow]:hover .aq-em-subj{text-decoration:underline}
  .aq-pick-list{margin-top:10px;max-height:46vh;overflow:auto;display:flex;flex-direction:column;gap:2px}
  .aq-pick-row{display:flex;align-items:center;gap:8px;width:100%;text-align:left;font-family:inherit;
    font-size:12.5px;padding:7px 9px;border:1px solid transparent;border-radius:var(--r-sm);
    background:transparent;color:var(--ink);cursor:pointer}
  /* The last row when nothing matched: an action, not a candidate. */
  .aq-pick-row.make{color:var(--accent-ink);font-weight:600}
  .aq-pick-row:hover{background:var(--panel-2);border-color:var(--line)}
  .aq-pick-row .aq-chip{margin-left:auto}
  /* The picker's two ticks — include archived/closed, and other clients' jobs (aqPickOpen). */
  .aq-pick-opts{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:8px;font-size:12px;color:var(--muted)}
  .aq-pick-opts:empty{display:none}
  .aq-pick-opts label{display:flex;align-items:center;gap:5px;cursor:pointer}
  /* A recorded answer's pill reopens its reason on click (mailDecidedPill / aqPickedHtml, 2026-09-15). */
  .mtb-decided[data-mail-noassign],.mtb-decided[data-mail-noclient],.mtb-decided[data-mail-nocontact],
  .mtb-decided[data-mail-attn-nojob],.mns-set[data-mail-nextstep],.aq-chip.picked.changeable{cursor:pointer}
  .mtb-decided[data-mail-noassign]:hover,.mtb-decided[data-mail-noclient]:hover,.mtb-decided[data-mail-nocontact]:hover,
  .mtb-decided[data-mail-attn-nojob]:hover,.mns-set[data-mail-nextstep]:hover,.aq-chip.picked.changeable:hover{box-shadow:0 0 0 1px var(--accent)}
  .aq-none{color:var(--faint);font-size:12px}
  /* Offered, not shouted: an action in a cell should be reachable without drawing the eye away from
     what the row SAYS. It firms up on hover, where the intent to use it already exists. */
  /* The reading pane's button (.mns-btn), so an action reads the same in both places (2026-09-15). */
  .aq-mini{font-size:11px;font-weight:600;padding:3px 9px;border-radius:99px;cursor:pointer;font-family:inherit;
    background:var(--panel);color:var(--muted);border:1px solid var(--line);white-space:nowrap}
  .aq-mini:hover{border-color:var(--accent);color:var(--accent-ink)}
  /* The pane's filing pills in a Triage cell (aqChipsHtml): long names shorten inside the column. */
  .aq-cell .tag{display:inline-flex;align-items:center;gap:3px;max-width:100%;min-width:0;font-size:11px;padding:2px 8px}
  .aq-cell .tag .aq-tag-l{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
  .aq-cell button.tag{font-family:inherit}
  .tag .aq-x{margin:0 -3px 0 2px;border:0;background:transparent;color:inherit;font-family:inherit;font-size:9.5px;
    line-height:1;padding:2px 3px;border-radius:999px;opacity:.6;cursor:pointer;flex:none}
  .tag .aq-x:hover{opacity:1;background:rgba(0,0,0,.08)}
  .aq-mini.good:hover{border-color:var(--good);color:var(--good)}
  .aq-pick{display:flex;align-items:center;gap:5px;width:100%;font-size:12px}
  .aq-pick>span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .aq-sel{font-family:inherit;font-size:12px;color:var(--ink);background:var(--panel);
    border:1px solid var(--line);border-radius:var(--r-sm);padding:3px 6px;max-width:100%}
  .aq-sel:hover{border-color:var(--accent)}
  .aq-sel.unset{color:var(--faint);border-style:dashed}
  .aq-done{display:flex;flex-direction:column;gap:6px;min-width:120px}
  /* ---- THE GRID (2026-09-10): every cell bordered, and a mark on its top edge that says whether that
     cell is answered — green fine, orange lacking. The colour comes from data-aq-st on the cell's own
     wrapper (js/mail-action-queue.js aqCellOpen), read here through :has, because dataTable has no
     per-cell class hook and the answer belongs to the cell, not the row.

     The mark is the EMAILS PAGE's (2026-09-11): a short solid bar at the top-left, the same 3px
     .mail-row::before draws on each email in the list — "rather than this dashed styling, check the
     emails page… switch to that sort of border style." A full-width dashed edge on every cell of every
     row read as a fence; one short bar per cell reads as a status, and it is now the same mark in both
     places for the same fact. Drawn as ::before, not a border, so it takes no space and rows stay level
     without reserving an invisible 3px.

     position:relative gives the bar its anchor — but NOT on a pinned column: `.dt-stick-col` is
     position:sticky (already an anchor for ::before), and this selector out-ranks it, so setting
     relative there would quietly unpin the column. */
  /* Laid out FIXED (2026-09-15): the Email column has its width (AQ_EMAIL_COL_W) and every column
     without one shares what is left equally — the five questions get the room. The floor keeps them
     readable on a narrow screen, where the table scrolls sideways instead of squeezing. */
  .dt-scroll[data-dt-scroll^="aq_"] .tbl{table-layout:fixed;width:100%;min-width:1180px}
  /* Done, under the email's preview: score and actions on one line. */
  .aq-em-wrap .aq-done{flex-direction:row;flex-wrap:wrap;align-items:center;gap:6px 12px;margin-top:8px;min-width:0}
  .dt-scroll[data-dt-scroll^="aq_"] .tbl td{border-right:1px solid var(--line-2);border-bottom:1px solid var(--line)}
  .dt-scroll[data-dt-scroll^="aq_"] .tbl td:not(.dt-stick-col):not(.dt-stick-sel){position:relative}
  .dt-scroll[data-dt-scroll^="aq_"] .tbl td::before{content:"";position:absolute;left:0;top:0;
    width:clamp(40px,25%,96px);height:3px;background:transparent;pointer-events:none}
  .dt-scroll[data-dt-scroll^="aq_"] .tbl thead th{border-right:1px solid var(--line-2)}
  .dt-scroll[data-dt-scroll^="aq_"] .tbl td:last-child,.dt-scroll[data-dt-scroll^="aq_"] .tbl thead th:last-child{border-right:0}
  .dt-scroll[data-dt-scroll^="aq_"] .tbl td:has(>[data-aq-st="ok"])::before{background:var(--good)}
  .dt-scroll[data-dt-scroll^="aq_"] .tbl td:has(>[data-aq-st="bad"])::before{background:var(--warn)}
  /* The question, in the column that lacks the answer ("Which client?", "No job", "Unknown sender"). */
  .aq-q{flex-basis:100%;font-size:11px;font-weight:600;color:var(--warn)}
  /* A row resolved while you worked stays, tinted, until Hide resolved or you leave the group — the
     tint is on the whole row (via :has, so dataTable needs no row-class hook), and quiet enough that
     the rows still to do are what the eye lands on. */
  tr:has(.aq-em.resolved)>td{background:color-mix(in srgb,var(--good-soft) 55%,transparent)}
  .aq-em.resolved .aq-em-subj,.aq-em.resolved .aq-em-snip{color:var(--muted)}
  .aq-resolved-bar{display:flex;align-items:center;gap:12px;margin:0 15px 10px;padding:8px 12px;
    border-radius:var(--r-sm);background:var(--good-soft);color:var(--ink);font-size:12.5px;
    border:1px solid color-mix(in srgb,var(--good) 25%,transparent)}
  .aq-resolved-bar>span{flex:1}
  .aq-resolved-bar .btn{flex:none;white-space:nowrap}
  .aq-pips{display:flex;align-items:center;gap:3px}
  /* Five marks, one per question. A number alone reads as a rating; the marks say WHICH are left. */
  .aq-score{font-family:var(--font-num);font-variant-numeric:tabular-nums;font-size:11px;color:var(--muted);margin-left:3px}
  .aq-done-acts{display:flex;flex-wrap:wrap;gap:5px}
  .aq-done-acts .btn{font-size:11.5px;padding:3px 9px;white-space:nowrap}
  /* An Actioned control that would be refused is OFF, not merely warned about (2026-09-10). */
  .btn:disabled,.icon-btn:disabled{opacity:.4;cursor:not-allowed}
  .aq-sug{font-size:11.5px;padding:3px 9px;border-radius:999px;white-space:nowrap;cursor:pointer;
    border:1px solid transparent;font-family:inherit;transition:filter .12s,border-color .12s}
  .aq-sug:hover{filter:brightness(.94);border-color:var(--accent)}
  .aq-sug.certain{background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
  .aq-sug.likely{background:var(--line-2);color:var(--ink)}
  .aq-sug.maybe{background:transparent;color:var(--muted);border:1px dashed var(--line)}
  /* A model's answer reads as its own thing — neither a certainty nor a word-overlap guess. Dashed
     like the guess it is, tinted so it is obvious which suggestions came from where. */
  .aq-sug.ai{background:transparent;color:var(--accent-ink);border:1px dashed var(--accent)}
  .aq-airow{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 15px 10px}
  .aq-ainote{font-size:11px;color:var(--faint)}
  .aq-ainote.done{color:var(--good)}
  .aq-ainote.err{color:var(--crit)}
  /* "6 identical · 21 Aug to 2 Sep" beside the subject. The dates are the checkable part — a bare
     count asks you to take it on trust. */
  .aq-dupe{margin-left:8px;font-size:10.5px;padding:1px 7px;border-radius:999px;
    background:var(--line-2);color:var(--muted);white-space:nowrap}
  .aq-acts{display:flex;gap:6px;flex:none;flex-wrap:wrap;justify-content:flex-end}
  .aq-acts .btn{font-size:11.5px;padding:5px 10px}
  /* The Actioned blocker (js/mail.js mailActionedBlockHtml) — what is outstanding, and what answers
     it, one line per flag. */
  .mab-list{display:flex;flex-direction:column;gap:8px}
  .mab-row{display:flex;flex-direction:column;gap:2px;padding:9px 11px;border:1px solid var(--line);border-left:3px solid var(--warn);border-radius:8px;background:var(--panel-2)}
  .mab-row b{font-size:12.5px}
  .mab-row span{font-size:11.5px;color:var(--muted)}
  /* ✦ Auto-triage: the rule picker, the step-through report, and the card a background run leaves
     (js/mail-autotriage-picker.js). The list scrolls inside the dialog; the dialog itself does not. */
  .am-modal.atp-modal{width:min(520px,94vw);max-height:86vh}
  .atp-modal .am-body{display:flex;flex-direction:column;gap:8px;min-height:0}
  .atp-lead{margin:0;font-size:12px;color:var(--muted)}
  .atp-bulk{display:flex;gap:12px;align-items:center}
  .atp-lnk{background:none;border:0;padding:0;font:inherit;font-size:12px;font-weight:600;color:var(--accent-ink);cursor:pointer}
  .atp-lnk.atp-last{margin-left:auto}
  .atp-list{overflow:auto;max-height:52vh;border:1px solid var(--line);border-radius:8px;padding:2px 0 6px}
  .atp-group-h{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--faint);padding:9px 12px 4px}
  .atp-row{display:grid;grid-template-columns:auto 1fr;column-gap:9px;padding:6px 12px;cursor:pointer;align-items:start}
  .atp-row:hover{background:var(--panel-2)}
  .atp-row input{margin-top:2px;grid-row:span 2}
  .atp-name{font-size:12.5px;color:var(--ink)}
  .atp-name em{font-style:normal;font-size:11px;color:var(--faint);margin-left:4px}
  .atp-row.off .atp-name{color:var(--muted)}
  .atp-fills{font-size:11px;color:var(--muted)}
  .am-modal.atr-modal{width:min(560px,94vw);max-height:86vh}
  .atr-sum{font-size:12.5px;color:var(--ink);margin-bottom:10px}
  .atr-empty{font-size:12.5px;color:var(--muted)}
  .atr-mail{border:1px solid var(--line);border-radius:10px;padding:12px 14px;display:flex;flex-direction:column;gap:8px;background:var(--panel-2)}
  .atr-subj{font-weight:650;font-size:13px}
  .atr-meta{font-size:11.5px;color:var(--muted)}
  .atr-changes{display:flex;flex-direction:column;gap:6px}
  .atr-change{display:grid;grid-template-columns:92px 1fr;column-gap:10px;font-size:12.5px}
  .atr-change small{grid-column:2;font-size:11px;color:var(--faint)}
  .atr-k{color:var(--muted);font-weight:600}
  .atr-v s{color:var(--faint)}
  .atr-v b{color:var(--good)}
  .atr-mail .btn{align-self:flex-start}
  .atr-modal .am-foot{justify-content:flex-end;gap:10px;align-items:center}
  .atr-pos{font-size:12px;color:var(--muted);font-family:var(--font-num)}
  .atp-card{position:fixed;right:18px;bottom:18px;z-index:210;display:flex;align-items:center;gap:10px;background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:9px 10px 9px 14px;font-size:12.5px;box-shadow:var(--shadow-lg)}
  .atp-card .atp-card-x{padding:2px 8px}
  /* "Add contact" dialog (js/mail.js mailAttnAddContactOpen) — three modes as a segmented control
     rather than radios, since each one swaps the whole form below it; and a two-up row for the
     first/last name pair, which reads as one field split rather than two unrelated ones. */
  .macm-tabs{display:inline-flex;gap:2px;background:var(--line-2);border-radius:10px;padding:3px;margin-bottom:12px}
  .macm-tabs button{border:0;background:transparent;color:var(--muted);font-weight:600;font-size:12px;padding:6px 11px;border-radius:7px;cursor:pointer}
  .macm-tabs button.on{background:var(--panel);color:var(--ink);box-shadow:var(--shadow)}
  .macm-2col{display:grid;grid-template-columns:1fr 1fr;gap:10px}
  /* "+ New organisation…" under Belongs to — the company made on the spot (mailAddOrgInlineOpen). */
  .macm-neworg{display:flex;align-items:center;gap:8px;margin:-4px 0 10px;font-size:11.5px}
  .macm-neworg .btn{font-size:11.5px;padding:3px 10px}
  .macm-neworg .q{color:var(--faint)}
  .stat.clickable,.tile.clickable,.cov.clickable{cursor:pointer}.stat.clickable:hover,.tile.clickable:hover{border-color:var(--accent)}
  /* Queue "Review bench" — persistent pre-send checks with checking… / clear / review states */
  .review-bench{border:1px solid var(--line);border-radius:12px;background:var(--panel);padding:11px 13px;margin-bottom:14px}
  .rb-head{display:flex;align-items:center;gap:10px;margin-bottom:9px}
  .rb-title{font-weight:800;letter-spacing:.05em;text-transform:uppercase;font-size:10px;color:var(--faint)}
  .rb-h{font-size:12px;display:inline-flex;align-items:center;gap:6px}
  .rb-h-load{color:var(--muted)} .rb-h-warn{color:var(--crit);font-weight:700} .rb-h-ok{color:var(--good);font-weight:700}
  .rb-chips{display:flex;flex-wrap:wrap;gap:8px}
  .rb-chip{display:inline-flex;align-items:center;gap:7px;font-size:12px;padding:7px 11px;border-radius:9px;border:1px solid var(--line);background:var(--panel-2);color:var(--muted);white-space:nowrap;font-weight:600}
  button.rb-chip{cursor:pointer;transition:filter .12s} button.rb-chip:hover{filter:brightness(1.04)}
  .rb-chip.rb-crit{border-color:var(--crit);background:var(--crit-soft);color:var(--crit)}
  .rb-chip.rb-warn{border-color:var(--warn);background:var(--warn-soft);color:var(--warn)}
  .rb-chip.rb-accent{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink)}
  .rb-chip.rb-clear{color:var(--faint);font-weight:500} .rb-chip.rb-clear .rb-check{color:var(--good);font-weight:800}
  .rb-chip.rb-loading{color:var(--muted);font-weight:500;border-style:dashed}
  .rb-chip b{font-weight:800} .rb-cta{font-weight:700;opacity:.75;font-size:11px}
  .rb-state{font-style:italic;opacity:.8}
  .rb-dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
  .rb-spin{width:11px;height:11px;border:2px solid var(--line-2);border-top-color:currentColor;border-radius:50%;display:inline-block;animation:rbspin .7s linear infinite;flex:none}
  @keyframes rbspin{to{transform:rotate(360deg)}}
  /* Karbon-linked badge in drawer subtitles (job + client): green mark + open + refresh */
  .kl{display:inline-flex;align-items:center;gap:4px;margin-left:8px;vertical-align:middle}
  .kl-mark{display:inline-flex;align-items:center;justify-content:center;width:17px;height:17px;border:1.5px solid var(--good);border-radius:5px;color:var(--good);font-size:10px;font-weight:800;line-height:1;background:color-mix(in srgb,var(--good) 10%,transparent)}
  .kl-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:1px solid var(--line);border-radius:5px;background:var(--panel-2);color:var(--muted);cursor:pointer;transition:color .12s,border-color .12s}
  .kl-btn:hover{color:var(--accent-ink);border-color:var(--accent)}
  .kl-btn.kl-spin{color:var(--muted);pointer-events:none}
  .kl-btn.kl-spin svg{animation:rbspin .7s linear infinite}
  .kl-none{color:var(--faint)}
  /* Top-bar Karbon "Sync now" (#ksyncBtn) — the ⟳ spins while a quick sync is in flight. */
  #ksyncBtn.ksync-spin{pointer-events:none;color:var(--accent-ink)}
  #ksyncBtn.ksync-spin svg{animation:rbspin .7s linear infinite}
  /* Compact variant for an inline job-row meta line — slightly smaller controls, tighter margin. */
  .kl-row{margin-left:6px;gap:3px}
  .kl-row .kl-mark{width:16px;height:16px;font-size:9.5px}
  .kl-row .kl-btn{width:18px;height:18px}
  .banner{position:sticky;top:0;z-index:21;background:var(--warm-soft);color:var(--warm);font-size:12px;font-weight:600;padding:7px 22px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:8px}
  .banner.announce{background:var(--accent-soft);color:var(--accent-ink)}
  /* "Read more" on an announcement — underlined so it reads as a link against text that is already
     coloured and bold, which on its own would not distinguish it. */
  .banner-more{color:inherit;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}
  .banner-more:hover{opacity:.75}
  /* The update notice (js/build-check.js) and the message bar share one sticky stack, so both can
     show at once without overlapping; --banner-h measures the stack, not either banner. */
  .banner-stack{position:sticky;top:0;z-index:21}
  .banner-stack>.banner{position:static}
  /* Amber, and the button breathes: in the app's own violet it read as part of the chrome and
     nobody noticed it. (The global reduced-motion rule stops the pulse for people who ask.) */
  .banner.update{background:var(--warn-soft);color:var(--warn);border-bottom-color:color-mix(in srgb,var(--warn) 35%,transparent)}
  .banner.update .bu-txt{flex:1;min-width:0}
  .banner.update .btn{flex:none;padding:4px 12px;font-size:12px;font-weight:700;background:var(--warn);border:1px solid var(--warn);color:#fff;animation:bu-pulse 2s ease-in-out infinite}
  .banner.update .btn:hover{filter:brightness(1.08);animation-play-state:paused}
  @keyframes bu-pulse{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--warn) 55%,transparent)}50%{box-shadow:0 0 0 6px color-mix(in srgb,var(--warn) 0%,transparent)}}
  .banner.update .bu-x{flex:none;background:none;border:0;color:inherit;cursor:pointer;font-size:15px;line-height:1;padding:2px 4px;opacity:.7}
  .banner.update .bu-x:hover{opacity:1}

  /* ============================ MAIL ============================ */
  .mail-connect{max-width:460px;margin:60px auto;text-align:center;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);padding:34px}
  .mail-connect .ic-lg{width:46px;height:46px;border-radius:14px;margin:0 auto 16px;display:grid;place-items:center;background:linear-gradient(135deg,var(--accent),#847fee);color:#fff}
  .mail-connect .ic-lg svg{width:24px;height:24px;stroke:#fff;stroke-width:1.8;fill:none}
  .mail-connect h2{font-size:18px;margin-bottom:6px}.mail-connect p{color:var(--muted);font-size:13px;margin:0 0 20px;line-height:1.5}
  .mail-connect .err{margin-top:14px;padding:10px 13px;border-radius:9px;background:var(--crit-soft);color:var(--crit);font-size:12px;text-align:left}
  /* Background filing (js/mail-bg-filing.js) — one line above the mail shell. Quiet when it is
     working, and it keeps the shell's own height intact by taking its margin out of the gap below. */
  /* Private conversations (js/mail-private.js). The badge sits above the message, where a party
     cannot miss it; the composer toggle sits beside the send buttons. */
  .mail-private-badge{display:flex;align-items:center;gap:9px;margin:0 18px 8px;padding:8px 12px;border-radius:var(--r);
    background:var(--panel-2);border:1px solid var(--line);font-size:12px;color:var(--ink);line-height:1.45}
  .mail-private-badge .ic{flex:none}
  .mail-private-badge .t{flex:1;min-width:0}
  .mail-private-badge .lnk{flex:none;border:none;background:none;padding:0 2px;font-size:11.5px;font-weight:600;color:var(--accent-ink);cursor:pointer}
  .mc-private{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);margin-right:2px;cursor:pointer;user-select:none}
  .mc-private.on{color:var(--ink);font-weight:600}
  .mc-private input:disabled{cursor:not-allowed}
  .mc-private-i{border:none;background:none;padding:0 6px 0 0;font-size:12px;color:var(--faint);cursor:pointer}
  /* Restrictions (js/mail-audiences.js): the line atop a timeline, and the warning in the triage bar. */
  .mail-aud-line{display:flex;align-items:center;gap:8px;padding:8px 14px;font-size:12px;color:var(--muted);border-bottom:1px solid var(--line-2)}
  .mail-aud-line.on{color:var(--ink);background:var(--panel-2)}
  .mail-aud-line .t{flex:1;min-width:0}
  .mail-aud-line .lnk{flex:none;border:none;background:none;padding:0;font-size:11.5px;font-weight:600;color:var(--accent-ink);cursor:pointer}
  .bft-row{padding:3px 0;color:var(--muted)}
  .bft-row.err{color:var(--crit)}
  .bft-row.wait{color:var(--faint)}
  .mail-aud-warn{display:flex;align-items:flex-start;gap:8px;padding:7px 12px;margin:4px 0 0;font-size:11.5px;line-height:1.45;
    color:var(--ink);background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r)}
  .mail-aud-warn .t{flex:1;min-width:0}
  .aud-list{display:flex;flex-direction:column;gap:4px;max-height:280px;overflow:auto;padding:2px}
  .aud-row{display:flex;align-items:center;gap:8px;font-size:12.5px;cursor:pointer}
  /* The one-time mailbox set-up card (js/mail-bg-filing.js) — drawn only while a consent is missing. */
  .mail-setup{margin:0 0 9px;border:1px solid var(--line);border-radius:var(--r);background:var(--panel);font-size:12.5px;color:var(--ink);line-height:1.45}
  .mail-setup-h{display:flex;align-items:center;gap:10px;padding:8px 12px;border-bottom:1px solid var(--line)}
  .mail-setup-h .n{color:var(--muted);font-size:11.5px;font-variant-numeric:tabular-nums}
  .mail-setup-h .bar{flex:0 0 72px;height:4px;border-radius:2px;background:var(--line);overflow:hidden;margin-left:auto}
  .mail-setup-h .bar i{display:block;height:100%;background:var(--good)}
  .mail-setup-row{display:flex;align-items:center;gap:9px;padding:7px 12px}
  .mail-setup-row + .mail-setup-row{border-top:1px solid var(--line)}
  .mail-setup-row .dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--warn)}
  .mail-setup-row.warn{background:var(--crit-soft)}
  .mail-setup-row.warn .dot{background:var(--crit)}
  .mail-setup-row .t{flex:1;min-width:0}
  .mail-setup .sub{color:var(--muted)}
  .mail-setup-row .btn.sm{flex:none;padding:4px 11px;font-size:11.5px;min-width:64px;justify-content:center}
  .mail-setup-row .lnk{flex:none;border:none;background:none;padding:0 2px;font-size:11.5px;font-weight:600;color:var(--muted);cursor:pointer}
  .mail-setup-row .lnk:hover{color:var(--ink)}
  .mail-setup-row .lnk:disabled{opacity:.5;cursor:default}
  .mail-setup-f{padding:6px 12px 8px;font-size:11.5px;color:var(--muted)}
  .mail-conn-row{display:flex;align-items:center;gap:12px;padding:8px 0;border-top:1px solid var(--line)}
  .mail-conn-row:first-child{border-top:none}
  .mail-conn-row .t{flex:1;min-width:0}
  .mail-conn-row .sub{color:var(--muted);font-size:12px}
  .mail-conn-row .b{flex:none;display:flex;gap:6px}
  .mail-shell{display:flex;height:calc(100vh - 190px);min-height:520px;border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);background:var(--panel);overflow:hidden}
  .mail-rail{width:196px;flex:none;border-right:1px solid var(--line);display:flex;flex-direction:column;overflow:hidden;padding:12px 8px}
  /* Only the middle scrolls (2026-09-14): Compose + the account buttons above and Demo inbox / Help
     below stay where they are; the folders and filters between take the room left and scroll in it. */
  .mail-rail-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain}
  .mail-rail .compose-btn,.mail-rail-tools,.mail-rail-foot{flex:none}
  .mail-rail .compose-btn{margin:2px 6px 12px}
  /* Signature + Disconnect (2026-08-21) — moved here from the page-wide header, icon-only so they
     don't compete with Compose for weight. */
  .mail-rail-tools{display:flex;gap:6px;margin:0 6px 12px}
  .mail-rail-tools .icon-btn{flex:1}
  /* Demo inbox + Help (2026-08-26) — moved out of the page-wide header into the Emails column, since
     that is what they are about. margin-top:auto pins them to the foot of a short rail; on a long one
     the auto margin collapses and they simply follow the last folder, which is where you would look
     for them anyway. Labelled rather than icon-only: neither is familiar enough to survive a glyph. */
  .mail-rail-foot{margin-top:auto;display:flex;flex-direction:column;gap:6px;padding:14px 6px 2px}
  .mail-rail-foot .btn{width:100%;justify-content:center}
  /* The same way in, for the state that has no rail to hang it off — no mailbox connected, so the
     connect card is all the Emails tab has. Still inside the tab, never in the page-wide header. */
  .mail-connect-demo{margin-top:14px;padding-top:14px;border-top:1px solid var(--line);display:flex;align-items:center;justify-content:center;gap:10px;font-size:12.5px;color:var(--muted)}
  .mail-folder{display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:var(--r-sm);color:var(--muted);font-size:12.5px;font-weight:500;cursor:pointer}
  .mail-folder:hover{background:var(--line-2);color:var(--ink)}
  .mail-folder.on{background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
  .mail-folder .ic{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none;flex:none}
  .mail-folder .n{margin-left:auto;font-family:var(--font-num);font-size:10.5px;color:var(--faint)}
  .mail-folder.on .n{color:var(--accent-ink)}
  .mail-rail .grp-lab{padding:14px 9px 4px 9px;font-size:10px}
  .mail-folder .del-x{opacity:0;margin-left:4px;color:var(--faint);font-size:10.5px;white-space:nowrap}.mail-folder:hover .del-x{opacity:1}
  .mail-list{width:340px;flex:none;border-right:1px solid var(--line);display:flex;flex-direction:column;overflow:hidden}
  .mail-list-tools{display:flex;align-items:center;gap:6px;padding:9px 10px;border-bottom:1px solid var(--line);position:relative}
  /* Icon only (2026-09-11): the ✦ in the same 34px square as the filter button beside it; while the
     run goes, the square widens just enough for its count. */
  .mail-list-tools .mail-auto-all{flex:none;font-size:14px;color:var(--accent-ink)}
  .mail-list-tools .mail-auto-all.busy{width:auto;min-width:34px;padding:0 7px;font-size:10.5px;font-family:var(--font-num);cursor:progress}
  .mail-list-tools .mail-bands-all{flex:none}
  .mail-list-tools .chk{flex:none}
  /* The filter button and its panel. The panel is always in the DOM and shown by class, so opening
     it costs no repaint of the list behind it. */
  /* NOT the positioning anchor. The panel hangs off the TOOLS BAR, not off the button, because
     .mail-list and .mail-shell are both overflow:hidden — anything that overshoots the column is
     clipped rather than scrolled, with no way for the reader to reach it. Anchored to the bar and
     inset on both sides, it cannot overshoot whatever the column's width happens to be. Measured:
     anchored to the button it rendered at x=-197 in a narrow bar. */
  .mail-filter{flex:none}
  .mail-filter .icon-btn{position:relative}
  /* A dot rather than a number on the button itself: the count is in the tooltip and the panel, and
     a digit at this size is harder to read than "something is on". */
  .mfm-dot{position:absolute;top:3px;right:3px;width:5px;height:5px;border-radius:50%;
    background:var(--accent);box-shadow:0 0 0 1.5px var(--panel)}
  .mail-filter-menu{display:none;position:absolute;top:calc(100% - 3px);left:8px;right:8px;z-index:40;
    padding:6px;background:var(--panel);border:1px solid var(--line);
    border-radius:var(--r-sm);box-shadow:var(--shadow-lg)}
  .mail-filter.open .mail-filter-menu{display:block}
  .mfm-row{display:flex;align-items:flex-start;gap:8px;padding:7px 8px;border-radius:6px;
    font-size:12.5px;color:var(--ink);cursor:pointer}
  .mfm-row:hover{background:var(--panel-2)}
  .mfm-row input{margin-top:1px;flex:none}
  .mfm-row b{font-weight:600;display:block}
  /* The second line is the rule in words. Without it "hide the ones I am only copied on" reads as
     "hide anything that ever copied me", which is a different and much blunter filter. */
  .mfm-row i{display:block;font-style:normal;font-size:11px;color:var(--faint);margin-top:1px}
  .mfm-sep{height:1px;background:var(--line-2);margin:4px 2px}
  .mfm-size{cursor:default;justify-content:space-between;align-items:center}
  .mfm-size:hover{background:none}
  .mfm-sizes{display:flex;gap:4px;flex:none}
  .mfm-n{font-family:var(--font-num);font-size:11px;padding:3px 8px;border-radius:6px;cursor:pointer;
    border:1px solid var(--line);background:var(--panel-2);color:var(--muted)}
  .mfm-n:hover{border-color:var(--muted);color:var(--ink)}
  .mfm-n.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-ink);font-weight:700}
  .mfm-clear{display:block;width:100%;text-align:left;padding:7px 8px;border:0;border-radius:6px;
    background:none;color:var(--accent-ink);font-size:12px;font-family:inherit;cursor:pointer}
  .mfm-clear:hover{background:var(--accent-soft)}
  .mc-selcount{font-size:12.5px;color:var(--muted);white-space:nowrap;margin-right:2px}
  .mail-list-rows{flex:1;overflow-y:auto}
  /* The foot of the list: "Loading more…", the Load more button, or that the folder is done. */
  .mail-list-more{padding:14px 12px 18px;text-align:center;font-size:12px;color:var(--faint)}
  .mail-list-more.err{color:var(--warn)}
  .mail-list-more .btn{margin-left:6px}
  /* The sender band. STICKY, because the whole point is working a run of ten from one sender: scroll
     into the middle of that run and the name and its checkbox have to still be there. */
  .mail-band{position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:8px;
    padding:6px 12px;background:var(--panel-2);border-bottom:1px solid var(--line-2);
    border-top:1px solid var(--line-2);font-size:11.5px}
  .mail-band .chk{flex:none}
  /* The group's own fold (2026-09-11). Leads the header so every chevron sits in one column. */
  .mb-chev{flex:none;width:18px;height:18px;border:0;background:transparent;color:var(--muted);cursor:pointer;
    display:grid;place-items:center;padding:0;border-radius:5px}
  .mb-chev:hover{color:var(--ink);background:var(--line-2)}
  .mb-chev .ic{width:14px;height:14px;stroke:currentColor;stroke-width:2.2;fill:none}
  .mb-who{font-weight:700;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mb-n{margin-left:auto;flex:none;font-family:var(--font-num);color:var(--faint)}
  .mail-row{display:flex;gap:9px;padding:10px 12px;border-bottom:1px solid var(--line-2);cursor:pointer;align-items:flex-start}
  .mail-row:hover{background:var(--panel-2)}.mail-row.sel{background:var(--accent-soft)}.mail-row.unread .subj{font-weight:700}
  .mail-row .chk{margin-top:3px}
  .mail-row .body{min-width:0;flex:1}
  .mail-row .top{display:flex;align-items:baseline;gap:6px}
  /* Widened from 15ch (2026-08-25): a row now names the PEOPLE in the conversation, not just the
     newest sender, and "Anupreksha, Lisa" does not fit in fifteen characters. */
  /* The last sender, IN FULL (2026-09-10): wraps rather than truncating, and the date keeps its own
     space (flex:none) so the two can never overlap. */
  .mail-row .from{font-size:12.5px;font-weight:600;flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
  /* How many messages are in the conversation. Outside .from on purpose — .from ellipsis-truncates,
     and a count clipped out of sight is exactly how the draft badge went missing. */
  .mail-thread-n{flex:none;font-size:10.5px;font-weight:650;font-family:var(--font-num);color:var(--muted);
    background:var(--panel-2);border:1px solid var(--line-2);border-radius:9px;padding:0 5px;line-height:15px}
  .mail-row.unread .mail-thread-n{color:var(--ink);border-color:var(--line)}
  .mail-row .when{margin-left:auto;flex:none;font-size:10.5px;color:var(--faint);white-space:nowrap;font-family:var(--font-num)}
  /* Triage's last-turn chip, beside the date in the grouped list (mailRowAgeHtml) — sized to the line. */
  .mail-row .mail-row-age{flex:none;font-size:10px;line-height:15px;padding:0 6px;white-space:nowrap;font-family:var(--font-num)}
  .mail-row .subj{font-size:12.5px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mail-row .prev{font-size:11.5px;color:var(--faint);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mail-row .flags{display:flex;gap:5px;align-items:center;margin-top:5px;flex-wrap:wrap}
  .mail-row .star{color:var(--faint)}.mail-row .star.on{color:var(--warm)}
  /* A flagged row wears the SAME flag icon as the toolbar button that set it (2026-09-11) — it was a ★,
     so the row and the button looked like two different ideas. Warm, as the star was. */
  .mail-row .mail-row-flag{display:inline-flex;align-items:center;color:var(--warm)}
  /* The stroke is the container's job here — there is no global .ic rule, and a bare svg fills black. */
  .mail-row .mail-row-flag .ic{width:13px;height:13px;stroke:currentColor;stroke-width:2;fill:none}
  .mail-reading{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden}
  .mail-reading-h{padding:14px 18px;border-bottom:1px solid var(--line)}
  .mail-reading-h .subj{font-size:16px;font-weight:700}
  /* Which draft this is (mail.js mailComposeOtherDraftsHtml) — quiet, but not missable. */
  .mail-reading-h .subj .mc-saved{font-size:12px;font-weight:400;color:var(--faint)}
  .mc-other-drafts{margin:0 0 10px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:var(--card, transparent);font-size:12.5px;display:flex;flex-wrap:wrap;gap:6px;align-items:center}
  .mail-reading-actions{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:10px;flex-wrap:wrap}
  .mra-left,.mra-right{display:flex;gap:6px}
  .icon-btn.on{color:var(--warm);border-color:var(--warm)}
  /* Compact 2026-08-21 — was 4 full .kv rows (From/To/Cc/Received, ~35px each, bordered+padded) and
     ate a third of the preview before any email content showed. Same fields, two dense lines. */
  .mail-reading-meta{padding:8px 18px;border-bottom:1px solid var(--line-2);font-size:12px;color:var(--muted);display:flex;flex-direction:column;gap:3px}
  .mail-reading-meta b{color:var(--ink);font-weight:600}
  .mrm-row{display:flex;align-items:baseline;gap:5px;line-height:1.4}
  .mrm-row .mrm-date{margin-left:auto;flex:none;color:var(--faint);font-size:11px;font-family:var(--font-num);padding-left:10px}
  .mrm-cc-lab{margin-left:8px}
  .mail-reading-scroll{flex:1;min-width:0;overflow-y:auto;overflow-x:hidden;padding:18px}
  .mail-reading-scroll .mail-html{font-size:13.5px;line-height:1.6;color:var(--ink)}
  /* Comments as their own region below the body scroller (2026-08-21) — was appended INSIDE the
     body's own scroll blob, so reaching the compose box on a long email meant scrolling past the
     whole thing first. flex:none caps it to its own content up to max-height; a comment thread
     longer than that scrolls WITHIN mail-comment-thread (below, scoped to this context only — the
     bare .mail-comment-thread class is shared with js/reviews.js, which must not inherit this cap)
     while the compose row (mail-comment-input-row, un-scoped, already flex:none by nature) stays
     pinned beneath it, always reachable without scrolling anything else first. */
  /* No overflow:hidden here on purpose — the @mention dropdown (.mail-at-dd) is an absolutely
     positioned CHILD of .mail-comment-input-row that pops up ABOVE the textarea; an ancestor with
     overflow:hidden between it and the viewport would clip it off mid-use. max-height alone still
     properly bounds this flex column and forces mail-comment-thread's own overflow-y:auto below to
     activate — clipping isn't needed for that, just for the (unlikely) case content still pokes out
     a pixel or two past the cap, which isn't worth losing the dropdown over. */
  /* …and it SHRINKS (2026-09-14). flex:none held it at full height even when the pane was shorter than
     everything in it: with the triage bar open, header + bar + comments overflowed the column and
     .mail-reading's overflow:hidden cut the reply box off at the bottom — "the replying to a comment
     thread user interface is cut off and clipped". min-height:0 lets the area give way; the thread list
     inside it scrolls, while the toggle, the "Replying in" line and the reply box (flex:none) stay whole. */
  .mail-comment-area{flex:0 1 auto;min-height:0;display:flex;flex-direction:column;max-height:46%;border-top:1px solid var(--line-2);padding:12px 18px;gap:8px}
  .mail-comment-area .mail-comment-thread{flex:0 1 auto;min-height:0;overflow-y:auto;margin-bottom:0}
  .mail-comment-area .mail-comment-input-row,.mail-comment-area .mail-comment-to,.mail-comment-area .mail-comment-toggle{flex:none}
  /* .mail-html renders untrusted email body content in three places (the reading pane above, the
     read-only preview modal, and quoted prior turns inside compose) — a real email is unbroken-length
     content with no guaranteed wrap points (a long URL, a run of underscores from a signature line, a
     pasted table), and without this ONE such token forces its container wider than the flex layout
     around it. Flex items default to min-width:auto, not 0, so the whole .mail-shell/.mail-reading
     chain gets shoved wide, squashing the folder rail and pushing the reading pane into horizontal
     overflow — reported 2026-08-21 against a specific email whose body had exactly this shape.
     overflow-wrap:anywhere is deliberately more aggressive than break-word: it breaks even where
     there's no better option, the only way to guarantee one long token can't blow out the layout
     again. Bare class rule (not scoped to one parent) so it protects all three usages, not just this
     one — the failure mode is identical regardless of which container the body happens to render in. */
  .mail-html{overflow-wrap:anywhere;word-break:break-word}
  .mail-html img,.mail-html table{max-width:100%}
  .mail-compose{min-width:0}
  .mail-empty{flex:1;display:grid;place-items:center;color:var(--faint);font-size:13px;text-align:center;padding:30px}
  /* A self-contained floating card (its own border/shadow/margin), sized to its own content, rather
     than stretched to match the shell's fixed height — that stretch either left the footer stranded
     far below a short message, or forced mail-compose-fields to grow into blank space wrapped in its
     own confusing (and mostly empty) scrollbar. Any leftover room below the card is just the reading
     pane's plain background, same as a short message being read shows blank space beneath it — not
     a second, unexplained scroll region.

     The cap USED to be max-height:calc(100% - 28px), which cut the Send/Save/Cancel row off the
     bottom of the screen on any long draft. 100% is the height of .mail-reading — but the card does
     not have that box to itself: the "Edit draft" header is its sibling in the same column. So the
     card was allowed to be the full height MINUS its own margins, the header pushed it down by
     another ~50px, and .mail-reading's overflow:hidden quietly ate the footer. It was never
     scrolled-past-able either, because the footer is outside the fields' scroll region.
     flex-shrink does the job properly: the card takes its content's height and shrinks only when
     there is not enough room, which is precisely the leftover space after the header. min-height:0
     on both is what lets a flex item shrink below its content at all (the default is auto = never),
     and .mail-compose-fields is where that shrink lands, as its own scroll region. */
  .mail-compose{margin:14px 18px;border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow);background:var(--panel-2);display:flex;flex-direction:column;flex:0 1 auto;min-height:0;overflow:hidden}
  .mail-compose-fields{overflow-y:auto;min-height:0;padding:14px 18px}
  /* A file dragged over the message: the whole card is the drop target, and says so (mailComposeWireDrop). */
  .mail-compose{position:relative}
  .mail-compose.drop-over{outline:2px dashed var(--accent);outline-offset:-4px}
  .mail-compose.drop-over::after{content:'Drop to attach';position:absolute;inset:0;display:grid;place-items:center;
    background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent-ink);font-weight:700;font-size:14px;
    pointer-events:none;z-index:5}
  .mail-compose .row{display:flex;gap:8px;margin-bottom:8px;align-items:center}
  .mail-compose .row label{font-size:11.5px;color:var(--faint);min-width:34px}
  .mail-compose input.f,.mail-compose textarea.f{flex:1;width:100%}
  .mail-compose textarea.f{min-height:130px;resize:vertical;font-family:var(--font-ui)}
  .mail-compose .foot{flex:none;display:flex;gap:8px;padding:12px 18px;border-top:1px solid var(--line);justify-content:flex-end}
  /* ---- SEND LATER (js/mail-schedule-ui.js) ----------------------------------------------------
     The panel is always present and revealed by class — toggling it must not go through render(),
     which would tear Quill down mid-sentence. So it is laid out collapsed (height 0, no padding)
     rather than display:none, which also gives the disclosure something to animate. */
  .mail-sendlater{flex:none;overflow:hidden;max-height:0;transition:max-height .16s ease;border-top:0 solid var(--line)}
  .mail-sendlater.open{max-height:230px;border-top-width:1px}
  .mail-sendlater .msl-panel{padding:12px 18px;display:flex;flex-direction:column;gap:8px}
  .mail-sendlater .msl-row{display:flex;gap:8px;align-items:center}
  .mail-sendlater .msl-row label{font-size:11.5px;color:var(--faint);min-width:56px}
  .mail-sendlater .msl-row input.f{flex:0 0 auto;width:auto;font-family:var(--font-num);font-variant-numeric:tabular-nums}
  .mail-sendlater .msl-note{font-size:11.5px;color:var(--faint);line-height:1.45}
  .mail-sendlater .msl-err{font-size:11.5px}
  .mail-sendlater .msl-acts{display:flex;gap:8px;justify-content:flex-end}
  /* The standing line on a draft that is going out on its own. Accent while it is simply scheduled;
     the warning colour only once a send has actually failed — colour is semantic here, so "waiting"
     and "went wrong" must never share one (§8). */
  .mail-sched-banner{flex:none;display:flex;gap:8px;align-items:center;flex-wrap:wrap;
    padding:9px 18px;border-top:1px solid var(--line);background:var(--accent-soft);color:var(--accent-ink);font-size:12px}
  .mail-sched-banner.bad{background:var(--crit-soft);color:var(--crit)}
  .mail-sched-banner .msb-ic{font-size:13px;line-height:1}
  .mail-sched-banner .msb-txt{flex:1;min-width:0;line-height:1.45}
  .mail-sched-banner .msb-err,.mail-sched-banner .msb-consent{flex:0 0 100%;display:flex;gap:8px;align-items:center;
    margin-top:6px;font-size:11.5px;line-height:1.45}
  .mail-sched-banner .msb-consent .btn{flex:none}
  /* The pill on the Drafts row — the same accent family as the Draft tag beside it, because they are
     two facts about one row rather than two unrelated states. */
  .tag.mail-sched-pill{color:var(--accent-ink);background:var(--accent-soft);font-variant-numeric:tabular-nums}
  .mail-linktags{display:flex;gap:5px;flex-wrap:wrap;margin-top:8px;align-items:center}
  .mail-linktags .tag .x{opacity:.6;font-size:12px}.mail-linktags .tag .x:hover{opacity:1}
  /* Confirmed marker on a multi-client auto pill (js/mail.js linkTags) — plain text, not a button,
     once a pill no longer needs a decision. */
  .mail-attn-confirmed{opacity:.7;font-size:11px;margin-left:3px}
  /* "Needs attention" row badges (js/mail.js mailAttentionBadgesHtml) — Triage list rows AND
     Timeline rows share this styling. warn = a decision this app can't make for you (an unlinked
     contact, a stranger); info = a filing gap that just needs the reading pane (multi-client still
     pending, no job yet) — same visual weight as the existing .tag pills elsewhere, distinguished by
     colour rather than size so a busy row doesn't get louder than the rest of the list. */
  .mail-attn{display:inline-flex;gap:4px;flex-wrap:wrap}
  .mail-attn-tag{display:inline-flex;align-items:center;gap:4px;padding:1px 7px;border-radius:99px;font-size:10.5px;font-weight:600;white-space:nowrap}
  .mail-attn-tag.warn{background:var(--warn-soft);color:var(--warn)}
  .mail-attn-tag.info{background:var(--info-soft);color:var(--info)}
  .mail-attn-action{cursor:pointer;text-decoration:underline;text-underline-offset:2px;opacity:.85}
  .mail-attn-action:hover{opacity:1}
  /* THE TRIAGE BAR (js/mail.js mailReadingHtml, 2026-08-24) — every triage action in one visually
     fenced panel between the email's identity and its body, one labelled row per kind of action
     (Next step / Client / Job). The left accent border + tinted ground is what says "this is the
     working surface, not part of the message" — the exact complaint was controls reading as email
     decoration. */
  .mail-triage-bar{border-bottom:1px solid var(--line-2);border-left:3px solid var(--accent);background:var(--panel-2);padding:8px 18px 8px 15px;display:flex;flex-direction:column;gap:6px}
  .mtb-row{display:flex;align-items:center;gap:8px}
  /* These triage pills already have click handlers (open the linked client/contact/job, or confirm
     an automatic decision). Keep the affordance visible so the hand cursor matches the behaviour. */
  .mail-triage-bar .tag[data-client],.mail-triage-bar .tag[data-contact],.mail-triage-bar .tag[data-job],
  .mail-triage-bar .tag[data-mail-confirm-auto],.mail-triage-bar .mns-set[data-mail-confirm-auto],
  .mail-triage-bar .tag[data-mail-party-client],.mail-triage-bar a.tag.pill-contact{cursor:pointer}
  .mtb-lab{flex:none;width:64px;font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--faint)}
  .mtb-content{display:flex;align-items:center;gap:5px;flex-wrap:wrap;min-width:0}
  /* The note that sits beside a triage row — a suggestion the app has made, or a plain statement of
     what it found. Reported as a mismatch (2026-08-24): the "nobody matches a contact" line was a
     bare .q and rendered at a different size to the suggestion note next to it, so two things of the
     same weight looked like two different kinds of thing. One class now, used by both. */
  .mtb-sug{font-size:10.5px;color:var(--faint);line-height:1.35}
  /* A suggestion sitting beside the answer (2026-09-12): ✨ what AI would say instead, ✦ what was
     displaced when one was taken. Outlined, never filled — an answer is solid, a suggestion is not. */
  .mtb-sug-chip{margin-left:6px;padding:2px 8px;border-radius:999px;cursor:pointer;font-family:inherit;
    font-size:11px;line-height:1.5;background:transparent;border:1px dashed var(--line);color:var(--muted);
    max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}
  .mtb-sug-chip.ai{border-color:var(--accent);color:var(--accent-ink)}
  .mtb-sug-chip:hover{border-style:solid;background:var(--panel-2);color:var(--ink)}
  /* An unsent draft reply to the open conversation, offered rather than opened for you (2026-08-25).
     Sits above the triage strip because it is about the message itself, not about filing it. */
  .mail-draftbar{display:flex;align-items:center;gap:9px;padding:7px 14px;font-size:12px;color:var(--body);
    background:color-mix(in srgb,var(--accent) 7%,var(--panel));border-bottom:1px solid var(--line-2)}
  .mail-draftbar .btn{margin-left:auto;padding:3px 10px;font-size:11.5px}

  /* The folded triage strip (2026-08-24). One line where five rows of controls used to sit
     permanently above the message — the reading pane had almost no room for the email itself. */
  .mtb-strip{display:flex;align-items:center;gap:10px;padding:6px 14px;border-bottom:1px solid var(--line-2);background:var(--panel-2);font-size:11.5px;flex-wrap:wrap}
  .mtb-strip.ready{background:color-mix(in srgb,var(--good) 8%,var(--panel-2));border-top:2px solid var(--good)}
  .mtb-strip.attention{background:color-mix(in srgb,var(--warn) 7%,var(--panel-2));border-top:2px solid var(--warn)}
  .mtb-toggle{display:inline-flex;align-items:center;gap:7px;border:0;background:transparent;cursor:pointer;padding:2px 0;color:var(--body);font:inherit;font-weight:650}
  .mtb-caret{color:var(--faint);font-size:10px;width:9px}
  .mtb-dots{display:inline-flex;gap:3px}
  .mtb-dot{width:7px;height:7px;border-radius:50%;background:var(--line);display:block}
  .mtb-dot.ready{background:var(--good)}
  .mtb-dot.attention{background:var(--warn)}
  .mtb-dot.conflicted{box-shadow:0 0 0 2px var(--warn-soft)}
  .mtb-row.ready{border-left:3px solid var(--good);padding-left:5px}
  .mtb-row.attention{border-left:3px solid var(--warn);padding-left:5px}
  /* The triage trail (2026-08-25) — how the conversation got to the state the strip is reporting.
     A count, not a word: the control has to be small enough to sit on a strip that is already doing
     a job, and the number is the only part worth reading before you decide to open it. */
  .mtb-trail-btn{display:inline-flex;align-items:center;gap:4px;border:0;background:transparent;cursor:pointer;
    padding:2px 6px;border-radius:7px;color:var(--muted);font:inherit;font-size:11px;font-weight:650}
  .mtb-trail-btn .ic{width:13px;height:13px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round}
  .mtb-trail-btn:hover{background:var(--panel);color:var(--ink)}
  .mtb-trail-btn.on{background:var(--panel);color:var(--ink)}
  /* Tall enough for an ordinary thread to show WHOLE. At 210px a five-turn conversation came to
     nine entries and showed eight, with the ninth below a thin scrollbar nobody notices — reported
     as "why is this turn not in your audit trail?" when it was in it all along. A trail that hides
     rows is worse than no trail, because it invites exactly that conclusion. Long threads still
     scroll, but by then the region is plainly a scroller and the header says how many to expect. */
  .mail-trail{border-bottom:1px solid var(--line-2);background:var(--panel-2);padding:0 14px 8px;max-height:min(46vh,420px);overflow-y:auto}
  .mtr-head{position:sticky;top:0;background:var(--panel-2);padding:7px 0 4px;font-size:10.5px;font-weight:650;
    letter-spacing:.03em;text-transform:uppercase;color:var(--faint);border-bottom:1px solid var(--line-2)}
  .mtr-row{display:flex;align-items:baseline;gap:9px;padding:4px 0;font-size:11.5px;border-bottom:1px solid var(--line-2)}
  /* Correspondence vs decision, told apart by a mark rather than by reading the line. The arrows
     reuse the timeline's own in/out colours (.mail-tl-dir) so one direction means one thing
     everywhere in the app. */
  .mtr-mark{flex:none;width:13px;text-align:center;font-size:11px;line-height:1}
  .mtr-mark.in{color:var(--info)}
  .mtr-mark.out{color:var(--good)}
  .mtr-mark.ev{color:var(--line);font-size:8px}
  .mtr-row.mail .mtr-what{font-weight:600}
  .mtr-row:last-child{border-bottom:0}
  .mtr-what{flex:1;min-width:0;color:var(--ink)}
  .mtr-who{color:var(--muted);white-space:nowrap}
  .mtr-when{font-family:var(--font-num);color:var(--faint);white-space:nowrap;font-size:10.5px}
  /* Amber, not grey. "Not ready" is a STATE — the same orange the unanswered contact and client
     questions already wear — and a muted grey read as a caption rather than as one half of a scale
     whose other half is green. */
  .mtb-missing{color:var(--warn)}
  .mtb-missing b{font-weight:700}
  /* "✓ Done" on the strip — green like the dots, and the way back out when pressed. */
  .mtb-done{color:var(--good);font-weight:650}
  .mtb-learn{padding:3px 9px;font-size:11px;color:var(--accent-ink);border-color:color-mix(in srgb,var(--accent) 35%,var(--line))}
  .mtb-learn:hover{border-color:var(--accent);background:var(--accent-soft)}
  /* Quick triage sits with the other strip controls, and reads as an action rather than a state. */
  /* The Triage page's own, in the Email column beside Mark as done — the same control, same wording. */
  .aq-done-acts .aq-quick{color:var(--accent-ink);border-color:color-mix(in srgb,var(--accent) 35%,var(--line))}
  .aq-done-acts .aq-quick:hover{border-color:var(--accent);background:var(--accent-soft)}
  /* Clear, beside ✓ Done: quieter than Done, because it changes nothing about the email itself. */
  .mtb-clearnow{padding:3px 9px;font-size:11px;color:var(--muted)}
  .mtb-clearnow:hover{color:var(--ink);border-color:var(--line-2)}
  .mtb-quick{padding:3px 9px;font-size:11px;color:var(--accent-ink);border-color:color-mix(in srgb,var(--accent) 35%,var(--line))}
  .mtb-quick:hover{border-color:var(--accent);background:var(--accent-soft)}
  /* The menu: one row per preset — what it is, what it does, and what it would answer on THIS email. */
  .mqt-rows{display:flex;flex-direction:column;gap:8px}
  .mqt-row{display:flex;flex-direction:column;gap:3px;text-align:left;cursor:pointer;padding:10px 12px;
    border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel-2);color:inherit;font:inherit}
  .mqt-row:hover:not([disabled]){border-color:var(--accent);background:var(--accent-soft)}
  .mqt-row[disabled]{opacity:.55;cursor:default}
  .mqt-lab{font-size:13px;font-weight:600}
  .mqt-hint{font-size:11.5px;color:var(--muted)}
  .mqt-sets{font-size:11px;color:var(--accent-ink)}
  .mqt-row[disabled] .mqt-sets{color:var(--faint)}
  .mqt-ignore{font-size:11px;color:var(--warn)}
  .mail-learn-panel{margin:0 14px 8px;padding:12px 14px;background:var(--panel);border:1px solid color-mix(in srgb,var(--accent) 28%,var(--line));border-radius:9px;box-shadow:var(--shadow);display:grid;gap:10px;box-sizing:border-box;max-height:min(62vh,520px);overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;scrollbar-gutter:stable}
  /* Learning is the open state of the triage surface, not a second reading-pane card, so the panel
     itself does not scroll — the TRIAGE BAR does, while it is learning (2026-09-14). This used to say
     "let the pane own scrolling", but the pane (.mail-reading) is overflow:hidden and its only
     scroller is the email body below it: a long review ran off the bottom and was cut off, and the
     sticky Add condition / Publish row stuck to that clipped edge, over the last outcome. Reported as
     "the rule learning surface is clipped". While learning, the bar shrinks to the room there is
     (min-height:0) and scrolls within itself, with its heading and publish row pinned to its own
     edges. Only while learning: the ordinary triage rows open pickers that must not be clipped. */
  .mail-triage-bar .mail-learn-panel{margin:0;padding:12px 14px;max-height:none;overflow:visible;overscroll-behavior:auto;scrollbar-gutter:auto;}
  .mail-triage-bar.learning{flex:0 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain}
  .mail-triage-bar .mail-learn-head{top:0}
  .mail-triage-bar .mail-learn-actions{bottom:0}
  .mail-learn-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;position:sticky;top:-12px;z-index:2;padding-top:0;background:var(--panel)}.mail-learn-head b{display:block;font-size:12.5px}.mail-learn-head span{display:block;margin-top:3px;font-size:11px;color:var(--muted)}.mail-learn-head .btn{padding:3px 8px;font-size:11px}
  .mail-learn-fields{display:grid;grid-template-columns:minmax(0,1fr);gap:8px}.mail-learn-caption{font-size:11px;font-weight:650;color:var(--muted)}.mail-learn-conditions{display:grid;gap:6px}.mail-learn-condition{display:grid;grid-template-columns:52px minmax(130px,1fr) minmax(120px,1fr) minmax(180px,1.4fr) auto;gap:6px;align-items:center}.mail-learn-join{font-size:10.5px;font-weight:700;color:var(--faint);text-transform:uppercase}.mail-learn-condition .btn{padding:5px 8px;font-size:11px}.mail-learn-outcomes{display:grid;gap:6px;border-top:1px solid var(--line-2);padding-top:10px}.mail-learn-outcome{display:flex;align-items:center;gap:8px;min-height:30px;padding:5px 7px;border:1px solid var(--line-2);border-radius:7px;background:var(--panel-2);cursor:pointer}.mail-learn-outcome input{flex:none;accent-color:var(--accent)}.mail-learn-outcome span{display:grid;gap:2px;min-width:0;flex:1}.mail-learn-outcome b{font-size:11.5px}.mail-learn-outcome small{font-size:10.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mail-learn-outcome .f{width:190px;min-width:150px;padding:5px 7px;font-size:11px}.mail-learn-actions{display:flex;gap:7px}.mail-learn-actions .btn{width:auto;flex:none;padding:6px 10px;font-size:11.5px}@media(max-width:800px){.mail-learn-condition{grid-template-columns:1fr 1fr}.mail-learn-join{grid-column:1 / -1}.mail-learn-condition [data-mail-learn-value]{grid-column:1 / -1}.mail-learn-condition [data-mail-learn-remove]{justify-self:start}.mail-learn-outcome{align-items:flex-start}.mail-learn-outcome .f{width:140px;min-width:110px}}
  /* A decision the app made and nobody has confirmed — dashed, so it reads as provisional beside a
     confirmed one, and clickable to confirm. */
  /* Thin. `border-style:dashed` with no width falls back to `medium` (~3px), and .mtb-decided
     already carried a 3px left bar — together they drew the heavy box reported as "don't make it
     this thick borders, make them lighter". Width and colour are both stated now. */
  .tag.mtb-decided.auto{border:1px dashed color-mix(in srgb,var(--good) 55%,transparent);cursor:pointer}
  .mns-set.auto{border:1px dashed color-mix(in srgb,var(--accent) 55%,transparent);cursor:pointer}
  .tag.mtb-decided.auto:hover{border-color:var(--good)}
  .mns-set.auto:hover{border-color:var(--accent)}
  .mtb-auto{border:1px solid var(--line);background:var(--panel);border-radius:6px;cursor:pointer;color:var(--accent-ink);font-size:11px;line-height:1;padding:3px 7px}
  /* "Latest message ↗" — only drawn when the open email is not the newest turn (mailLatestTurnBtnHtml).
     Amber, because it is telling you what you are reading is out of date, not offering a decision. */
  .mtb-latest{border:1px solid var(--warn);background:var(--warn-soft);color:var(--warn);border-radius:6px;
    cursor:pointer;font-family:inherit;font-size:11px;line-height:1;padding:3px 8px;margin-right:4px;white-space:nowrap}
  .mtb-latest:hover{background:var(--warn);color:#fff}
  .mtb-auto:hover{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 8%,var(--panel))}
  /* ✨ beside ✦ — the AI half of the same job (js/mail-ai-triage.js). */
  .mtb-auto.mtb-ai{margin-left:4px}
  .mtb-auto.mtb-ai[disabled]{opacity:.55;cursor:progress}
  /* The ✦ inside a pill the AI chose — its own target, so the pill body still opens the record. */
  .mtb-ai-mark{cursor:pointer;color:var(--accent-ink);margin-left:2px}
  .mtb-ai-mark:hover{color:var(--accent)}
  .mail-auto-report{margin:0 12px 8px;padding:9px 12px;background:var(--panel-2);border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:7px;font-size:11px}.mail-auto-report-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:6px}.mail-auto-report-head span{color:var(--muted)}.mail-auto-rule{font-weight:650;color:var(--accent-ink)}.mail-auto-report-row{display:grid;grid-template-columns:90px minmax(0,1fr) auto;gap:8px;align-items:baseline;padding:4px 0;border-top:1px solid var(--line-2)}.mail-auto-report-row span{color:var(--ink)}.mail-auto-report-row small{font-size:10.5px}.mail-auto-report-row small.applied{color:var(--good)}.mail-auto-report-row small.preserved{color:var(--warn)}.mail-auto-conflict{margin:5px 0;padding:6px 8px;color:var(--warn-ink,var(--warn));background:color-mix(in srgb,var(--warn) 10%,var(--panel));border-radius:5px}
  /* The heuristic review is part of the same triage surface as the rows below it. Keep its edge
     aligned with those rows and use the regular light rule; the triage bar already carries the
     semantic accent. */
  .mail-triage-bar .mail-auto-report{margin:0;padding:9px 12px;border:1px solid var(--line-2);border-radius:4px}
  @media(max-width:700px){.mail-auto-report-row{grid-template-columns:1fr}.mail-auto-report-row small{padding-left:0}}
  .mtb-clear{margin-left:auto;font-size:11.5px;padding:3px 11px}
  /* Whose email it is, beside "Clear from my list" — takes over the push-right so the two sit together. */
  .mtb-notmine{margin-left:auto;font-size:11.5px;color:var(--muted);white-space:nowrap}
  .mtb-notmine + .mtb-clear{margin-left:8px}
  .mail-triage-bar.folded{display:none}
  /* The two other folds (2026-08-25) — the message actions and the comment box, which took
     permanent height above and below an email that had very little left. */
  .mra-more.on{color:var(--accent-ink)}
  /* Tinted, so it reads as a thing to use rather than a rule at the foot of the pane — "give it a
     different colour so that it catches the attention of the user". The @ carries the meaning, so it
     is sized up and the words never repeat it. */
  .mail-comment-toggle{display:flex;align-items:center;gap:8px;width:100%;border:0;border-top:1px solid var(--line-2);cursor:pointer;padding:8px 15px;font:inherit;font-size:12px;font-weight:650;text-align:left;
    background:color-mix(in srgb,var(--accent) 7%,var(--panel));color:var(--accent-ink)}
  .mail-comment-toggle:hover{background:color-mix(in srgb,var(--accent) 14%,var(--panel))}
  .mct-at{font-size:16px;font-weight:800;line-height:1;color:var(--accent)}
  .mct-sum{flex:0 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mct-chev{flex:none;margin-left:auto;color:var(--muted);font-size:11px}
  /* "N new" — comments I have not had in front of me yet. Solid, so it reads from across the pane. */
  .mct-new{flex:none;font-size:10px;font-weight:800;letter-spacing:.03em;text-transform:uppercase;
    color:var(--panel);background:var(--accent);border-radius:9px;padding:1px 7px}
  .mct-new.sm{font-size:9.5px;padding:0 6px}
  .mail-comment-toggle.has-new{background:color-mix(in srgb,var(--accent) 16%,var(--panel));box-shadow:inset 3px 0 0 var(--accent)}
  .mail-comment-area.folded{flex:none}
  /* A recorded decision — who said it and why live in the title; × undoes it. */
  .tag.mtb-decided{color:var(--good);background:var(--good-soft);border-left:3px solid var(--good)}
  /* Reason presets in the "why" dialogs — radio rows, presets before Other. */
  .mtb-reasons{display:flex;flex-direction:column;gap:4px;margin-bottom:8px}
  .mtb-reason{display:flex;align-items:center;gap:7px;font-size:12.5px;cursor:pointer;padding:3px 2px}
  /* Next-step verb row (js/mail.js mailNextStepRowHtml) — hosted inside the bar's first row, which
     carries the label. The suggested verb (✦, study-driven) is pre-lit so the common case is a
     single click on an already-highlighted answer. */
  .mail-nextstep{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:11.5px}
  .mns-btn{border:1px solid var(--line);background:var(--panel);color:var(--muted);font-size:11px;font-weight:600;padding:3px 9px;border-radius:99px;cursor:pointer}
  .mns-btn:hover{border-color:var(--accent);color:var(--accent-ink)}
  .mns-btn.sug{border-color:var(--good);color:var(--good);background:var(--good-soft)}
  /* Offered, but not yet answerable — it says on hover what it is waiting for. */
  .mns-btn.off{opacity:.55;cursor:default;font-weight:500}
  .mns-btn.off:hover{border-color:var(--line);color:var(--muted)}
  /* The other answers beside a ✦ suggestion (2026-09-14) — offered, not asked, so lighter than the
     buttons of an unanswered row: dashed, and one click replaces the suggestion. */
  .mns-alts{display:inline-flex;flex-wrap:wrap;gap:4px;margin-left:2px}
  /* The vertical rule between the three verb groups — work to do | awaiting | the closures. */
  .mns-sep{display:inline-block;width:1px;align-self:stretch;min-height:18px;margin:0 3px;background:var(--line)}
  .mns-btn.mns-alt{border-style:dashed;font-weight:500;padding:2px 8px}
  .mns-set{display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border-radius:99px;background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
  .mns-set .x{cursor:pointer;opacity:.6}.mns-set .x:hover{opacity:1}
  .tag.mns-pill{color:var(--accent-ink);background:var(--accent-soft);border-left:3px solid var(--accent)}
  .mtb-row.ready .mns-set,.mtb-row.ready .mns-btn.sug{color:var(--good);border-color:var(--good);background:var(--good-soft)}
  .mtb-row.attention .mns-set,.mtb-row.attention .mns-btn{border-color:var(--warn)}
  .mtb-row.attention .mns-set,.mtb-row.attention .mns-btn.sug{color:var(--warn);background:var(--warn-soft)}
  /* Filing COUNTS on a list row (2026-08-24) — "2 clients", "1 job". Quiet by design: settled
     filing should read as settled, leaving the orange warnings to carry the eye. Names and actions
     live in the reading pane's triage bar. */
  /* WHICH WAY THE LAST MESSAGE WENT, on a thread that has gone both ways. Two glyphs and two
     colours: an arrow alone is legible only once you have learned the convention, and a colour alone
     is no use to anyone who cannot separate these two hues. */
  .mail-dir{flex:none;display:inline-flex;align-items:center;justify-content:center;
    width:16px;height:16px;border-radius:5px;font-size:11px;line-height:1;font-weight:700}
  /* Incoming is now on almost every row, so it is the QUIET one: two hundred saturated chips would
     drown the list they are meant to help you read. Outgoing is the exception and the thing you are
     scanning for, so it carries the accent. Different hues, different arrows, words on hover. */
  /* Leading the line, so every sender name starts at the same x. align-self keeps the chip off the
     baseline the rest of the row sits on, which would otherwise drop it a pixel or two. */
  .mail-dir{align-self:center}
  /* A draft: holds the column, says nothing. */
  .mail-dir.none{background:transparent}
  .mail-dir.in{color:var(--bb);background:var(--bb-soft)}
  .mail-dir.out{color:var(--accent-ink);background:var(--accent-soft)}
  .tag.mail-count{color:var(--muted);background:var(--line-2)}
  /* The mention badge on an email that is not in your mailbox (mailSharedMentionRows). Accent, not
     the muted grey the counts use: it is the reason the row is in your list at all. The left edge
     marks the whole row as borrowed, and the spacer keeps the body aligned with rows that do have a
     checkbox. */
  /* The redaction review, and the AI compose dialog in front of it. */
  .mrv-modal{max-width:760px}
  .mrv-lead{font-size:12.5px;color:var(--muted);margin:0 0 10px}
  .mrv-chips{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px}
  .mrv-chip{font-size:11px;padding:3px 9px;border-radius:999px;cursor:pointer;font-family:inherit;
    border:1px solid var(--line);background:var(--panel-2);color:var(--muted)}
  .mrv-chip.on{background:var(--accent-soft);color:var(--accent-ink);border-color:var(--accent)}
  /* A redaction a person added reads differently from one a rule proposed — it is the part of the
     review that could not have happened without them. */
  .mrv-chip.manual{border-style:dashed;font-weight:600}
  .mrv-tools{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:8px}
  .mrv-count{font-size:11px;color:var(--faint);font-family:var(--font-num)}
  .mrv-body{font-size:11.5px;color:var(--muted);display:flex;align-items:center;gap:6px;margin-left:auto}
  /* Monospace, because this is a payload and not prose — it should not look like a nicely rendered
     email, it should look like the string it is. */
  .mrv-text{font-family:var(--font-mono,ui-monospace,monospace);font-size:11.5px;line-height:1.55;
    white-space:pre-wrap;word-break:break-word;background:var(--panel-2);border:1px solid var(--line);
    border-radius:var(--r-sm);padding:11px 13px;max-height:38vh;overflow:auto;margin:0;user-select:text}
  /* ARMED. A marking tool that looks the same as no tool is why the eraser read as doing nothing:
     you drag, you let go, and without the box itself changing there is nothing to tell you the
     gesture landed. The crosshair says the box is a target, the accent edge says which mode, and the
     selection colour is the accent so the drag itself is visibly the tool's colour and not the
     browser's ordinary blue. */
  .mrv-text.marking{border-color:var(--accent);cursor:crosshair}
  .mrv-text.marking::selection{background:var(--accent);color:#fff}
  .mrv-text.marking::-moz-selection{background:var(--accent);color:#fff}
  /* What just happened, in one line. It replaces the old silence after an action. */
  .mrv-hint{font-size:11.5px;color:var(--accent-ink);background:var(--accent-soft);
    border-radius:var(--r-sm);padding:5px 9px;margin-bottom:8px}
  .mrv-rw{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px;padding:8px 10px;
    border:1px solid var(--accent);border-radius:var(--r-sm);background:var(--accent-soft)}
  /* The quoted original is capped: a long drag must not push the input off the row. */
  .mrv-rw-q{font-size:11.5px;color:var(--accent-ink);max-width:260px;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  .mrv-rw .f{flex:1;min-width:180px}
  /* The batch summary: what this run IS, before any of the detail. */
  .mrv-sum{font-size:12px;color:var(--muted);background:var(--panel-2);border:1px solid var(--line-2);
    border-radius:var(--r-sm);padding:7px 10px;margin-bottom:10px}
  .mrv-sum b{color:var(--ink)}
  .mrv-sum-x{color:var(--warn)}
  /* Partially revealed: neither on nor off, and it must not be mistaken for either. */
  .mrv-chip.part{background:var(--warn-soft);color:var(--warn);border-color:var(--warn)}
  /* On a batch the payload IS the review, so it gets the height the chips used to eat. */
  .mrv-modal .mrv-text.batch{max-height:46vh}
  .mrv-none{font-size:12px;color:var(--warn);margin-bottom:10px}
  .mrv-foot{font-size:11px;color:var(--faint);flex:1;margin-right:12px}
  .mai-modal{max-width:520px}
  .mai-opts{display:flex;gap:12px}
  .mai-opts .am-f{flex:1}
  .mai-note{font-size:11.5px;color:var(--muted);margin:10px 0 0}
  .mai-after{font-size:11.5px;color:var(--muted);margin:6px 0;padding:7px 10px;border-radius:var(--r-sm);
    background:var(--panel-2);border:1px solid var(--line-2)}
  .tag.mail-shared{color:var(--accent);background:var(--accent-soft);font-weight:600}
  /* The watch list reads the other way round — somebody else owes this, so it is muted, not accent. */
  .tag.mail-waiting{color:var(--muted);background:var(--line-2)}
  /* A search hit on an earlier turn (mailRowIsEarlierTurn) — quiet, because it asks nothing of you. */
  .tag.mail-earlier{color:var(--muted);background:transparent;border:1px dashed var(--line)}
  .mail-row.shared{border-left:2px solid var(--accent)}
  .mail-row{position:relative}
  /* The dots sit in their own top line. They are taken out of flex flow so the sender, subject and
     preview keep the same left edge as before the indicator was added. */
  /* One dot, on the sender line — no strip above the row, so no line of height per row (2026-09-17).
     Orange: something is still to answer, and the tooltip names it. Green: nothing is. */
  .mail-row .mail-row-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--warn);
    display:inline-block;margin-right:2px}
  .mail-row .mail-row-dot.ready{background:var(--good)}
  /* Grey: the filing and triage state has not arrived yet (insight_proto_emailLinks is warm, round 5). */
  .mail-row .mail-row-dot.pending{background:var(--faint)}
  .mail-state-badge{font-size:10.5px;font-weight:700;padding:2px 6px;border-radius:6px;white-space:nowrap}
  .mail-state-badge.ready,.mail-state-badge.actioned{color:var(--good);background:var(--good-soft)}
  .mail-state-badge.attention{color:var(--warn);background:var(--warn-soft);border-left:3px solid var(--warn)}
  .mail-row .chk-spacer{display:inline-block;width:13px;flex:0 0 13px}
  .mbt-modal{max-width:760px;width:min(760px,calc(100vw - 32px))}
  .mbt-body{max-height:70vh;overflow:auto}
  .mbt-intro{margin:0 0 10px;color:var(--muted);font-size:12.5px}
  .mbt-auto{width:auto}
  .mbt-actions{display:flex;gap:8px;flex-wrap:wrap}
  .mbt-note{margin:7px 0 12px;color:var(--faint);font-size:11.5px}
  .mbt-grid{display:grid;gap:8px}
  .mbt-row{display:grid;grid-template-columns:110px minmax(0,1fr);gap:10px;align-items:center;padding:8px 10px;border:1px solid var(--line-2);border-radius:var(--r-sm);background:var(--panel-2);font-size:12px;font-weight:600}
  .mbt-row .f{min-width:0;width:100%}
  /* Quick filters sit in the rail among the folders and behave like them, so they inherit
     .mail-folder wholesale; only the count needs to lean on the accent, since a filter's count is a
     number of things waiting on you rather than a number of unread messages. */
  .mail-folder.mail-quick .n{color:var(--accent-ink)}
  .mail-folder.mail-quick.on .n{color:inherit}
  .mail-more{display:flex;align-items:center;gap:6px;padding:7px 9px;margin-top:6px;border-radius:var(--r-sm);color:var(--faint);font-size:12px;font-weight:600;cursor:pointer;user-select:none}
  .mail-more:hover{background:var(--line-2);color:var(--ink)}
  /* A queued-but-not-yet-created suggestion: context beside the action, never instead of it. */
  .mail-attn-note{opacity:.7;font-style:italic;margin-right:2px}
  /* ---- filing pill colours (js/mail.js mailClientPillKind) --------------------------------------
     Where an email was filed, readable at a glance without opening anything. Jobs already had their
     own colour (.tag.draft-pending, accent) and keep it; these three split what used to be one flat
     grey "client" pill into the three things it can actually be. Distinct hues rather than shades of
     one, because the point is telling them apart in peripheral vision:
       org         info   (blue)   — a company
       person      good   (green)  — an individual who is a client in their own right
       person-org  warm   (amber)  — an individual who is BOTH, i.e. also a contact at a company;
                                     the case that explains one email landing on two timelines
     Left border rather than a different background alone: the pills sit next to job pills and each
     other, and a bar reads as a category marker faster than a fill does. */
  .tag.pill-org{color:var(--info);background:var(--info-soft);border-left:3px solid var(--info)}
  .tag.pill-person{color:var(--good);background:var(--good-soft);border-left:3px solid var(--good)}
  .tag.pill-person-org{color:var(--warm);background:var(--warm-soft);border-left:3px solid var(--warm)}
  /* ⇄ the person a company was preferred over (2026-09-14) — OFFERED, not filed: dashed and quieter
     than the filed pills beside it, and a click switches the filing to them (mailSwitchToPersonClient). */
  .tag.pill-alt{color:var(--warm);background:transparent;border:1px dashed var(--warm);cursor:pointer}
  .tag.pill-alt:hover{background:var(--warm-soft)}
  /* …and ⇄ back to the company after a switch: the company's own colour, same offered look. */
  .tag.pill-alt.org{color:var(--info);border-color:var(--info)}
  .tag.pill-alt.org:hover{background:var(--info-soft)}
  /* CONTACT pills (2026-08-24) — a person, not a client, and deliberately quieter than the three
     client colours above: several contacts on an email is the normal case and must not read as a
     warning. Neutral ground, no left rule, with the type/role in the muted secondary colour. */
  .tag.pill-contact{color:var(--ink);background:var(--line-2);border:1px solid var(--line)}
  .tag.pill-contact .q{color:var(--muted);font-weight:400}
  /* Somebody on the email nobody holds (2026-09-14) — dashed, and kept together with its two answers
     (Add as contact / Ignore) so each party reads as one question. */
  /* One row per outside person on the Contact step, each with its own answers (2026-09-14). */
  .mtb-parties{display:flex;flex-direction:column;align-items:flex-start;gap:4px;flex-basis:100%}
  .mtb-party{display:flex;align-items:center;gap:4px;flex-wrap:wrap}
  .mtb-party-h{margin-top:2px}
  .tag.pill-unknown{color:var(--warn);background:transparent;border:1px dashed var(--warn)}
  .tag.pill-unknown .q{color:var(--muted);font-weight:400}
  /* On the thread, not on their timeline — removed by hand or filed before they were a contact. Orange:
     every participant belongs on their timeline (mailLeftOutPartyHtml, 2026-09-14). */
  .tag.pill-left-out{color:var(--warn);background:var(--warn-soft);border:1px solid var(--warn);cursor:pointer}
  .tag.pill-left-out .q{color:var(--warn);font-weight:400}
  /* The Senders directory (js/mail-senders.js, 2026-09-14). The table scrolls inside its own frame. */
  .snd-page{display:flex;flex-direction:column;gap:12px;padding:4px 2px}
  /* Team threads (js/mail-team-threads.js) — sits on the Senders page's own frame (.snd-page, chips). */
  .tt-list{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:var(--r);background:var(--panel);overflow:hidden}
  .tt-row{display:flex;align-items:center;gap:14px;padding:10px 14px;border-bottom:1px solid var(--line-2);cursor:pointer}
  .tt-row:last-child{border-bottom:none}
  .tt-row:hover{background:var(--panel-2)}
  .tt-main{flex:1;min-width:0}
  .tt-subj{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tt-client{font-weight:400;color:var(--muted);margin-left:6px}
  .tt-who{font-size:11.5px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tt-owner{flex:none;width:130px;font-size:11.5px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tt-none{color:var(--faint)}
  .tt-age{flex:none;width:120px;text-align:right;font-size:11.5px;color:var(--muted);font-family:var(--font-num)}
  .tt-age.waiting{color:var(--ink);font-weight:600}
  .tt-age.late{color:var(--crit)}
  .tt-empty{padding:18px 14px;font-size:12.5px;color:var(--faint);text-align:center}
  /* Organising the list (js/mail-team-threads.js): the Group by / narrow-to bar and the folding group headers. */
  .tt-organise{flex-wrap:wrap;gap:8px 14px;align-items:center;margin-top:-4px}
  .tt-organise .lnk{border:none;background:none;padding:0;font-size:11.5px;font-weight:600;color:var(--accent-ink);cursor:pointer}
  .tt-fold{margin-left:auto;font-size:11.5px;color:var(--muted)}
  .tt-showing{font-size:11.5px;color:var(--muted)}
  .tt-group{border-bottom:1px solid var(--line)}
  .tt-group:last-child{border-bottom:none}
  .tt-ghead{display:flex;align-items:baseline;gap:8px;width:100%;padding:8px 14px;border:none;border-bottom:1px solid var(--line-2);background:var(--panel-2);text-align:left;cursor:pointer;font:inherit}
  .tt-group.folded .tt-ghead{border-bottom:none}
  .tt-ghead:hover{background:var(--panel)}
  .tt-gchev{flex:none;width:12px;color:var(--muted);font-size:11px}
  .tt-gname{font-size:12.5px;font-weight:700;color:var(--ink);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .tt-gname.none{color:var(--muted);font-weight:600;font-style:italic}
  .tt-gcount{margin-left:auto;flex:none;font-size:11.5px;color:var(--muted);font-family:var(--font-num)}
  .tt-priv{border:1px solid var(--line);border-radius:var(--r);background:var(--panel-2);padding:10px 14px;font-size:12px}
  .tt-priv-h{font-weight:600;margin-bottom:6px}
  .tt-prow{display:flex;align-items:center;gap:8px;padding:3px 0;color:var(--ink)}
  .tt-prow .t{flex:1;min-width:0}
  .tt-prow .n{flex:none;color:var(--muted);font-family:var(--font-num);font-size:11px}
  .tt-priv-who{margin-top:8px;color:var(--muted);font-size:11.5px}
  /* Not filed yet (mail-unfiled.js) — who and when only; the bell asks the people on it to file it. */
  .tt-unf{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);padding:10px 14px;font-size:12px}
  .tt-unf.off{background:var(--panel-2)}
  .tt-unf-h{display:flex;align-items:center;gap:10px;font-weight:600;margin-bottom:6px}
  .tt-unf-h .lnk{margin-left:auto;border:none;background:none;padding:0;font-size:11.5px;font-weight:600;color:var(--accent-ink);cursor:pointer}
  .tt-unf-p{margin:0 0 8px;color:var(--muted);line-height:1.5}
  .tt-urow{display:flex;align-items:center;gap:12px;padding:6px 0;border-top:1px solid var(--line-2)}
  .tt-urow .t{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tt-urow .n{flex:none;color:var(--muted);font-family:var(--font-num);font-size:11px}
  .tt-urow .asked{flex:none;color:var(--muted);font-size:11px}
  .tt-urow .tt-age{width:auto}
  .tt-bell{flex:none;border:1px solid var(--line);background:var(--panel);border-radius:99px;padding:2px 8px;cursor:pointer;font-size:12px}
  .tt-bell:disabled{opacity:.5;cursor:default}
  /* Thread summaries (js/mail-thread-summary-ui.js) */
  .ts-note{margin:5px 14px 7px;padding:7px 10px;border-left:3px solid var(--accent);background:var(--accent-soft);border-radius:0 7px 7px 0;font-size:12px;line-height:1.5;color:var(--ink)}
  .ts-note.compact{margin:5px 0 0;font-size:11.5px}
  .ts-tag{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink);margin-right:4px}
  .ts-out{display:block;margin-top:2px;color:var(--ink)}.ts-out b{color:var(--muted);font-weight:600}
  .ts-meta{display:block;margin-top:2px;font-size:10.5px;color:var(--faint)}
  .tsg{margin:6px 0 0;padding:6px 10px;border-left:3px solid var(--warm);background:var(--warm-soft);border-radius:0 7px 7px 0;font-size:12px;line-height:1.5;color:var(--ink);display:flex;flex-wrap:wrap;gap:4px 8px;align-items:center}
  .tsg-tag{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--warm)}
  .tsg-stale{color:var(--muted);font-size:11px}.tsg-acts{margin-left:auto;display:inline-flex;gap:6px;align-items:center}.tsg-acts .btn{padding:3px 9px;font-size:11px}.tsg-done{font-size:11px;color:var(--muted)}
  .tt-sort{display:inline-flex;align-items:center;gap:6px;margin-left:auto;font-size:12px;color:var(--muted)}.tt-sort .f{padding:3px 8px;font-size:12px}
  /* Team threads → Assistant suggestions (js/mail-team-triage.js) — the AI triage tab's look (.ait-*), plus these. */
  .tta{margin:12px 0 14px}
  .tta .tta-sum{padding:8px 15px 10px}
  .tta-tools{display:flex;flex-wrap:wrap;gap:8px 14px;padding:4px 15px 8px}
  .tta-acts{display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:0 15px 10px}
  .tta-dec{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);margin-left:auto}
  .tta-ro{margin:0 15px 10px;padding:7px 10px;border-left:3px solid var(--warn);background:var(--warn-soft);border-radius:0 7px 7px 0;font-size:12px}
  .ait-row.tta-row{grid-template-columns:auto minmax(0,1fr) auto;cursor:default}
  .tta-l{padding-top:2px}
  .tta-conf{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;border-radius:99px;padding:1px 7px;background:var(--line-2);color:var(--muted)}
  .tta-conf.high{background:var(--good-soft);color:var(--good)}.tta-conf.medium{background:var(--info-soft);color:var(--info)}.tta-conf.tta-owb{background:var(--crit-soft);color:var(--crit)}
  .ait-chip.tta-ow{border-color:transparent;background:var(--crit-soft);color:var(--crit)}
  .tta-ev{margin-top:6px;font-size:12px}.tta-ev blockquote{margin:4px 0;padding:4px 10px;border-left:3px solid var(--line);color:var(--ink);font-style:italic}
  .tta-batches{margin:8px 15px 12px;font-size:12px}.tta-batches summary{cursor:pointer;color:var(--muted)}
  .tta-batch{display:flex;gap:10px;align-items:flex-start;justify-content:space-between;padding:6px 0;border-top:1px solid var(--line-2)}
  .tta-moved{color:var(--muted);font-size:11.5px;margin-top:2px}
  .am-modal.tta-modal{width:min(720px,95vw)}
  .tta-modal .tta-p{margin:0;font-size:13px}.tta-modal .tta-sec{font-size:12.5px}.tta-modal .tta-sec ul{margin:4px 0 0;padding-left:18px}
  .tta-modal .tta-owsec{padding:8px 10px;border-left:3px solid var(--crit);background:var(--crit-soft);border-radius:0 7px 7px 0}
  /* Team threads → Suggested rules (js/mail-team-rules.js) — the same rows, with the rule's own lines. */
  .trs-g .trs-how{padding:0 15px 8px;font-size:12px}
  .ait-row.trs-row{cursor:default}
  .trs-plain{margin-top:3px;font-size:12.5px;color:var(--ink);font-family:var(--font-mono,inherit)}
  .trs-counts{margin-top:5px;font-size:12px;color:var(--ink)}.trs-counts b{font-family:var(--font-num);font-variant-numeric:tabular-nums}
  .trs-ex{margin-top:5px;font-size:12px}.trs-ex ul{margin:2px 0 0;padding-left:18px}
  .trs-row .tta-ro{margin:6px 0 0}
  .rp-draft-note{margin:0 0 8px;padding:6px 10px;border-left:3px solid var(--accent);background:var(--accent-soft);border-radius:0 7px 7px 0}
  .snd-head h2{margin:0 0 4px;font-size:17px}
  .snd-head p{margin:0;font-size:12.5px;color:var(--muted);max-width:760px}
  .snd-tools{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
  .snd-tools .f{max-width:320px}
  .snd-chips{display:inline-flex;gap:6px;flex-wrap:wrap}
  .snd-chip{border:1px solid var(--line);background:var(--panel);color:var(--muted);font:inherit;font-size:12px;font-weight:600;padding:3px 10px;border-radius:99px;cursor:pointer}
  .snd-chip span{font-family:var(--font-num);opacity:.75;margin-left:3px}
  .snd-chip.on{border-color:var(--accent);color:var(--accent-ink);background:var(--accent-soft)}
  .snd-wrap{overflow:auto;max-height:70vh;border:1px solid var(--line);border-radius:10px;background:var(--panel)}
  .snd-table{width:100%;border-collapse:collapse;font-size:12.5px}
  .snd-table th{text-align:left;font-size:11px;color:var(--muted);font-weight:600;padding:8px 10px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--panel)}
  .snd-table td{padding:7px 10px;border-bottom:1px solid var(--line-2);vertical-align:middle}
  .snd-who{font-weight:600;color:var(--ink)}
  .snd-addr{font-size:11.5px;color:var(--muted)}
  .snd-when,.snd-num{font-family:var(--font-num);color:var(--muted);white-space:nowrap}
  .snd-acts{white-space:nowrap;text-align:right}
  .snd-acts .btn{font-size:11.5px;padding:3px 9px;margin-left:4px}
  .snd-st{display:inline-block;font-size:11px;font-weight:650;padding:2px 8px;border-radius:99px;background:var(--line-2);color:var(--muted)}
  .snd-st.unknown{color:var(--warn);background:var(--warn-soft)}
  .snd-st.contact,.snd-st.known{color:var(--good);background:var(--good-soft)}
  .snd-empty{color:var(--muted);text-align:center;padding:18px}
  .snd-foot{font-size:11.5px;color:var(--muted)}
  /* Filing pickers: the shared .mail-ac dropdown, re-anchored to its own input (its default offsets
     were tuned for the compose recipient rows). */
  .mail-pick{position:relative;display:inline-block}
  .mail-pick .mail-pick-dd{left:0;right:auto;top:100%;min-width:280px;max-width:380px;max-height:240px}
  .mail-pick .mail-ac-i input{margin-right:4px}
  /* Email timelines (client drawer Timeline tab; job drawer + dedicated pages later) */
  .mail-tl{display:flex;flex-direction:column}
  /* One block per conversation (2026-09-17). The messages inside stay flattened — every turn, in and
     out — and the header carries what belongs to the whole thread rather than to any one turn. */
  .mail-tl-thread{border-bottom:1px solid var(--line);margin-bottom:2px}
  .mail-tl-thread:last-child{border-bottom:none}
  .mail-tl-thead{display:flex;align-items:baseline;gap:8px;padding:9px 14px 6px;background:var(--panel-2)}
  .mail-tl-tsubj{font-size:12.5px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mail-tl-tn{flex:none;font-size:10.5px;color:var(--faint);font-family:var(--font-num)}
  .mail-tl-tjobs{flex:none;display:flex;gap:4px}
  .mail-tl-twhen{margin-left:auto;flex:none;font-size:10.5px;color:var(--faint);white-space:nowrap;font-family:var(--font-num)}
  /* The last turn of a thread carries no rule of its own — the block's own border closes it. */
  .mail-tl-thread .mail-tl-row:last-child{border-bottom:none}
  .mail-tl-row{display:flex;gap:10px;padding:10px 14px;border-bottom:1px solid var(--line-2);cursor:pointer;align-items:flex-start}
  .mail-tl-row:hover{background:var(--panel-2)}
  .mail-tl-dir{flex:none;width:22px;height:22px;border-radius:50%;display:grid;place-items:center;font-size:12px;font-weight:700;margin-top:2px}
  .mail-tl-dir.in{background:var(--accent-soft);color:var(--accent-ink)}
  .mail-tl-dir.out{background:var(--line-2);color:var(--muted)}
  .mail-tl-main{min-width:0;flex:1}
  .mail-tl-top{display:flex;align-items:baseline;gap:8px}
  .mail-tl-who{font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mail-tl-when{margin-left:auto;font-size:10.5px;color:var(--faint);white-space:nowrap;font-family:var(--font-num)}
  .mail-tl-subj{font-size:12.5px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mail-tl-prev{font-size:11.5px;color:var(--faint);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mail-tl-cmt{font-size:11px;color:var(--accent-ink);font-weight:600;margin-left:4px}
  /* Email comments — stored on the link doc, rendered below the email body in the reading pane */
  .mail-comment-section{border-top:1px solid var(--line-2);margin-top:20px;padding-top:16px}
  .mail-comment-thread{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}
  /* One card per comment thread (2026-08-25). Most emails will only ever have one, so a single card
     has to look ordinary rather than like scaffolding — the structure only earns its keep when a
     conversation genuinely has two questions running at once. */
  .mail-comment-card{border:1px solid var(--line-2);border-radius:9px;background:var(--panel);padding:8px 10px}
  .mail-comment-card.targeted{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}
  .mail-comment-card.done{background:var(--panel-2);opacity:.9}
  .mct-head{display:flex;align-items:center;gap:8px;font-size:12px}
  .mct-dot{flex:none;font-size:8px;color:var(--warn)}
  .mct-dot.done{font-size:11px;color:var(--good)}
  .mct-dot.you{color:var(--accent)}
  /* "Somebody is waiting on YOU" — on the folded header so it is visible without opening, and on the
     card so opening answers "which thread?" straight away. */
  .mct-you{flex:none;font-size:10px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;
    color:var(--accent-ink);background:var(--accent-soft);border-radius:9px;padding:1px 7px}
  .mail-comment-card.wants-me{border-color:var(--accent-soft);box-shadow:inset 3px 0 0 var(--accent)}
  /* Thread owner (2026-09-14): who it waits on, FYI, or — on an older thread — nobody yet. */
  .mct-owner{flex:none;border:1px solid var(--accent-soft);background:var(--accent-soft);color:var(--accent-ink);border-radius:9px;
    padding:0 7px;font:inherit;font-size:10.5px;font-weight:650;line-height:17px;cursor:pointer;white-space:nowrap}
  .mct-owner.fyi{background:var(--panel-2);border-color:var(--line-2);color:var(--muted)}
  .mct-owner.none{background:transparent;border-style:dashed;border-color:var(--warn);color:var(--warn)}
  .mct-owner-pick{flex:none;font-size:11px;padding:1px 4px;max-width:200px}
  /* A new thread's "For … / FYI" line, under the comment box. */
  .mail-comment-for{flex:none;display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;font-size:11.5px;color:var(--muted)}
  .mail-comment-for select{max-width:260px;font-size:12px;padding:3px 6px}
  .mail-comment-for.is-fyi select{opacity:.5}
  .mcf-fyi{display:inline-flex;align-items:center;gap:5px;cursor:pointer}
  .mail-comment-err{flex-basis:100%;color:var(--crit);font-size:11.5px}
  .mct-title{flex:1;min-width:0;font-weight:650;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  /* The title is the rename control. Underlined only on hover so the card stays quiet until you go
     looking — a dotted underline on every thread name would read as an error. */
  .mct-title.can-rename{cursor:text;border-bottom:1px dashed transparent}
  .mct-title.can-rename:hover{border-bottom-color:var(--line)}
  .mct-rename{flex:1;min-width:0;font-size:12px;font-weight:650;padding:2px 6px}
  .mct-n{flex:none;font-family:var(--font-num);font-size:10.5px;color:var(--muted);background:var(--panel-2);
    border:1px solid var(--line-2);border-radius:9px;padding:0 5px;line-height:15px}
  .mct-btn{flex:none;border:1px solid var(--line);background:var(--panel);border-radius:7px;cursor:pointer;
    padding:2px 8px;font:inherit;font-size:11px;color:var(--muted)}
  .mct-btn:hover{color:var(--ink);border-color:var(--muted)}
  .mct-btn.accent{color:var(--accent-ink);border-color:var(--accent-soft);background:var(--accent-soft)}
  .mct-btn[disabled]{opacity:.45;cursor:default}
  .mct-res{margin-top:6px;font-size:11.5px;color:var(--ink)}
  /* The header line: how many separate things are going on, and the one way to start another. */
  .mct-bar{display:flex;align-items:center;gap:8px;padding:0 2px 2px}
  .mct-count{flex:1;min-width:0;font-size:11px;font-weight:650;letter-spacing:.03em;text-transform:uppercase;color:var(--faint)}
  button.mct-count{border:0;background:none;padding:0;cursor:pointer;text-align:left;font-family:inherit}
  button.mct-count:hover{color:var(--ink)}
  /* Unread, the first time the area is opened — the card that holds it, and the comment itself. */
  .mail-comment-card.has-new{border-color:var(--accent)}
  .mail-comment-row.unread .mail-comment-body{background:color-mix(in srgb,var(--accent) 12%,var(--panel));box-shadow:inset 3px 0 0 var(--accent)}
  .mct-prev-text.is-new{font-weight:650}
  /* The collapsed preview — enough to know whether the thread needs you, not the thread itself. */
  .mct-prev{margin-top:6px;padding-left:16px;border-left:2px solid var(--line-2)}
  .mct-prev-row{display:flex;align-items:baseline;gap:8px;font-size:11px}
  .mct-prev-who{font-weight:650;color:var(--muted)}
  .mct-prev-when{margin-left:auto;font-family:var(--font-num);color:var(--faint);font-size:10.5px;white-space:nowrap}
  .mct-prev-text{font-size:12px;color:var(--ink);margin:1px 0 5px;overflow-wrap:anywhere}
  .mct-prev-more{font-size:10.5px;color:var(--faint);font-style:italic;margin:0 0 5px}
  .mail-comment-to{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--muted);padding:0 0 6px}
  .mail-comment-to b{color:var(--ink)}
  .mail-comment-row{display:flex;gap:8px;align-items:flex-start}
  .mail-comment-avatar{flex:none;width:26px;height:26px;border-radius:50%;background:var(--accent-soft);color:var(--accent-ink);font-size:11px;font-weight:700;display:grid;place-items:center;margin-top:1px}
  .mail-comment-body{flex:1;background:var(--panel-2);border-radius:9px;padding:8px 10px;min-width:0}
  .mail-comment-header{display:flex;align-items:baseline;gap:8px;margin-bottom:3px}
  .mail-comment-name{font-size:11.5px;font-weight:600}
  .mail-comment-when{margin-left:auto;font-size:10.5px;color:var(--faint);font-family:var(--font-num)}
  .mail-comment-edited{color:var(--faint);font-weight:400;cursor:help}
  .mail-comment-actions{display:flex;gap:8px;margin-left:6px}
  .mail-comment-actions button{border:0;background:none;padding:0;font-size:10.5px;color:var(--faint);font-weight:600;cursor:pointer}
  .mail-comment-actions button:hover{color:var(--accent-ink);text-decoration:underline}
  .mail-comment-row.deleted .mail-comment-body{background:transparent;border:1px dashed var(--line)}
  .mail-comment-tombstone{color:var(--faint);font-style:italic}
  .mail-comment-edit-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:6px}
  .mail-comment-text{font-size:12.5px;line-height:1.45}
  .mail-comment-text .at{color:var(--accent-ink);font-weight:600}
  .mail-comment-input-row{display:flex;gap:8px;align-items:flex-end;position:relative}
  .mail-comment-input-row.rv-col{flex-direction:column;align-items:stretch;gap:6px}
  .mail-comment-input-row.rv-col .btn{align-self:flex-start;min-width:64px}
  /* Live @-mention highlighting while typing (not just once posted): the backdrop div sits
     UNDER the textarea, showing the same text with mention spans given a visible background —
     its own base text stays color:transparent so it never double-renders under the textarea's
     real glyphs. Every font/padding/border/line-height value below must match the textarea's
     exactly or the two layers drift out of alignment. */
  .mail-comment-input-wrap{position:relative;flex:1;min-width:0}
  .mail-comment-backdrop{position:absolute;inset:0;z-index:0;box-sizing:border-box;border:1px solid transparent;border-radius:9px;font-size:12.5px;line-height:1.45;padding:8px 10px;font-family:var(--font-ui);white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:transparent;pointer-events:none}
  .mail-comment-backdrop .at{background:var(--accent-soft);border-radius:3px;color:transparent}
  .mail-comment-input-wrap textarea{position:relative;z-index:1;width:100%;resize:none;border:1px solid var(--line);border-radius:9px;background:transparent;color:var(--ink);font-size:12.5px;line-height:1.45;padding:8px 10px;font-family:var(--font-ui);min-height:38px;max-height:120px;overflow-y:auto;box-sizing:border-box}
  .mail-comment-input-wrap textarea:focus{outline:none;border-color:var(--accent)}
  .mail-at-dd{position:absolute;bottom:calc(100% + 4px);left:0;min-width:200px;background:var(--panel);border:1px solid var(--line);border-radius:9px;box-shadow:var(--shadow-lg);z-index:30;overflow:hidden;display:none}
  .mail-at-dd:not(:empty){display:block}
  .mail-at-item{padding:7px 11px;font-size:12.5px;cursor:pointer}
  .mail-at-item:hover{background:var(--panel-2)}
  /* Document register — attachments discovered across filed emails (client/job Documents tabs) */
  .mail-docs{display:flex;flex-direction:column}
  .mail-doc-group{display:flex;align-items:center;gap:7px;padding:9px 14px 6px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--faint);background:var(--panel-2);border-bottom:1px solid var(--line-2)}
  .mail-docs-count{font-size:11.5px;color:var(--faint);padding:2px 14px 8px}
  .mail-doc-job{display:inline-block;max-width:55%;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:var(--line-2);color:var(--muted);border-radius:20px;padding:0 7px;font-size:10.5px;font-weight:600}
  .mail-doc-count{font-family:var(--font-num);font-weight:600;letter-spacing:0;background:var(--line-2);color:var(--muted);border-radius:20px;padding:1px 7px;font-size:10.5px}
  .mail-doc-row{display:flex;gap:10px;padding:9px 14px;border-bottom:1px solid var(--line-2);align-items:center;cursor:pointer}
  .mail-doc-row:hover{background:var(--panel-2)}
  .mail-doc-icon{flex:none;font-size:16px;line-height:1}
  .mail-doc-main{min-width:0;flex:1}
  .mail-doc-name{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mail-doc-size{font-weight:500;color:var(--faint);font-size:11px;font-family:var(--font-num)}
  .mail-doc-meta{font-size:11.5px;color:var(--faint);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mail-doc-row .mail-doc-dl{flex:none;padding:4px 10px;font-size:11.5px}
  /* Insights — responsiveness analytics (Triage 4th tab, js/mail-insights.js) */
  .mi-head{display:flex;align-items:center;gap:10px;margin-bottom:14px}
  .mi-months{display:flex;gap:3px;background:var(--panel-2);border-radius:9px;padding:3px}
  .mi-months button{border:0;background:transparent;color:var(--muted);font-weight:600;font-size:12px;padding:5px 11px;border-radius:6px}
  .mi-months button.on{background:var(--panel);color:var(--ink);box-shadow:var(--shadow)}
  /* The FIRST button pushes the group to the right; the rest sit beside it. Every button used to get
     margin-left:auto, which was right while each header had one Refresh button — Triage has four now,
     and four auto margins split the free space between them and scattered them across the row. */
  .mi-head .btn{margin-left:auto}
  .mi-head .btn ~ .btn{margin-left:0}
  .mi-clickable{cursor:pointer;border-radius:var(--r);transition:box-shadow .12s}
  .mi-clickable:hover{box-shadow:0 0 0 2px var(--accent-soft)}
  .mi-roles{padding:4px 14px}
  .mi-role-row{display:flex;justify-content:space-between;padding:6px 0;font-size:12.5px;border-bottom:1px solid var(--line-2);cursor:pointer}
  .mi-role-row:last-child{border-bottom:0}
  .mi-role-row:hover{color:var(--accent-ink)}
  .mi-role-row.warn span{color:var(--warn)}
  .mi-drill-list{display:flex;flex-direction:column}
  .mi-drill-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--line-2)}
  .mi-drill-row:last-child{border-bottom:0}
  .mi-drill-main{min-width:0;flex:1}
  .mi-add-form{flex:1;display:flex;flex-direction:column;gap:6px;padding:8px 0}
  .mi-add-form .f{width:100%}
  .mi-row{display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid var(--line-2)}
  .mi-row:last-child{border-bottom:0}
  .mi-row-main{min-width:0;flex:1}
  .mi-row-main[data-mail-open-real]{cursor:pointer}
  .mi-row-main[data-mail-open-real]:hover .mi-row-who{color:var(--accent-ink)}
  .mi-row-who{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mi-row-subj{font-size:11.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mi-row-age{flex:none;font-family:var(--font-num);font-size:11.5px;color:var(--muted);font-weight:600}
  .mi-excl-list{margin-top:14px;background:var(--panel-2);border-radius:10px;padding:4px 14px}
  .mi-excl-h{font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--faint);padding:8px 0 4px}
  .mi-excl-row{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:7px 0;font-size:12px;color:var(--muted);border-top:1px solid var(--line-2)}
  .mi-foot{margin-top:14px;font-size:11px;color:var(--faint);line-height:1.5}
  .mi-info{display:inline-flex;vertical-align:-2px;margin-left:4px;color:var(--faint);cursor:help}
  .mi-info svg{fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
  .mi-info:hover,.mi-info:focus-visible{color:var(--accent)}
  .err{margin:0 0 10px;padding:9px 12px;border-radius:9px;background:var(--crit-soft);color:var(--crit);font-size:12px}

  /* Compose — chip recipients, Cc/Bcc toggle, attachments, rich text */
  .mc-row{display:flex;gap:8px;margin-bottom:8px;align-items:flex-start;position:relative}
  .mc-row label{font-size:11.5px;color:var(--faint);min-width:34px;padding-top:8px}
  .mail-recip{flex:1;display:flex;flex-wrap:wrap;gap:5px;align-items:center;border:1px solid var(--line);background:var(--panel);border-radius:9px;padding:5px 8px;min-height:34px}
  .mail-recip:focus-within{border-color:var(--accent)}
  .mail-recip.drag-over{border-color:var(--accent);border-style:dashed;background:var(--accent-soft)}
  .mail-recip .recip-input{flex:1;min-width:120px;border:0;background:transparent;outline:none;font-size:12.5px;color:var(--ink);padding:3px 2px}
  .mchip{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;background:var(--accent-soft);color:var(--accent-ink);border-radius:20px;padding:3px 5px 3px 10px;white-space:nowrap;cursor:grab}
  .mchip.dragging{opacity:.4}
  .mchip .x{cursor:pointer;opacity:.7;font-size:14px;line-height:1}.mchip .x:hover{opacity:1}
  .mc-ccbcc-toggle{display:flex;gap:8px;padding-top:8px}
  .mc-ccbcc-toggle .link{font-size:11.5px;color:var(--accent-ink);font-weight:600;cursor:pointer}
  .mail-ac{position:absolute;left:42px;right:0;top:100%;z-index:30;background:var(--panel);border:1px solid var(--line);border-radius:9px;box-shadow:var(--shadow-lg);max-height:180px;overflow-y:auto;margin-top:2px}
  .mail-ac-i{padding:7px 11px;font-size:12.5px;cursor:pointer;display:flex;gap:7px;align-items:baseline}
  .mail-ac-i:hover{background:var(--panel-2)}.mail-ac-i span{color:var(--faint);font-size:11px}
  /* THE SUGGESTIONS' OWN ANCHOR (2026-09-14). The host sits AFTER its row, so typing can repaint it alone
     (tests/mail-recipient-typing), and that took the dropdown out of the row it was positioned against:
     with no positioned parent, top:100% hung it off the bottom of the whole compose card, where
     overflow:hidden clipped it — measured 220px below the card. Reported as "absolutely no suggestion
     coming up at all". The host is the anchor now, directly under its own row, taking no space. */
  .mail-ac-host{position:relative;height:0}
  .mail-ac-host .mail-ac{top:-6px}
  .mail-ac-i.on{background:var(--accent-soft)}
  .mail-ac-i .mail-ac-n{font-weight:500}
  .mail-ac-i .mail-ac-k{margin-left:auto;padding-left:10px;font-style:normal;font-size:10.5px;color:var(--faint);white-space:nowrap}
  .mc-atts{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
  .mail-att-chip{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;background:var(--line-2);color:var(--muted);border-radius:20px;padding:4px 6px 4px 10px}
  /* A file that came with the message being forwarded, rather than one picked here. Marked, because
     a forward that quietly grew three paperclips otherwise reads as the app attaching things on your
     behalf. */
  .mail-att-chip.from-original{border-style:dashed;color:var(--muted)}
  .mail-att-chip .x{cursor:pointer;opacity:.7}.mail-att-chip .x:hover{opacity:1}
  /* The name opens the file (mailOpenComposeAttachment) — a button that looks like the chip's text. */
  .mail-att-chip .mca-open{all:unset;cursor:pointer;color:inherit}
  .mail-att-chip .mca-open:hover{color:var(--accent-ink);text-decoration:underline}
  .mail-att-chip .mca-open:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
  .mc-toolbar-row{margin-bottom:8px}
  .mail-quill{background:var(--panel);border-radius:9px;overflow:hidden}
  .mail-quill .ql-toolbar.ql-snow{border-color:var(--line);background:var(--panel-2);border-radius:9px 9px 0 0}
  .mail-quill .ql-container.ql-snow{border-color:var(--line);border-radius:0 0 9px 9px;font-size:13.5px;font-family:var(--font-ui)}
  /* No cap of its own (a signature with a logo image needs more than a fixed 220px, and capping it
     here on top of mail-compose-fields' own scroll was two nested scrollbars for one thing) — the
     card as a whole is what's bounded (see .mail-compose), with a single scroll region in fields. */
  /* The editable area is exactly as tall as its text (height:auto, not Quill's 100%), so the signature
     pill that wireCompose moves into this box sits right under the last line and follows it down as
     the message grows. The box stacks them; clicking its empty part still means "write here". */
  .mail-quill .ql-container{display:flex;flex-direction:column;height:auto;cursor:text}
  /* A SCROLLBAR IN THE DRAFT BOX (2026-09-17). Reported twice: "I no longer can see the bottom bits
     of my draft… I am still not getting any scroll bar inside the draft box at all." Bounding the
     CARD (2026-09-17, above) made the whole composer scrollable, which is not the same thing — the
     message you are writing is its own region, and on a long AI draft it has to be the one that
     scrolls, or the quoted thread and the footer are pushed off and every line costs an outer scroll.

     So the editable area is capped and scrolls itself. The cap is viewport-relative because the
     alternative is the fixed 220px this file used to carry, which was too small for a signature with
     a logo; at this size the outer region rarely scrolls at all, so there is one scrollbar on screen
     and it is in the box the caret is in. The signature pill still sits under the last line — it is
     the container's next child, outside the scrolling area, which is why it stays in view. */
  .mail-quill .ql-editor{min-height:90px;max-height:min(52vh,520px);color:var(--ink);flex:none;height:auto;overflow-y:auto}
  .mail-quill .ql-editor.ql-blank::before{color:var(--faint);font-style:normal}
  .mail-quill .ql-snow .ql-stroke{stroke:var(--muted)}.mail-quill .ql-snow .ql-fill{fill:var(--muted)}
  .mail-quill .ql-snow .ql-picker{color:var(--muted)}
  .mail-quill .ql-snow.ql-toolbar button:hover .ql-stroke,.mail-quill .ql-snow.ql-toolbar button.ql-active .ql-stroke{stroke:var(--accent-ink)}
  .mail-quill .ql-snow.ql-toolbar button:hover .ql-fill,.mail-quill .ql-snow.ql-toolbar button.ql-active .ql-fill{fill:var(--accent-ink)}

  /* Collapsible quoted-thread turns — sits below the Quill editor, never inside it */
  .mail-quote-thread{margin-top:14px;border-top:1px solid var(--line-2)}
  .mail-quote-turn{border-bottom:1px solid var(--line-2)}
  .mqt-head{display:flex;align-items:center;gap:8px;padding:9px 2px;cursor:pointer;font-size:12px;color:var(--muted)}
  .mqt-head:hover{color:var(--ink)}
  .mqt-chev{width:13px;height:13px;flex:none;stroke:currentColor;stroke-width:2;fill:none;transition:transform .15s}
  .mail-quote-turn.open .mqt-chev{transform:rotate(90deg)}
  .mqt-from{font-weight:600;color:var(--ink)}
  .mqt-to{color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mqt-cc{color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:.95em}
  /* The pane moved on to the conversation's latest turn — says so, with the way back to your copy. */
  .mail-latest-note{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;padding:6px 18px;font-size:12px;
    color:var(--muted);background:var(--panel-2);border-top:1px solid var(--line-2)}
  .mail-latest-note span{flex:1 1 240px;min-width:0}
  /* A colleague answered it without you (mailColleagueReplyOfferHtml) — directly under the note that
     says the pane moved on, in the same band, so it reads as the consequence of it. */
  .mail-colleague-replied{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;padding:7px 18px;font-size:12px;
    color:var(--ink);background:var(--accent-soft);border-top:1px solid var(--line-2)}
  .mail-colleague-replied .t{flex:1 1 240px;min-width:0}
  .mail-colleague-replied .btn.sm{flex:none}
  /* Attachments on a quoted turn. Sits before the date rather than after it, so a row that carries
     files reads as "who, what's attached, when" — the clip is the thing you are scanning for. */
  .mqt-clip{flex:none;border:1px solid var(--line-2);background:var(--panel-2);border-radius:999px;
    cursor:pointer;padding:0 7px;font:inherit;font-size:10.5px;line-height:16px;color:var(--muted)}
  .mqt-clip:hover{border-color:var(--muted);color:var(--ink)}
  /* Opening the original: an icon on the header line beside the sender and the date, not a button in
     the body. Quiet until the row is hovered, since it is an occasional reach rather than the point
     of the row. */
  .mqt-openbtn{flex:none;border:0;background:transparent;cursor:pointer;padding:2px 4px;border-radius:6px;
    color:var(--faint);display:inline-flex;opacity:0;transition:opacity .12s}
  .mqt-head:hover .mqt-openbtn,.mqt-openbtn:focus{opacity:1}
  .mqt-openbtn:hover{color:var(--ink);background:var(--panel-2)}
  .mqt-openbtn .ic{width:12px;height:12px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
  /* The quoted-copy variant reads as the lesser of the two on purpose: what it opens is a copy from
     somebody else's forward, not a message we hold. Dashed, so the difference is visible before you
     click rather than only in the tooltip. */
  .mqt-openbtn.quoted{border:1px dashed var(--line);opacity:.55}
  .mqt-head:hover .mqt-openbtn.quoted{opacity:1;border-color:var(--muted)}
  /* The window itself: header lines, then the caveat, then the text. */
  .mqv-wrap{padding:16px 18px}
  .mqv-head{display:flex;flex-direction:column;gap:3px;padding-bottom:10px;margin-bottom:10px;
    border-bottom:1px solid var(--line-2)}
  .mqv-line{display:flex;align-items:baseline;gap:8px;font-size:12.5px;color:var(--ink)}
  .mqv-lab{flex:none;width:38px;font-size:11px;color:var(--faint)}
  .mqv-who{font-weight:600}
  .mqv-addr{font-size:11.5px;color:var(--faint)}
  .mqv-when{font-family:var(--font-num);font-size:11.5px;color:var(--muted)}
  .mqv-asq{font-size:9.5px;font-weight:650;letter-spacing:.03em;text-transform:uppercase;
    color:var(--faint);border:1px solid var(--line-2);border-radius:4px;padding:1px 4px;margin-left:5px}
  /* Stated, not whispered: this window can do less than the one it looks like. */
  .mqv-note{font-size:11.5px;color:var(--muted);background:var(--panel-2);border:1px solid var(--line-2);
    border-radius:var(--r-sm);padding:8px 10px;margin-bottom:12px}
  .mqv-body{max-height:52vh;overflow:auto}
  /* Copyable. Deliberately quiet until you go near it — the addresses are on almost every row of a
     thread, and a permanent affordance on each would be louder than the names themselves. */
  .mail-party[data-mail-copy]{cursor:copy}
  .mail-party[data-mail-copy]:hover{text-decoration:underline dotted;text-underline-offset:2px}
  /* A COLLEAGUE, TOLD APART FROM A CLIENT AT A GLANCE (2026-09-14). First a brown underline; then
     (2026-09-15): "in some places that's a thin underline, some places a thick underline… best thing is
     we just show the name itself in a brown font… something that works with both dark and light modes."
     So the NAME is coloured, one rule everywhere a colleague is shown, with --staff tuned per theme; the
     title on each still says "a colleague" on hover, and a copyable one keeps the dotted copy cue above. */
  .mail-staff{color:var(--staff)}
  .mqv-foot{display:flex;align-items:center;gap:10px;margin-top:14px;padding-top:12px;
    border-top:1px solid var(--line-2)}
  .mqv-foot .mqv-addr{font-size:11.5px}
  .mqv-nono{font-size:11.5px;color:var(--faint)}
  .mqt-when{margin-left:auto;font-family:var(--font-num);color:var(--faint);white-space:nowrap;font-size:11px}
  /* A time we could not check against our own record, shown as the sender wrote it. Marked, because
     it is the one date on the row that may be in another timezone. */
  /* A turn we hold no record of. It used to be italic and faded, which read as "something is wrong
     with this row" rather than "we cannot vouch for this time" — reported as "why is this one styled
     differently?". The label says it instead, and the date is left looking like a date. */
  .mqt-when.as-quoted{opacity:.8}
  .mqt-asq{flex:none;margin-left:auto;font-size:9.5px;font-weight:650;letter-spacing:.03em;text-transform:uppercase;
    color:var(--faint);border:1px solid var(--line-2);border-radius:8px;padding:0 5px;line-height:14px;cursor:help}
  .mqt-asq + .mqt-when{margin-left:6px}
  .mqt-body{padding:2px 2px 12px 21px;font-size:13px;line-height:1.55;color:var(--ink)}
  /* The folded signature block (2026-08-25). The name is set in an italic serif so it reads as a
     sign-off rather than a button label — the point is that you can still see who signed the email
     even while the block itself is out of the way. */
  /* Two clear lines between the last line of the message and the signature control, always. Asked
     for as: "where none exists we give it two lines, and if there is more gap we still bring it down
     to a consistent gap." The second half is the harder one and is done in JS, not here —
     mailTrimTrailingBlanks drops the run of empty MsoNormal paragraphs Outlook leaves behind, so this
     margin is the ONLY thing setting the space rather than being added to whatever the sender's
     client happened to emit. 40px is two lines of the 13.5px/1.6 body text. */
  .mail-sigfold{margin:40px 0 10px}
  .msf-btn{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line-2);background:var(--panel-2);
    border-radius:999px;cursor:pointer;padding:3px 11px 3px 9px;color:var(--muted);font:inherit}
  .msf-btn:hover{border-color:var(--muted);color:var(--ink)}
  .msf-btn .ic{width:13px;height:13px;flex:none;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
  /* An actual hand, not italics pretending. Alex Brush (index.html) with a cursive fallback stack, so
     a blocked or slow font still reads as a signature rather than as body text — the whole point of
     the treatment is that the name looks SIGNED.

     Bigger than the serif it replaces because a brush face has a small x-height: at 12.5px it was a
     squiggle, and 18px matches the visual weight of the text around it. */
  .msf-name{font-family:var(--font-sig);font-size:var(--font-sig-size);line-height:1;color:var(--ink);padding-bottom:2px}
  .mail-sigfold.open .msf-btn{background:var(--panel);border-color:var(--line)}
  /* The composer's own copy of the fold — INSIDE the message box, under the text (2026-09-14), indented
     to line up with the editor's own 15px padding. Tight above: the three blank lines the message
     opens with are the gap. */
  .mail-sigfold.compose-sig{margin:0 15px 12px;cursor:auto}
  .compose-sig .msf-body{display:none}
  .compose-sig.open .msf-body{display:block}
  /* Says the quiet part on the pill itself: tucked here, sent in full. */
  .msf-note{font-size:10.5px;color:var(--faint);letter-spacing:.01em}
  .msf-edit{margin-top:9px}
  .msf-edit .btn{font-size:11px;padding:4px 9px}
  .msf-body{margin-top:8px;padding-left:11px;border-left:2px solid var(--line-2);font-size:12.5px;color:var(--muted)}
  .msf-body img{max-width:100%}
  /* The same turns in the READ view (2026-08-25). Given a heading and a little more air than the
     composer's version, because here they are a list you choose from rather than a tail below the
     thing you are writing. Rows sit on the panel so the collapsed history reads as one block. */
  .mail-quote-thread.read{margin-top:18px;border-top:1px solid var(--line);padding-top:4px}
  .mail-quote-thread.read .mqt-lab{display:flex;align-items:center;gap:8px;font-size:11px;font-weight:650;letter-spacing:.03em;text-transform:uppercase;color:var(--faint);padding:8px 2px 2px}
  /* Expand all / collapse all. Two distinct glyphs rather than one that flips — see the note in
     mailReadTurnsHtml. `.off` is the "this would change nothing" state: still there, still says what
     it does on hover, but plainly not the one to press. */
  .mqt-all{margin-left:auto;display:inline-flex;gap:2px}
  .mqt-allbtn{border:0;background:transparent;cursor:pointer;padding:3px 5px;border-radius:6px;color:var(--muted);display:inline-flex}
  .mqt-allbtn:hover{background:var(--panel-2);color:var(--ink)}
  .mqt-allbtn .ic{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
  /* Dimmed, not erased: at --line it read as a missing button rather than an idle one, which is a
     different message. Opacity keeps the shape legible so the pair still shows both choices. */
  .mqt-allbtn.off{opacity:.3;cursor:default}
  .mqt-allbtn.off:hover{background:transparent;color:var(--muted)}
  .mail-quote-thread.read .mqt-head{padding:8px 8px;border-radius:7px}
  .mail-quote-thread.read .mqt-head:hover{background:var(--panel-2)}
  .mail-quote-thread.read .mail-quote-turn:last-child{border-bottom:0}

  /* Admin → Practice setup: the client × service matrix, the staff rollup, the yearly staff load.
     A matrix cell has to read at a glance across 20 columns, so a person is an initial disc rather
     than a name, and "inherited from the client default" is carried by opacity — the same value, just
     not set on the service itself. */
  .ps-bar-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin:0 0 12px}
  .ps-bar-lab{font-size:11.5px;font-weight:600;color:var(--muted)}
  .ps-bar-hint{font-size:11.5px;color:var(--faint);flex:1;min-width:180px}
  .ps-ini{display:inline-flex;align-items:center;justify-content:center;width:21px;height:21px;border-radius:50%;
    font-size:9.5px;font-weight:700;color:#fff;letter-spacing:.02em;vertical-align:middle}
  .ps-ini.inherit{opacity:.42}
  .ps-none{color:var(--faint)}
  .ps-off{font-size:10.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.05em}
  /* ORG / PER — a two-letter marker beside the name, so the organisation/personal split is visible in
     the grid itself and not only in the Kind filter. */
  .ps-kind{font-size:9px;font-weight:700;letter-spacing:.06em;padding:1px 4px;border-radius:4px;
    background:var(--bb-soft);color:var(--bb);vertical-align:middle}
  .ps-kind.org{background:var(--info-soft);color:var(--info)}
  .ps-mix{display:inline-block;font-size:11px;color:var(--muted);background:var(--panel-2);border:1px solid var(--line-2);
    border-radius:5px;padding:1px 6px;margin:1px 4px 1px 0;white-space:nowrap}
  .ps-mix b{font-family:var(--font-num);color:var(--ink)}
  .ps-mix.more{color:var(--faint);cursor:default}
  .ps-prog{display:inline-flex;align-items:center;gap:7px}
  .ps-bar{display:inline-block;height:6px;width:64px;border-radius:3px;background:var(--line-2);overflow:hidden;vertical-align:middle}
  .ps-bar i{display:block;height:100%;background:var(--good);border-radius:3px}
  .ps-pct{font-family:var(--font-num);font-size:11px;color:var(--muted);min-width:30px;text-align:right}
  .ps-frac{font-family:var(--font-num);font-size:11.5px;color:var(--muted);white-space:nowrap}
  .ps-frac b{color:var(--ink)}
  .ps-frac.done,.ps-frac.done b{color:var(--good)}

  /* ---- Folded group in the bill editor (js/invoice.js) ----------------------------------------
     ONE header per fold, then its members indented beneath it. The banner used to repeat on every
     member card, so a 7-line fold said "prints as one line" seven times. */
  .lie-foldhead{border:1px solid var(--accent);border-bottom:0;border-radius:10px 10px 0 0;
    background:var(--accent-soft);padding:9px 12px;margin-bottom:0}
  /* Members hang off the header: square the top corners and share its left edge so the group reads
     as one block rather than as loose cards that happen to follow a banner. */
  .lie-foldmem{border-left:3px solid var(--accent)!important;border-radius:0 10px 10px 0!important;margin-bottom:4px!important}
  .lie-foldhead+.lie-foldmem{border-top-right-radius:0!important}

  /* ---- Bill editor selection bar (js/invoice.js lieBulkBarHtml) -------------------------------
     Sticky, so on a 25-line bill the actions stay reachable while you scroll to tick more lines —
     scrolling back to the top to act was the thing that made the old per-card buttons feel necessary. */
  .lie-bulk{position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:9px;flex-wrap:wrap;
    padding:9px 12px;margin:0 0 12px;border:1px solid var(--line);border-radius:10px;background:var(--panel-2)}
  .lie-bulk.on{border-color:var(--accent);background:var(--accent-soft)}
  .lie-bulk-n{font-size:12px;font-weight:700;color:var(--muted);white-space:nowrap}
  .lie-bulk.on .lie-bulk-n{color:var(--accent-ink)}
  .lie-bulk-acts{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
  /* Disabled stays legible rather than ghosted — the tooltip on it is the explanation of why the
     action does not apply, and nobody hovers something they read as absent. */
  .lie-bulk .btn[disabled]{opacity:.5;cursor:not-allowed}
  .lie-bulk .lie-bulk-wo:not([disabled]):hover,.lie-bulk .lie-bulk-remove:not([disabled]):hover{border-color:var(--crit);color:var(--crit)}

  /* ---- Invoice / credit-note preview action bar (js/invoice.js) -------------------------------
     Its own row under the header. These used to share the header with the title, page count and zoom,
     and on a voided invoice with a payment link that came to nine text buttons wrapping over two
     lines. Grouped by what the action does to the document — work on it · send it · reverse it —
     with a hairline between groups so the eye can find the group before the button. */
  .inv-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:8px 20px;border-bottom:1px solid var(--line);background:var(--panel-2)}
  .inv-actg{display:inline-flex;align-items:center;gap:4px}
  .inv-actg+.inv-actg{padding-left:14px;border-left:1px solid var(--line)}
  .inv-actions .icon-btn{width:30px;height:30px;border-radius:8px}
  .inv-actions .icon-btn .ic{width:15px;height:15px}
  .inv-actions .icon-btn:hover{background:var(--panel);border-color:var(--accent);color:var(--accent-ink)}
  /* Reversing a document is not the same class of act as printing it — the void/credit group reads
     in the critical colour on hover so a mis-aimed click has a moment to register. */
  .inv-actg:last-child .inv-void:hover,.inv-actg:last-child .inv-credit:hover{border-color:var(--crit);color:var(--crit)}
  .inv-zoomrange{height:4px;-webkit-appearance:none;appearance:none;background:var(--line);border-radius:2px;outline:none}
  .inv-zoomrange::-webkit-slider-thumb{-webkit-appearance:none;width:13px;height:13px;border-radius:50%;background:var(--accent);cursor:pointer}
  .inv-zoomrange::-moz-range-thumb{width:13px;height:13px;border:0;border-radius:50%;background:var(--accent);cursor:pointer}

  /* ---- Client grading (js/client-grades.js) — the drawer scorecard ---------------------------- */
  .cg-pane{padding:2px 0}
  .cg-head{display:flex;gap:16px;align-items:center;padding:14px 15px;border:1px solid var(--line);border-radius:12px;background:var(--panel-2);margin-bottom:12px}
  .cg-badge{width:64px;height:64px;border-radius:14px;display:grid;place-items:center;font-size:24px;font-weight:800;letter-spacing:-.02em;color:#fff;background:var(--bb);flex:none}
  .cg-badge.good{background:var(--good)}.cg-badge.info{background:var(--info)}
  .cg-badge.warn{background:var(--warn)}.cg-badge.crit{background:var(--crit)}
  .cg-head-b{min-width:0}
  .cg-score{font-family:var(--font-num);font-size:26px;font-weight:800;line-height:1.1;color:var(--ink)}
  .cg-pcm{font-size:15px;font-weight:600;color:var(--faint);margin-left:2px}
  .cg-meta{font-size:12px;color:var(--muted);margin-top:3px}
  .cg-stale{display:flex;align-items:center;gap:8px;margin-top:7px;font-size:12px;color:var(--warn)}
  .cg-caps{border:1px solid var(--warn);background:var(--warn-soft);border-radius:11px;padding:12px 15px;margin-bottom:12px;font-size:12.5px;color:var(--ink)}
  .cg-caps ul{margin:7px 0 0;padding-left:19px}.cg-caps li{margin:3px 0;color:var(--muted)}
  .cg-actions{display:flex;align-items:center;gap:12px;margin-bottom:16px}
  .cg-grp{margin-bottom:18px}
  .cg-grp-h{display:flex;align-items:center;gap:10px;padding:0 2px 8px;border-bottom:1px solid var(--line-2);margin-bottom:8px}
  .cg-grp-h h4{font-size:13px;font-weight:700;margin:0}
  .cg-grp-p{font-family:var(--font-num);font-size:12px;color:var(--muted);margin-left:auto}
  .cg-grp-bar{display:inline-block;height:6px;width:72px;border-radius:3px;background:var(--line-2);overflow:hidden}
  .cg-grp-bar i{display:block;height:100%;background:var(--good);border-radius:3px}
  .cg-row{display:flex;align-items:flex-start;gap:11px;padding:9px 4px;border-bottom:1px solid var(--line-2)}
  .cg-row:last-child{border-bottom:0}
  .cg-dot{width:9px;height:9px;border-radius:50%;flex:none;margin-top:5px;background:var(--bb)}
  .cg-dot.good{background:var(--good)}.cg-dot.info{background:var(--info)}
  .cg-dot.warn{background:var(--warn)}.cg-dot.crit{background:var(--crit)}
  .cg-dot.none{background:transparent;border:1.5px dashed var(--faint)}
  .cg-lab{flex:1;min-width:0}.cg-lab b{font-size:12.5px}
  .cg-hint{font-size:11.5px;color:var(--faint);margin-top:2px;line-height:1.45}
  .cg-detail{font-size:11.5px;color:var(--muted);margin-top:3px}
  .cg-val{text-align:right;font-size:12px;color:var(--ink);min-width:140px;flex:none}
  .cg-unans{color:var(--faint);font-style:italic}
  .cg-src{display:block;font-size:10px;letter-spacing:.05em;text-transform:uppercase;margin-top:3px;color:var(--faint)}
  .cg-src.ovr{color:var(--warn)}.cg-src.ev{color:var(--info)}.cg-src.ans{color:var(--accent-ink)}
  /* What survives of the old embedded incident list: the "Risk on the record" summary card, which is
     all this tab shows now — the records themselves live on Record (js/client-records-ui.js). The
     per-incident row classes (.old / -d / -a / -b / -del) went with the renderer that used them. */
  .cg-inc{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px;background:var(--panel)}
  .cg-inc-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
  .cg-inc-n{font-size:12px;color:var(--muted);margin-top:6px;line-height:1.5}
  .cg-note{font-size:12.5px;color:var(--muted);line-height:1.6;padding:2px}
  .cg-empty{font-size:12px;color:var(--faint);padding:8px 2px}
  .cg-hist{display:flex;align-items:center;gap:11px;padding:6px 2px;border-bottom:1px solid var(--line-2);font-size:12px;color:var(--muted)}
  .cg-hist:last-child{border-bottom:0}

  /* ---- Client records (js/client-records-ui.js) — the drawer's Record tab ----------------------
     Reuses the grading pane's group/heading/hint/empty rhythm above deliberately: the two tabs sit
     next to each other on the same drawer and a second visual language between them would read as
     two different apps. Only the row and the alarm band are new shapes. */
  .cr-pane{padding:2px 0}
  .cr-grp{margin-bottom:18px}
  .cr-grp-h{display:flex;align-items:center;gap:10px;padding:0 2px 8px;border-bottom:1px solid var(--line-2);margin-bottom:8px}
  .cr-grp-h h4{font-size:13px;font-weight:700;margin:0}
  .cr-hint{font-size:11.5px;color:var(--faint);margin:0 0 10px;line-height:1.45}
  .cr-empty{font-size:12px;color:var(--faint);padding:8px 2px}
  .cr-row{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px;background:var(--panel);cursor:pointer}
  .cr-row:hover{background:var(--panel-2)}
  /* The escalated tier, and the second axis on top of it. A complaint has to read differently from a
     note-to-self at a glance or the tiers are decoration: `.escalated` takes a crit left bar and a
     heavier title, and `.outstanding` deepens it — an escalated record nobody has dealt with is the
     loudest row on the tab. An outstanding LESSON is warm, not critical: it is a reminder, not a
     complaint, and making both scream leaves nothing to escalate to. */
  .cr-row.escalated{border-left:3px solid var(--crit);border-color:var(--crit) var(--line) var(--line) var(--crit)}
  .cr-row.escalated .cr-title{font-weight:700}
  .cr-row.escalated.outstanding{background:var(--crit-soft)}
  .cr-row.escalated.outstanding .cr-ico{background:var(--crit);color:#fff}
  .cr-row.outstanding:not(.escalated){border-left:3px solid var(--warn)}
  /* "Insight asked and did not get an answer." Distinct from empty and from loading, because a
     reader has to be able to tell "nothing has happened here" from "we could not check". */
  .cr-stalled{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:9px 13px;margin-bottom:14px;
    border:1px solid var(--warn);background:var(--warn-soft);border-radius:10px;font-size:12.5px;color:var(--ink)}
  /* One line at the top of the tab: what is still owed on this client, before anybody scrolls. */
  .cr-outstanding{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 13px;margin-bottom:14px;
    border:1px solid var(--warn);background:var(--warn-soft);border-radius:10px;font-size:12.5px;color:var(--ink)}
  .cr-row-h{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
  .cr-ico{width:18px;height:18px;border-radius:5px;display:grid;place-items:center;font-size:11px;flex:none;background:var(--line-2);color:var(--muted)}
  .cr-title{font-size:12.5px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:38ch}
  .cr-when{font-family:var(--font-num);font-size:11.5px;color:var(--muted);margin-left:auto;flex:none}
  .cr-meta{font-size:11.5px;color:var(--muted);margin-top:5px;line-height:1.5}
  .cr-body{font-size:12px;color:var(--muted);margin-top:6px;line-height:1.55;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .cr-cred{font-size:11.5px;color:var(--accent-ink);margin-top:5px}
  .cr-foot{font-size:11px;color:var(--faint);margin-top:6px}
  .cr-res{font-size:12px;color:var(--muted);margin-top:6px;padding-top:6px;border-top:1px solid var(--line-2);line-height:1.5}
  /* The un-dismissable band. Loud on purpose, and it carries no close control of any kind — see the
     header of js/client-records-ui.js. */
  .cr-alarm{border:1px solid var(--crit);background:var(--crit-soft);border-radius:11px;padding:12px 15px;margin-bottom:16px}
  .cr-alarm-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:12.5px;color:var(--ink)}
  .cr-alarm .cr-hint{margin:7px 0 9px}
  .cr-alarm-row{display:flex;align-items:center;gap:11px;padding:8px 0;border-top:1px solid var(--line-2)}
  .cr-fo-grp{border:1px solid var(--line);border-radius:10px;margin-bottom:8px;background:var(--panel);overflow:hidden}
  .cr-fo-h{display:flex;align-items:center;gap:9px;padding:10px 12px;cursor:pointer;flex-wrap:wrap}
  .cr-fo-h:hover{background:var(--panel-2)}
  .cr-fo-caret{color:var(--faint);font-size:10px;width:10px;flex:none}
  .cr-fo-grp .cr-fo{padding:9px 12px;border-top:1px solid var(--line-2);border-bottom:0}
  .cr-fo-grp .cr-empty{padding:8px 12px}
  .cr-fo{display:flex;align-items:flex-start;gap:11px;padding:9px 4px;border-bottom:1px solid var(--line-2)}
  .cr-fo:last-child{border-bottom:0}
  .cr-fo .hdot{margin-top:5px;flex:none}
  .cr-fo.late .cr-title{color:var(--crit)}
  /* A period where process-guide and the recorded answer disagree. Warm, not critical: nothing is
     wrong with the filing, something is wrong with the record of it (js/filing-outcomes.js rule 1). */
  .cr-fo.clash{background:var(--warn-soft)}
  .cr-pg-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:9px 12px;margin-top:4px;
    border-top:1px solid var(--line-2);font-size:11.5px;color:var(--faint);line-height:1.45}
  .cr-read-s{margin-bottom:12px}
  .cr-read-s b{font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--faint)}
  .cr-read-s p{margin:4px 0 0;font-size:12.5px;color:var(--ink);line-height:1.6;white-space:pre-wrap}
  .cr-acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}

  /* ---- Revenue demand notices (js/revenue-demands-ui.js) --------------------------------------
     Shares the register's row rhythm on purpose — they sit on the same tab — with one difference:
     an OPEN notice inside its window is warm, and one past day 4 (.hot) is critical, because that
     is the moment the primary owner and the partners start getting copied. */

  /* ---- an edited staff update: the marker, its chevron, and the versions underneath ----
     Quiet by design. "edited" is a fact about the entry, not a warning about it — somebody correcting
     their own typo should not leave a row that looks disputed. */
  .su-ft{display:flex;align-items:center;gap:5px;flex-wrap:wrap}
  .su-ft-r{margin-left:auto}
  .su-edited{border:0;background:none;padding:0 0 0 6px;cursor:pointer;font:inherit;font-size:11px;
    color:var(--faint);display:inline-flex;align-items:center;gap:3px}
  .su-edited:hover{color:var(--ink)}
  .su-chev{display:inline-block;transition:transform .12s ease;font-size:13px;line-height:1}
  .su-edited.on .su-chev{transform:rotate(90deg)}
  .su-edit{border:1px solid var(--line);background:var(--panel);border-radius:7px;padding:2px 8px;
    font-size:11px;color:var(--muted);cursor:pointer}
  .su-edit:hover{border-color:var(--accent);color:var(--accent)}
  .su-hist{margin-top:8px;padding:8px 10px;border-left:2px solid var(--line);background:var(--panel-2);
    border-radius:0 8px 8px 0}
  .su-ver+.su-ver{margin-top:8px;padding-top:8px;border-top:1px solid var(--line-2)}
  .su-ver-h{font-size:10.5px;color:var(--faint);font-family:var(--font-num)}
  .su-ver-b{font-size:12px;color:var(--ink-2);white-space:pre-wrap;margin-top:2px;line-height:1.45}
  .su-ver-b.q{color:var(--faint);font-style:italic}
  /* ---- the reminder plan, read back on the follow-up ---- */
  .fu-remind{font-size:11.5px;color:var(--muted);line-height:1.5}
  .rd-row{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px;background:var(--panel);cursor:pointer}
  .rd-row:hover{background:var(--panel-2)}
  /* ---- THE ESCALATION LADDER. An open demand notice is a FILLED card, never a white one with a hint
     of colour down its edge: Revenue has written to a client demanding money and there is a clock on
     it, so the base is already loud and the levels above it get louder. The level is decided in
     js/revenue-demands.js (rdLoudness) — the maximum of what the letter says and what the clock says
     — precisely so this stylesheet is not the thing choosing between two colours by source order.
       lvl1  a demand, inside the four working days      — amber, filled
       lvl2  a demand past the window                    — red
       lvl3  a FINAL demand: enforcement is next         — red, heavier, red heading
       lvl4  REFERRED TO THE SHERIFF: fees and interest are already being added — the loudest thing
             on the tab, with a ring, because there is no stage after it
     A resolved notice gets none of this on purpose (lvl0, the plain .rd-row above): if closed ones
     shouted too, the open ones would stop standing out, which is the entire point. */
  /* Level 1 is written as the default for ANY open notice as well as for lvl1 by name: if a level ever
     arrives with no rule of its own, it still renders as an open demand notice rather than as a plain
     white card — invisible in exactly the case the ladder exists for. */
  .rd-row.open,.rd-row.open.lvl1{border-left:4px solid var(--warn);background:var(--warn-soft);
    border-color:color-mix(in srgb,var(--warn) 32%,var(--line));
    border-left-color:var(--warn)}
  .rd-row.open:hover,.rd-row.open.lvl1:hover{background:color-mix(in srgb,var(--warn) 16%,var(--panel))}
  /* Every red level mixes against --panel rather than hard-coding a tint, so the ladder keeps the same
     three distinguishable steps in the dark theme, where --panel is nearly black and a fixed pale red
     would collapse them all into one. */
  .rd-row.open.lvl2{border-left-color:var(--crit);background:var(--crit-soft);
    border-color:color-mix(in srgb,var(--crit) 32%,var(--line))}
  .rd-row.open.lvl2:hover{background:color-mix(in srgb,var(--crit) 18%,var(--panel))}
  .rd-row.open.lvl3{border-left:5px solid var(--crit);
    background:color-mix(in srgb,var(--crit) 24%,var(--panel));
    border-color:color-mix(in srgb,var(--crit) 55%,var(--line))}
  .rd-row.open.lvl3:hover{background:color-mix(in srgb,var(--crit) 32%,var(--panel))}
  .rd-row.open.lvl3 .rd-head{color:var(--crit);font-weight:800}
  .rd-row.open.lvl4{border-left:7px solid var(--crit);border-color:var(--crit);
    background:color-mix(in srgb,var(--crit) 40%,var(--panel));
    box-shadow:0 0 0 2px color-mix(in srgb,var(--crit) 30%,transparent)}
  .rd-row.open.lvl4:hover{background:color-mix(in srgb,var(--crit) 48%,var(--panel))}
  .rd-row.open.lvl4 .rd-head{color:var(--ink);font-weight:800}
  /* At lvl4 the card itself is saturated enough that muted greys start to disappear into it. */
  .rd-row.open.lvl4 .rd-meta,.rd-row.open.lvl4 .rd-when{color:var(--ink);opacity:.75}
  .rd-row.open.lvl4 .rd-next{color:var(--ink);font-weight:600}
  /* The stage badge goes SOLID at lvl4. A crit-on-crit-soft pill sitting on a saturated red card is
     the one element that loses legibility as the ladder gets louder, and "referred to the sheriff" is
     the last thing that should be hard to read. */
  .rd-row.open.lvl4 .pill.crit{background:var(--crit);color:#fff}
  /* The window is gone. EMPHASIS on top of whichever level applies, never a colour of its own — it
     says the four days are up, not how bad the letter is. */
  .rd-row.open.hot .rd-next{color:var(--crit);font-weight:600}
  .rd-row.open.hot .rd-when{color:var(--crit)}
  .rd-row-h{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
  .rd-head{font-size:12.5px;font-weight:700;color:var(--ink)}
  .rd-client{font-size:12.5px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:26ch}
  .rd-when{font-family:var(--font-num);font-size:11.5px;color:var(--muted);margin-left:auto;flex:none}
  /* An update written up days after it happened (js/revenue-demands.js rdUpdateLagDays). Quiet on
     purpose: it is context, not a fault — and it only appears where the two dates differ, which is
     the small minority of rows. */
  .rd-lag{font-size:10.5px;color:var(--faint);font-style:italic;flex:none;white-space:nowrap}
  /* A client name that opens the client (js/engine.js clientLinkHtml). Dotted, not a link colour:
     these sit inside rows that are themselves clickable, so it has to read as "this one does
     something different" without competing with the row. */
  .client-link{cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}
  .client-link:hover{text-decoration-style:solid;color:var(--accent)}
  .rd-meta{font-size:11.5px;color:var(--muted);margin-top:5px;line-height:1.5}
  .rd-next{font-size:12px;color:var(--ink);margin-top:7px;line-height:1.5}
  /* The hold, above the urgency. Its own left rule so it reads as a note ABOUT the notice rather
     than as the next thing in the same sentence — both lines are true and they say different things.
     Deliberately not coloured by the row's loudness: a hold is neither good news nor bad, it is a
     fact with a date on it, and the loud styling belongs to the clock underneath. */
  .rd-row .rd-hold{color:var(--muted);border-left:2px solid var(--line-2);padding-left:9px;margin-top:8px}
  .rd-row.open.hot .rd-hold,.rd-row.open.lvl4 .rd-hold{color:var(--muted);font-weight:400}
  /* THE LINES TABLE on the notice panel. A quiet grid that grows a Head column only when there is
     more than one line — see rdLinesPanelHtml. The total row is bolder, because a multi-line notice
     is the whole reason this exists. */
  .rd-lines{margin:10px 0}
  .rd-lines-tbl{width:100%;border-collapse:collapse;font-size:12.5px;margin-top:4px}
  .rd-lines-tbl th{text-align:left;padding:6px 8px;color:var(--muted);font-weight:500;
    border-bottom:1px solid var(--line-2)}
  .rd-lines-tbl td{padding:6px 8px;border-bottom:1px solid var(--line-2)}
  .rd-lines-tbl tfoot .rd-line-total td{border-top:1px solid var(--line);
    border-bottom:none;padding-top:8px}
  .rd-lines-tbl .rd-line-act{text-align:right;white-space:nowrap}
  .rd-lines-tbl .rd-line-act .btn.ghost{color:var(--muted);background:transparent;border-color:transparent}
  /* The line chip on a trail row — "which line did this move" without opening the update. */
  .rd-line-chip{display:inline-block;background:var(--surface-2);color:var(--muted);
    border:1px solid var(--line-2);border-radius:9px;padding:1px 8px;font-size:11px;margin-left:4px}
  .rd-res{font-size:12px;color:var(--muted);margin-top:7px;padding-top:6px;border-top:1px solid var(--line-2);line-height:1.5}
  .rd-read-h{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:8px;font-size:13px}
  /* The stage warning. Loud, and deliberately the only red block in the reading dialog — it is the
     sentence that says what happens if nobody acts. */
  .rd-warn{border:1px solid var(--crit);background:var(--crit-soft);border-radius:9px;padding:10px 12px;
    margin:10px 0;font-size:12.5px;color:var(--ink);line-height:1.55}
  .rd-trail{margin-top:12px;border-top:1px solid var(--line-2);padding-top:10px;max-height:260px;overflow:auto}
  .rd-trail-i{padding:7px 0;border-bottom:1px solid var(--line-2);display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11.5px}
  .rd-trail-i:last-child{border-bottom:0}
  .rd-trail-i .cr-body{flex-basis:100%;margin-top:2px}
  /* The remove control on the edit form. Set apart and tinted, not styled as a peer of Save: it is
     the one action on that dialog you cannot undo from the same dialog. */
  .cr-del-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:8px 11px;border-radius:9px;
    border:1px solid var(--line);background:var(--panel-2);font-size:11.5px;color:var(--muted)}
  .cr-del-row .cr-del-btn{margin-left:auto;color:var(--crit);border-color:var(--crit)}

  /* ---- Global search: the top-bar box, its dropdown, and the #search results surface ------------
     (js/search.js). The dropdown is absolutely positioned out of the topbar, which is itself
     position:sticky with a backdrop-filter — so it needs its own opaque background and a z-index
     ABOVE the topbar's 20, or the blur layer paints over the results. */
  .search-wrap{position:relative;flex:1;max-width:420px;display:flex}
  .search-wrap .search{flex:1;max-width:none}
  .search-kbd{font-family:var(--font-num);font-size:10px;color:var(--faint);border:1px solid var(--line);border-radius:5px;padding:1px 5px;flex:none;letter-spacing:.02em}
  .search-drop{position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:60;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-lg);max-height:min(72vh,560px);overflow:auto;padding:5px}
  .sr-empty{padding:13px 12px;font-size:12.5px;color:var(--faint);display:flex;align-items:center;gap:9px;flex-wrap:wrap}
  .sr-foot{display:flex;align-items:center;gap:10px;padding:9px 11px 7px;margin-top:4px;border-top:1px solid var(--line-2);font-size:11.5px;color:var(--faint);cursor:pointer}
  .sr-foot:hover{background:var(--panel-2)}
  .sr-foot .sr-see{margin-left:auto;color:var(--accent-ink);font-weight:600}

  /* One row primitive for both the dropdown and the surface (.compact is the dropdown variant), so a
     result reads identically in the two places it can appear. */
  /* align-items:start, not center: the title wraps now, so a two-line row must not float its icon
     and its buttons in the middle of the gap. */
  .sr-row{display:grid;grid-template-columns:26px 1fr auto;align-items:start;gap:10px;padding:9px 10px;border-radius:var(--r-sm);cursor:pointer;outline:none}
  .sr-row:hover,.sr-row.on,.sr-row:focus-visible{background:var(--panel-2)}
  .sr-row.on{box-shadow:inset 0 0 0 1px var(--line)}
  .sr-ic{display:grid;place-items:center;color:var(--faint);padding-top:1px}
  .sr-ic .ic{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none}
  .sr-body{min-width:0}
  /* The title is the answer to the search, so it is never truncated. overflow-wrap:anywhere so a long
     unbroken token (a reference, a URL) still cannot push the row wider than its column. */
  .sr-t{font-size:13px;font-weight:600;color:var(--ink);line-height:1.35;overflow-wrap:anywhere}
  .sr-s{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}
  /* Was a right-hand column competing with the title for width; now its own line under the subtitle. */
  .sr-m{font-family:var(--font-num);font-size:10.5px;color:var(--faint);margin-top:2px;overflow-wrap:anywhere}
  /* The matched run. A background tint rather than bold, so highlighting never reflows the row. */
  .sr-t mark,.sr-s mark{background:var(--accent-soft);color:var(--accent-ink);border-radius:3px;padding:0 1px}
  .sr-right{display:flex;align-items:center;gap:9px;justify-content:flex-end;flex:none}
  .sr-kind{font-size:10.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--muted);background:var(--line-2);border-radius:20px;padding:2px 8px;white-space:nowrap}
  .sr-pin,.sr-new{border:1px solid var(--line);background:var(--panel);color:var(--muted);border-radius:7px;flex:none}
  .sr-pin{font-size:10.5px;font-weight:700;padding:3px 8px}
  .sr-pin:hover{color:var(--accent-ink);border-color:var(--accent)}
  .sr-new{width:24px;height:24px;display:inline-grid;place-items:center}
  .sr-new:hover{color:var(--ink);border-color:var(--faint)}
  .sr-new .ic{width:13px;height:13px;stroke:currentColor;stroke-width:1.9;fill:none}
  /* The dropdown is a narrow column, so the row drops its kind chip there and keeps the glyph. */
  .sr-row.compact{padding:7px 8px;grid-template-columns:22px 1fr auto}
  .sr-row.compact .sr-t{font-size:12.5px}

  .sr-chips{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin:0 0 14px}
  .sr-group{margin-bottom:14px}
  .sr-group .sr-ic{width:20px}
  .sr-list{padding:2px 6px 8px}
  .sr-more{padding:8px 15px 12px;font-size:11.5px;color:var(--faint);border-top:1px solid var(--line-2)}
  .sr-pad{padding:14px 15px}

  /* Pinned scope chip — the omni-filter's only always-visible surface. Accent-tinted because it
     changes what every other page shows: a global filter that looks like neutral chrome is a global
     filter people stop noticing. */
  .pin-chip{display:inline-flex;align-items:center;gap:7px;flex:none;background:var(--accent-soft);border:1px solid var(--accent);border-radius:20px;padding:3px 4px 3px 10px;max-width:290px}
  .pin-k{font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink)}
  .pin-l{font-size:12px;font-weight:600;color:var(--accent-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .pin-open{font-size:10.5px;font-weight:700;color:var(--accent-ink);background:transparent;border:1px solid var(--accent);border-radius:6px;padding:2px 7px;flex:none}
  .pin-open:hover{background:var(--panel)}
  .pin-x{width:20px;height:20px;display:inline-grid;place-items:center;border:0;background:transparent;color:var(--accent-ink);flex:none}
  .pin-x .ic{width:13px;height:13px;stroke:currentColor;stroke-width:2.2;fill:none}
  .pin-x:hover{background:var(--panel);border-radius:50%}
  /* A clickable document reference inside a ledger/table cell (docRefIsOpenable, js/doc-open.js) —
     drawn as a link ONLY where the document is actually held, so it can never be a dead click. */
  .doc-ref{color:var(--accent-ink);font-weight:600;cursor:pointer;background:none;border:0;padding:0;font-size:inherit;font-family:inherit}
  .doc-ref:hover{text-decoration:underline}

  /* Recent searches, shown in the dropdown when the box is empty (js/search.js). */
  .sr-recent-h{display:flex;align-items:center;gap:8px;padding:8px 10px 5px;font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}
  .sr-recent-clr{margin-left:auto;border:0;background:transparent;color:var(--accent-ink);font-size:10.5px;font-weight:700;text-transform:none;letter-spacing:0}
  .sr-recent-clr:hover{text-decoration:underline}
  .sr-row.sr-recent .sr-m{font-family:var(--font-ui);font-size:10.5px;color:var(--faint);opacity:0}
  .sr-row.sr-recent:hover .sr-m{opacity:1}

  /* DataTable quick search — the forgiving, ranked box that leads a register's toolbar
     (cfg.search, js/datatable.js). Mirrors the top-bar .search so the two read as the same control
     doing the same job at two scopes. */
  .dt-search{display:flex;align-items:center;gap:7px;background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:5px 9px;color:var(--faint);min-width:190px;max-width:290px;flex:0 1 250px}
  .dt-search.on{border-color:var(--accent);background:var(--accent-soft)}
  .dt-search .ic{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;flex:none}
  .dt-search input{border:0;background:transparent;color:var(--ink);flex:1;outline:none;font-size:12px;min-width:0}
  .dt-search.on input{color:var(--accent-ink);font-weight:600}
  .dt-search-x{width:18px;height:18px;display:inline-grid;place-items:center;border:0;background:transparent;color:var(--accent-ink);flex:none}
  .dt-search-x .ic{width:12px;height:12px;stroke-width:2.4}
  .dt-search-x:hover{background:var(--panel);border-radius:50%}

  @media (max-width:900px){.search-kbd{display:none}.pin-chip{max-width:170px}}

  @media (max-width:1080px){.split{grid-template-columns:1fr}.tiles,.pulse-grid.three,.pulse-grid.four,.pulse-grid.five{grid-template-columns:repeat(2,1fr)}.pulse-grid,.cov-grid{grid-template-columns:1fr}}
/* ---- Permissions matrix (Admin → People → Permissions, js/permissions-matrix.js) ----
   Deliberately small and quiet: fourteen columns of switches becomes noise the moment each one is
   the size of a normal control, so a cell is a 20px square that reads as ticked/blank at a glance
   and carries its explanation on hover. `firmoff` greys a whole column when the firm-wide switch
   above is off — the permission is still really held, it just cannot be used, and showing it as
   simply "off" would be a lie the person's drawer would then contradict. */
.pm-cell{width:20px;height:20px;border-radius:5px;border:1px solid var(--line-2);background:var(--bg-2);
  display:inline-flex;align-items:center;justify-content:center;font-size:11px;line-height:1;
  color:transparent;cursor:pointer;padding:0;transition:background .12s,border-color .12s}
.pm-cell:hover{border-color:var(--accent)}
.pm-cell.on{background:var(--good-soft);border-color:var(--good);color:var(--good);font-weight:700}
.pm-cell.locked{cursor:default;opacity:.55}
.pm-cell.locked:hover{border-color:var(--line-2)}
.pm-cell.firmoff{opacity:.4;filter:grayscale(1)}
.pm-na{color:var(--line-2);font-weight:700;cursor:default}

/* The firm-wide switch strip — one chip per capability, same order as the columns below it. */
.pm-firm{display:flex;flex-wrap:wrap;gap:6px;padding:10px 15px 14px}
.pm-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 9px;border-radius:999px;
  border:1px solid var(--line-2);background:var(--bg-2);color:var(--muted);font-size:11.5px;
  cursor:pointer;transition:background .12s,border-color .12s,color .12s}
.pm-chip:hover{border-color:var(--accent)}
.pm-chip .pm-dot{width:7px;height:7px;border-radius:50%;background:var(--line-2);flex:none}
.pm-chip.on{background:var(--good-soft);border-color:var(--good);color:var(--good);font-weight:600}
.pm-chip.on .pm-dot{background:var(--good)}

/* ---- Notification policy matrix (Admin → Notifications, js/notifications-matrix.js) ----
   Reuses .pm-cell for the channel switches so a switch means the same thing on both admin grids.
   The audience chips are their own thing: they carry a word, not a tick, because "who" is only
   readable as a name. Off chips stay visible rather than being hidden — the question this screen
   answers is as often "who is NOT getting this" as "who is". */
.nm-aud{display:flex;flex-wrap:wrap;gap:4px;max-width:330px}
.nm-chip{font-size:10.5px;line-height:1.5;padding:1px 7px;border-radius:999px;cursor:pointer;
  border:1px solid var(--line-2);background:transparent;color:var(--faint);transition:background .12s,border-color .12s,color .12s}
.nm-chip:hover{border-color:var(--accent);color:var(--muted)}
.nm-chip.on{background:var(--good-soft);border-color:var(--good);color:var(--good);font-weight:600}
/* A firm-wide audience is a standing group, not a relationship — different shape so an accidental
   "every partner, every admin" on a busy event is visible at a glance. */
.nm-chip.firm{border-style:dashed}
.nm-chip.firm.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}
.nm-chip.locked{cursor:default;opacity:.6}
.nm-chip.locked:hover{border-color:var(--line-2);color:var(--faint)}

/* ---- Reports → Rates & realization: the blended-rate timeline (js/rate-timeline.js) ------------
   The chart is inline SVG with no library, so every colour has to come from the tokens the rest of
   the app uses — hard-coded hex here would be the one thing on the page that ignores dark mode. */
.rt-card .card-h{align-items:center}
.rt-legend{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--muted)}
.rt-k{width:14px;height:3px;border-radius:2px;display:inline-block}
.rt-k-bar{height:10px;width:8px;background:color-mix(in srgb,var(--accent) 30%,var(--panel));border:1px solid var(--line)}
.rt-k-line{background:var(--accent)}
.rt-k+.rt-k{margin-left:10px}
.rt-head{display:flex;align-items:baseline;flex-wrap:wrap;gap:8px 16px;padding:14px 16px 4px}
.rt-big{display:flex;align-items:baseline;gap:6px}
.rt-v{font:600 30px/1 var(--font-num);color:var(--ink);letter-spacing:-.5px}
.rt-u{font-size:12.5px;color:var(--muted)}
.rt-delta{font:600 13px/1 var(--font-ui);padding:3px 8px;border-radius:99px}
.rt-delta.down{color:var(--crit);background:var(--crit-soft)}
.rt-delta.up{color:var(--good);background:var(--good-soft)}
.rt-sub{flex:1 1 100%;font-size:12px;color:var(--muted)}
.rt-chartwrap{padding:4px 12px 10px}
.rt-svg{width:100%;height:auto;display:block}
.rt-grid{stroke:var(--line-2);stroke-width:1}
.rt-yr{stroke:var(--line);stroke-width:1;stroke-dasharray:3 3}
.rt-yrlab{font:600 10px var(--font-ui);fill:var(--faint)}
.rt-axis{font:11px var(--font-num);fill:var(--faint)}
.rt-axis2{fill:var(--bb)}
/* --accent-soft is tuned for panel fills, and against the dark panel it lands at about 1.1:1 —
   invisible for a bar that carries a number. Tinting the accent itself keeps the bars legible in
   both themes without a second hard-coded colour. */
.rt-bar{fill:var(--accent);fill-opacity:.3}
.rt-line{fill:none;stroke:var(--accent);stroke-width:2.5;stroke-linejoin:round}
.rt-dot{fill:var(--panel);stroke:var(--accent);stroke-width:2}
.rt-note{padding:2px 16px 14px;font-size:11.5px;color:var(--faint);line-height:1.5}
.rt-empty{padding:18px;color:var(--muted);font-size:13px}
.rt-chip{display:inline-block;margin:1px 4px 1px 0;padding:1px 7px;border-radius:99px;
  background:var(--panel-2);border:1px solid var(--line);font-size:11px;color:var(--muted);white-space:nowrap}
.rt-chip b{color:var(--ink);font-weight:600}
.rt-tbl .rt-dn{color:var(--crit)}
.rt-tbl .rt-up{color:var(--good)}

/* ---- Boot screen (js/boot-screen.js) ----
   Five marks, one per collection group, settling green as each group lands; the orbit is the gate.
   Every colour comes from the app's own tokens so this reads as Insight rather than as a widget. */
.bsc{position:fixed; inset:0; z-index:9000; background:var(--ground); display:grid; place-items:center;
  opacity:0; transition:opacity .3s ease; padding:24px}
.bsc.in{opacity:1}
/* The first-paint variant (the copy in index.html). It has to become visible with no JS at all,
   since the point is to cover the window where the scripts are still downloading — but it keeps the
   same 400ms-ish hold-back in pure CSS, so a warm boot removes it before it was ever seen. Ends at
   the same opacity as .in, so js/boot-screen.js adopting the node is not a visible handover. */
.bsc.bscpre{animation:bscPreIn .3s ease .45s forwards}
@keyframes bscPreIn{to{opacity:1}}
.bsc-panel{display:flex; flex-direction:column; align-items:center; gap:24px; text-align:center; max-width:560px; width:100%}
.bsc-brand{display:flex; align-items:center; gap:10px; font-weight:700; font-size:18px; letter-spacing:-.015em; color:var(--ink)}
.bsc-m{width:26px; height:26px; border-radius:8px; background:var(--accent); color:#fff; display:grid; place-items:center; font-size:14px; font-weight:800}
.bsc-cap{margin:-14px 0 0; font-size:13px; color:var(--muted); min-height:20px; transition:color .3s}
.bsc.done .bsc-cap{color:var(--good); font-weight:600}
.bsc-marks{display:grid; grid-template-columns:repeat(5,1fr); gap:10px; width:100%}
.bsc-cell{background:var(--panel); border:1px solid var(--line); border-radius:var(--r-sm);
  padding:15px 8px 12px; display:flex; flex-direction:column; align-items:center; gap:11px;
  transition:border-color .35s ease, background .35s ease}
.bsc-cell.done{border-color:transparent; background:var(--good-soft)}
.bsc-stage{height:70px; display:grid; place-items:center}
.bsc-lab{font-size:10.5px; font-weight:650; line-height:1.3; color:var(--muted)}
.bsc-cell.done .bsc-lab{color:var(--good)}
/* dots — clients & contacts. Took over from the counting number, so it has to carry the card on its
   own and must not read as another bar chart. */
.bsc-dots{display:grid; grid-template-columns:repeat(3,12px); gap:8px; justify-content:center}
.bsc-dots i{width:12px; height:12px; border-radius:50%; background:var(--line);
  transition:background .3s ease}
.bsc-dots i.on{background:var(--accent)}
.bsc-cell.done .bsc-dots i{background:var(--good)}
/* ring — jobs */
.bsc-ring{position:relative; width:66px; height:66px}
.bsc-ring i{position:absolute; left:50%; top:50%; width:2.5px; height:9px; margin:-33px 0 0 -1.25px;
  border-radius:2px; background:var(--line); transform-origin:50% 33px; transition:background .2s ease}
.bsc-ring i.on{background:var(--accent)}
.bsc-cell.done .bsc-ring i{background:var(--good)}
/* ledger — billing. Widths taper (set inline) so it reads as figures, not five loading bars. */
.bsc-ledger{width:78px; display:flex; flex-direction:column; gap:7px; align-items:flex-start}
.bsc-ledger span{height:5px; border-radius:3px; background:var(--line); position:relative; overflow:hidden}
.bsc-ledger span::after{content:""; position:absolute; inset:0; background:var(--accent);
  transform:scaleX(0); transform-origin:left; transition:transform .45s cubic-bezier(.4,0,.2,1)}
.bsc-ledger span.on::after{transform:scaleX(1)}
.bsc-cell.done .bsc-ledger span::after{background:var(--good); transform:scaleX(1)}
/* pulse — time & people */
.bsc-pulse{width:42px; height:42px; border-radius:13px; background:var(--accent); color:#fff;
  display:grid; place-items:center; font-size:21px; font-weight:800;
  animation:bscBreathe 2s cubic-bezier(.4,0,.2,1) infinite; transition:background .35s}
.bsc-cell.done .bsc-pulse{animation:none; background:var(--good)}
@keyframes bscBreathe{0%,100%{transform:scale(1)} 50%{transform:scale(.9)}}
/* cols — deadlines. Keeps a varied height when settled so it still reads as a chart. */
.bsc-cols{display:flex; align-items:flex-end; gap:5px; height:62px}
.bsc-cols i{width:8px; border-radius:2px; background:var(--line); height:10px;
  transition:height .4s cubic-bezier(.4,0,.2,1), background .3s}
.bsc-cols i.on{background:var(--accent)}
.bsc-cell.done .bsc-cols i{background:var(--good)}
/* ---- idle motion: every mark is alive from the first frame ----
   The marks used to sit still until their own collections landed, and during the phases before that
   they were dimmed too. On a real 30-40s boot that is five grey, motionless cards under a spinner —
   which reads as "stuck", the exact conclusion this screen exists to prevent.

   So each mark now runs its own loop in accent blue from the moment the screen appears. This is NOT a
   progress report and must not be read as one: it promises only "these are the things being fetched".
   The honest signals are untouched and still have to be earned — a card turns GREEN only when its
   collections have genuinely arrived, the caption still names the group actually holding boot up, and
   a failure still replaces the whole screen with the error. Optimistic motion, truthful completion.

   Nothing here animates `transform` on .bsc-ring i: that carries an inline rotate() placing the tick
   on the dial, and a keyframe touching transform would fling them all to twelve o'clock. */

/* One stagger scale, shared by every mark, so they ripple instead of blinking in unison. Goes up to
   8 because the jobs group has eight collections — the widest mark on screen. */
.bsc-stage i:nth-child(1),.bsc-stage span:nth-child(1){animation-delay:0s}
.bsc-stage i:nth-child(2),.bsc-stage span:nth-child(2){animation-delay:.11s}
.bsc-stage i:nth-child(3),.bsc-stage span:nth-child(3){animation-delay:.22s}
.bsc-stage i:nth-child(4),.bsc-stage span:nth-child(4){animation-delay:.33s}
.bsc-stage i:nth-child(5),.bsc-stage span:nth-child(5){animation-delay:.44s}
.bsc-stage i:nth-child(6){animation-delay:.55s}
.bsc-stage i:nth-child(7){animation-delay:.66s}
.bsc-stage i:nth-child(8){animation-delay:.77s}

.bsc-dots i{animation:bscDot 1.5s ease-in-out infinite}
@keyframes bscDot{0%,100%{background:var(--line); transform:scale(.82)}
  45%{background:var(--accent); transform:scale(1.06)}}

.bsc-ring i{animation:bscTick 1.3s ease-in-out infinite}
@keyframes bscTick{0%,100%{background:var(--line)} 40%{background:var(--accent)}}

.bsc-cols i{animation:bscEq 1.6s ease-in-out infinite}
@keyframes bscEq{0%,100%{height:12px; background:var(--line)}
  50%{height:52px; background:var(--accent)}}

.bsc-ledger span::after{animation:bscSweep 1.9s ease-in-out infinite}
.bsc-ledger span:nth-child(1)::after{animation-delay:0s}
.bsc-ledger span:nth-child(2)::after{animation-delay:.16s}
.bsc-ledger span:nth-child(3)::after{animation-delay:.32s}
.bsc-ledger span:nth-child(4)::after{animation-delay:.48s}
@keyframes bscSweep{0%,100%{transform:scaleX(0)} 55%{transform:scaleX(1)}}

/* Landed. The motion stops and the settled green state underneath shows through — this is the moment
   that actually means something, so it must look different in kind from the waiting state. */
.bsc-cell.done .bsc-dots i,
.bsc-cell.done .bsc-ring i,
.bsc-cell.done .bsc-cols i,
.bsc-cell.done .bsc-ledger span::after{animation:none}

/* orbit — the gate. Collapses its dots to a point when everything is in. */
.bsc-gate{display:flex; flex-direction:column; align-items:center; gap:12px}
.bsc-orbit{position:relative; width:40px; height:40px; animation:bscRot 1.5s linear infinite}
.bsc-orbit i{position:absolute; width:7px; height:7px; border-radius:50%; background:var(--accent); transition:all .5s cubic-bezier(.4,0,.2,1)}
.bsc-orbit i:nth-child(1){top:0; left:50%; margin-left:-3.5px; opacity:1}
.bsc-orbit i:nth-child(2){top:50%; right:0; margin-top:-3.5px; opacity:.6}
.bsc-orbit i:nth-child(3){bottom:0; left:50%; margin-left:-3.5px; opacity:.32}
.bsc-orbit i:nth-child(4){top:50%; left:0; margin-top:-3.5px; opacity:.15}
@keyframes bscRot{to{transform:rotate(360deg)}}
.bsc.done .bsc-orbit{animation:none}
.bsc.done .bsc-orbit i{background:var(--good); opacity:1}
.bsc.done .bsc-orbit i:nth-child(1){top:50%; margin-top:-3.5px}
.bsc.done .bsc-orbit i:nth-child(2){right:50%; margin-right:-3.5px}
.bsc.done .bsc-orbit i:nth-child(3){bottom:50%; margin-bottom:-3.5px}
.bsc.done .bsc-orbit i:nth-child(4){left:50%; margin-left:-3.5px}
.bsc-gatelab{font-size:11.5px; color:var(--faint); font-family:var(--font-num); font-variant-numeric:tabular-nums}
.bsc.done .bsc-gatelab{color:var(--good)}
/* ---- something to read while you wait (js/boot-interlude.js) ----
   A 30-40 second wait you only watch feels far longer than one you spent reading. Sits below the
   spinner, deliberately quiet: it is the thing you notice second, never competing with the caption
   that tells you what is actually happening. */
/* The offer itself. Reads as an invitation, not a control — it sits with "Continue anyway" as the
   other answer to a long wait, so it must not look like the primary action. */
/* The ready confirmation, shown only when somebody chose to read something and boot then finished.
   It is a report, not an interruption: the screen says it is done and waits to be dismissed, because
   pulling them into the app mid-sentence is exactly what they opted out of. */
.bsc-ready{display:flex; flex-direction:column; align-items:center; gap:9px}
.bsc-ready-note{margin:0; font-size:11.5px; color:var(--good); font-weight:600}
.bsc-ready-go{font:inherit; font-size:12.5px; font-weight:650; padding:7px 20px; border-radius:99px;
  border:0; background:var(--good); color:#fff; cursor:pointer; box-shadow:var(--shadow);
  transition:transform .15s ease, filter .15s ease}
.bsc-ready-go:hover{filter:brightness(1.06)}
.bsc-ready-go:active{transform:translateY(1px)}

.bsc-offer{font:inherit; font-size:12px; font-weight:600; padding:5px 15px; border-radius:99px;
  border:1px dashed var(--line); background:transparent; color:var(--muted); cursor:pointer;
  transition:border-color .2s, color .2s, background .2s}
.bsc-offer:hover:enabled{border-style:solid; border-color:var(--accent); color:var(--accent-ink); background:var(--panel)}
.bsc-offer:disabled{cursor:default; opacity:.6; border-style:solid}
/* Said out loud when the model surfaced the quotation rather than a person checking it. */
/* The "this came from a model" note, on a generated quote or a generated true/false statement. */
.bsc-ai-flag{font-size:10px; color:var(--faint); font-style:normal; letter-spacing:.01em}

.bsc-interlude{max-width:430px; min-height:74px; display:flex; align-items:flex-start; justify-content:center}
.bsc-quote{margin:0; display:flex; flex-direction:column; gap:7px}
.bsc-quote blockquote{margin:0; font-size:13px; line-height:1.6; color:var(--muted); font-style:italic}
.bsc-quote figcaption{font-size:11px; color:var(--faint); line-height:1.45}
.bsc-quote figcaption a{color:var(--accent-ink); text-decoration:underline; text-underline-offset:2px}
.bsc-quote figcaption a:hover{color:var(--accent)}
.bsc-quiz{display:flex; flex-direction:column; align-items:center; gap:8px}
.bsc-quiz-tag{font-size:9.5px; font-weight:750; letter-spacing:.09em; text-transform:uppercase; color:var(--faint)}
.bsc-quiz-q{margin:0; font-size:13px; line-height:1.55; color:var(--muted)}
.bsc-quiz-row{display:flex; gap:7px}
.bsc-quiz-btn{font:inherit; font-size:11.5px; font-weight:650; padding:4px 16px; border-radius:99px;
  border:1px solid var(--line); background:var(--panel); color:var(--muted); cursor:pointer;
  transition:border-color .2s, color .2s, background .2s}
.bsc-quiz-btn:hover:enabled{border-color:var(--accent); color:var(--accent-ink)}
.bsc-quiz-btn:disabled{cursor:default; opacity:.55}
.bsc-quiz-btn.right{border-color:transparent; background:var(--good-soft); color:var(--good); opacity:1}
.bsc-quiz-btn.wrong{border-color:transparent; background:var(--warn-soft); color:var(--warn); opacity:1}
.bsc-quiz-a{margin:0; font-size:11.5px; line-height:1.5; color:var(--faint); opacity:0;
  transition:opacity .3s ease}
.bsc-quiz-a.in{opacity:1}
/* Nothing dims when boot finishes. That rule made sense when the screen left immediately; now it can
   be HELD open for somebody still reading, and fading the thing they asked for — to .35, mid-sentence
   — makes it unreadable at exactly the moment they are reading it. */

.bsc-skip{font:inherit; font-size:12px; color:var(--muted); background:none; border:0;
  text-decoration:underline; cursor:pointer; padding:4px}
@media (prefers-reduced-motion: reduce){
  .bsc-pulse,.bsc-orbit,.bsc-dots i,.bsc-ring i,.bsc-cols i,.bsc-ledger span::after{animation:none}
}
@media (max-width:640px){ .bsc-marks{grid-template-columns:repeat(2,1fr)} }

/* ============================ DEMO INBOX + GUIDED TOUR ============================
   js/mail-demo.js and js/tour.js. The demo bar sits directly under the Triage page head and must
   read as a mode, not a notice — hence the solid accent pill and the full-width tint, rather than
   the quieter .note treatment an informational strip would get. */
.demo-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 14px;padding:9px 12px;
  background:var(--accent-soft);border:1px solid var(--accent);border-radius:var(--r-sm)}
.demo-pill{flex:none;background:var(--accent);color:#fff;border-radius:999px;padding:2px 9px;
  font-size:10.5px;font-weight:700;letter-spacing:.08em}
.demo-say{flex:1;min-width:220px;font-size:12.5px;color:var(--ink)}
.demo-score{font-family:var(--font-num);font-variant-numeric:tabular-nums;font-size:12px;
  color:var(--accent-ink);font-weight:600;white-space:nowrap}
.demo-stamp{font-size:11px;color:var(--muted);white-space:nowrap}
.demo-bar .btn{flex:none}

/* The spotlight is ONE element: a transparent box over the target with a 9999px shadow doing the
   dimming. pointer-events stays off it so the page underneath is still genuinely clickable — a step
   can ask the person to click the thing being pointed at, which is what makes this teach rather
   than narrate. Only the card itself takes clicks. */
.tour-root{position:fixed;inset:0;pointer-events:none}
.tour-spot{position:fixed;border-radius:10px;pointer-events:none;
  box-shadow:0 0 0 9999px rgba(10,14,22,.58),0 0 0 2px var(--accent) inset;
  outline:2px solid var(--accent);outline-offset:0;transition:left .18s ease,top .18s ease,width .18s ease,height .18s ease}
.tour-card{position:fixed;width:min(360px,92vw);pointer-events:auto;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-lg);padding:14px 15px 12px}
.tour-h{display:flex;align-items:center;gap:8px;margin-bottom:4px}
.tour-count{font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
.tour-x{margin-left:auto;border:0;background:none;color:var(--faint);font-size:14px;line-height:1;padding:2px 4px;border-radius:5px}
.tour-x:hover{color:var(--ink);background:var(--panel-2)}
.tour-card h4{margin:0 0 6px;font-size:15px;font-weight:650;color:var(--ink)}
.tour-body{font-size:13px;color:var(--muted);line-height:1.5}
.tour-body p{margin:0 0 7px}
.tour-body p:last-child{margin-bottom:0}
.tour-body b{color:var(--ink);font-weight:600}
.tour-foot{display:flex;align-items:center;gap:8px;margin-top:12px}
.tour-foot .tour-next{margin-left:auto}
.tour-hint{margin-left:auto;font-size:12px;color:var(--accent-ink);font-weight:600}
@media (prefers-reduced-motion: reduce){ .tour-spot{transition:none} }

/* The mode marker that follows you off Triage. Bottom-LEFT so it never sits under invToast
   (bottom-right, z 200), and below that z so a save message still wins the corner it owns. */
.demo-mark{position:fixed;left:14px;bottom:14px;z-index:190;border:1px solid var(--accent);
  background:var(--accent);color:#fff;border-radius:999px;padding:5px 12px;font:inherit;
  font-size:10.5px;font-weight:700;letter-spacing:.08em;box-shadow:var(--shadow);opacity:.92}
.demo-mark:hover{opacity:1}

/* ---- File tracker (process-guide bridge) ---------------------------------------------------
   A ratio, never a stored percentage: process-guide's templates are editable, so the denominator
   moves. The bar is derived at render time from done/total and the number is always shown beside it,
   because the bar alone would imply a precision the underlying data does not have. */
.ft-cards{display:flex;gap:10px;flex-wrap:wrap;margin:0 0 14px}
.ft-card{min-width:132px;padding:10px 13px;border:1px solid var(--line-2);border-radius:9px;background:var(--panel-2)}
.ft-card.ok{border-color:color-mix(in srgb,var(--ok) 40%,var(--line-2))}
.ft-card.bad{border-color:color-mix(in srgb,var(--bad) 45%,var(--line-2))}
.ft-card.warn{border-color:color-mix(in srgb,var(--warn) 45%,var(--line-2))}
.ft-c-n{font:600 21px/1.1 var(--font-num);color:var(--ink)}
.ft-c-l{margin-top:3px;font-size:11.5px;color:var(--muted)}
.ft-head,.ft-row{display:grid;grid-template-columns:minmax(0,2.2fr) 96px 190px minmax(0,1.3fr) auto;gap:12px;align-items:center}
.ft-head{padding:0 12px 6px;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--faint)}
.ft-list{border:1px solid var(--line-2);border-radius:9px;overflow:hidden}
.ft-row{padding:9px 12px;border-top:1px solid var(--line-2);background:var(--panel)}
.ft-list .ft-row:first-child{border-top:0}
.ft-cl .nm{font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ft-cl .sub{font-size:11.5px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ft-due{font-family:var(--font-num);font-size:12px;color:var(--muted);white-space:nowrap}
.ft-prog{display:flex;align-items:center;gap:8px}
.ft-bar{flex:1;height:7px;border-radius:5px;background:var(--line-2);overflow:hidden}
.ft-fill{display:block;height:100%}
.ft-fill.ok{background:var(--ok)}
.ft-fill.warn{background:var(--warn)}
.ft-fill.bad{background:var(--bad)}
.ft-num{font-family:var(--font-num);font-size:11.5px;color:var(--muted);white-space:nowrap}
.ft-flags{display:flex;gap:5px;flex-wrap:wrap}
.ft-flag{font-size:10.5px;padding:1px 7px;border-radius:999px;border:1px solid var(--line-2);color:var(--muted);white-space:nowrap}
.ft-flag.bad{border-color:color-mix(in srgb,var(--bad) 50%,var(--line-2));color:var(--bad)}
.ft-flag.warn{border-color:color-mix(in srgb,var(--warn) 50%,var(--line-2));color:var(--warn)}
.ft-acts{display:flex;gap:6px;justify-content:flex-end}
.ft-age{margin-top:10px;font-size:11.5px;color:var(--faint);display:flex;align-items:center;gap:9px}
/* Pulling the filing/payment dates across (js/file-tracker.js ftFilingPullHtml). A panel rather than
   a button in the header: it carries a dry run, its own result and up to forty rows of it. */
.ft-pull{margin-top:16px;border:1px solid var(--line);border-radius:11px;padding:12px 14px;background:var(--panel)}
.ft-pull-h{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:13px}
.ft-pull-h .btn{margin-left:0}
.ft-pull-h b{margin-right:auto}
.ft-pull-note{font-size:11.5px;color:var(--faint);margin-top:7px;line-height:1.5}
.ft-pull-rows{margin-top:9px;max-height:280px;overflow:auto}
.ft-pull-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:6px 2px;border-top:1px solid var(--line-2);font-size:11.5px}
.ft-pull-row b{min-width:14ch}
@media (max-width:900px){
  .ft-head{display:none}
  .ft-row{grid-template-columns:1fr;gap:6px}
  .ft-acts{justify-content:flex-start}
}


/* Deployment badge — see js/env-badge.js. Warn tokens, not critical: this is "know where you are",
   not "something is broken". Both tokens are theme-aware, so it stays legible in dark mode. */
.env-badge{display:inline-flex;align-items:center;gap:6px;height:22px;padding:0 9px;border-radius:6px;
  background:var(--warn-soft);color:var(--warn);border:1px solid var(--warn);
  font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;cursor:default}
.env-badge::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--warn);flex:0 0 auto}


/* The message bar stays put. It is sticky at the very top with the top bar sticking BELOW it
   (--banner-h, measured and published by bannerSyncOffset in js/surfaces.js), because the banner
   carries standing instructions -- "you are on the experimental backend" -- and an instruction that
   scrolls away is one that only applies to people who have not scrolled. The height has to be
   measured rather than hard-coded: the text wraps to two or three lines on a narrow window. */
.banner{white-space:normal}

/* Follow-up audience: staff choices are shown only when the picker is expanded. */
.ap-watcher-picker{border:1px solid var(--line);border-radius:8px;background:var(--panel);font-size:12px}
.ap-watcher-picker summary{display:flex;align-items:center;gap:12px;padding:10px 12px;cursor:pointer;list-style:none;color:var(--ink-2)}
.ap-watcher-picker summary::-webkit-details-marker{display:none}
.ap-watcher-picker summary span{flex:1;min-width:0;overflow-wrap:anywhere}
.ap-watcher-picker summary::after{content:"";width:7px;height:7px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg);flex-shrink:0;margin:0 2px 4px}
.ap-watcher-picker[open] summary::after{transform:rotate(225deg);margin-top:4px;margin-bottom:0}
.ap-watcher-picker summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:8px}
.ap-watcher-options{max-height:240px;overflow-y:auto;border-top:1px solid var(--line-2);padding:4px}
.ap-watcher-option{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:5px;cursor:pointer;color:var(--ink-2)}
.ap-watcher-option:hover,.ap-watcher-option:focus-within{background:var(--panel-2)}
.ap-watcher-option input{margin:0;accent-color:var(--accent);flex-shrink:0}
.ap-watcher-option span{min-width:0;overflow-wrap:anywhere}
.ap-watcher-empty{padding:10px 8px;color:var(--faint)}

/* Follow-ups keeps the same scope lens available beside its own controls. */
.fu-head{display:flex;align-items:stretch;justify-content:space-between;margin:0 0 10px;gap:10px}
.fu-head-actions{display:flex;align-items:center;gap:8px;flex:none}
.fu-scope{display:inline-flex;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:2px}
.fu-scope button{border:0;background:transparent;color:var(--muted);font-weight:600;font-size:12px;padding:5px 10px;border-radius:6px;display:flex;align-items:center;gap:5px;cursor:pointer}
.fu-scope button.on{background:var(--accent);color:#fff}
.fu-scope .dot{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.75}
@media (max-width:900px){.fu-head{flex-direction:column}.fu-head-actions{justify-content:space-between;flex-wrap:wrap}.fu-scope{flex:1}.fu-scope button{flex:1;justify-content:center}}

/* The learned-rule review lives above the fixed email-body scroller. Give the review itself a
   bounded scroll region so every outcome and the publish controls remain reachable on short panes. */
.mail-learn-actions{position:sticky;bottom:-12px;z-index:2;padding-bottom:0;background:var(--panel)}

/* Connect an assistant (js/mcp-connect.js). The code is the one thing on the dialog that has to be
   readable across a desk — big, spaced, monospace — and everything else stays quiet around it. */
.mcp-p{font-size:12.5px;line-height:1.55;margin:0 0 12px;color:var(--ink)}
.mcp-p.q{color:var(--muted);margin-top:10px}
.mcp-codewrap{position:relative;display:flex;align-items:center;justify-content:center;border:1px solid var(--line);border-radius:10px;padding:16px 44px;background:var(--panel)}
.mcp-code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:26px;font-weight:700;letter-spacing:3px}
.mcp-ic{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:30px;height:30px}
.mcp-ic.done{color:var(--ok,#1a9e5f)}
.mcp-exp{text-align:center;color:var(--muted);font-size:12px;margin:8px 0 2px}
.mcp-exp.gone{color:var(--crit)}
.mcp-say{font-size:12.5px;color:var(--muted);margin:12px 0 0;text-align:center;background:var(--bg-soft,rgba(0,0,0,.03));border:1px dashed var(--line);border-radius:8px;padding:9px 12px}
.mcp-say b{color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.mcp-row{display:flex;gap:8px;margin-top:14px}
.mcp-row .btn{flex:1;justify-content:center}
.mcp-sess-h{font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:16px 0 6px}
.mcp-sess{display:flex;flex-direction:column;gap:6px}
/* A pairing is a name on its own line, then when it was paired / last used beside End. It was one flex
   row with the dates as a flexible column, so in a narrow dialog the dates wrapped a word per line
   ("paired / 20 / Oct / 10:46 / ...") and the name was squeezed beside them. */
.mcp-srow{display:flex;flex-wrap:wrap;align-items:center;gap:2px 10px;border:1px solid var(--line);border-radius:8px;padding:9px 12px;font-size:12.5px}
.mcp-srow .t{flex:1 1 100%;min-width:0;overflow-wrap:anywhere;line-height:1.35}
.mcp-srow .t b{font-weight:600}
.mcp-srow .mcp-sid{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--muted)}
.mcp-srow .n{color:var(--muted);flex:1 1 0;min-width:11em;font-size:12px}
.mcp-srow .lnk{flex:none;margin-left:auto}
/* Paired assistants as a table: Session · Started · Last used · Ends · ×. */
.mcp-tbl{border:1px solid var(--line);border-radius:10px;overflow:hidden;font-size:12.5px}
.mcp-tr{display:grid;grid-template-columns:minmax(0,1.6fr) repeat(3,minmax(0,.8fr)) 34px;gap:8px;align-items:center;padding:9px 12px;border-top:1px solid var(--line)}
.mcp-tr:first-child{border-top:0}
.mcp-th{background:var(--panel);color:var(--muted);font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;padding-top:7px;padding-bottom:7px}
.mcp-nm{display:flex;flex-direction:column;gap:1px;min-width:0;overflow-wrap:anywhere}
.mcp-nm small{color:var(--muted);font-size:11.5px}
.mcp-dt{color:var(--ink);line-height:1.35}
.mcp-dt i{color:var(--faint);font-style:normal}
.mcp-act{display:flex;justify-content:flex-end}
.mcp-x{width:26px;height:26px;font-size:16px;line-height:1;color:var(--muted)}
.mcp-x:hover{color:var(--crit);border-color:var(--crit)}
@media (max-width:560px){.mcp-tr{grid-template-columns:minmax(0,1fr) 34px}.mcp-th span:nth-child(n+2):not(:last-child),.mcp-tr .mcp-dt{display:none}}
.mcp-empty{color:var(--faint);font-size:12.5px;padding:6px 2px}
.mcp-url{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.mcp-url .q{color:var(--muted);font-size:12px}
.mcp-url code{font-size:12.5px;background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:4px 8px}

/* Team threads — standing delegation (js/team-delegation.js): Pause is the loud button; the classes say
   what each does AND what it cannot; the Advanced group is fenced off in the warning colour. */
.tdl-row .btn{flex:0 1 auto}
.tdl-pause{color:var(--crit);border-color:var(--crit);font-weight:700}
.tdl-paused{color:var(--crit)}
.am-modal.tdl-modal{width:min(640px,96vw);max-height:90vh}
#tdlBody{display:block;overflow:auto;padding:16px 18px}
.tdl-state{display:flex;align-items:center;gap:10px;flex-wrap:wrap;border:1px solid var(--line);border-radius:10px;padding:10px 12px;background:var(--panel);font-size:12.5px;margin-bottom:12px}
.tdl-state>span{flex:1;min-width:200px}
.tdl-state .mcp-row{margin-top:0}
.tdl-err{border-color:var(--crit);color:var(--crit)}
.tdl-sw{display:flex;align-items:center;gap:8px;font-size:13px;margin:4px 0 10px}
.tdl-scopes{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:12.5px;margin-bottom:6px}
.tdl-scopes .q{color:var(--muted)}
.tdl-scopes label{display:flex;align-items:center;gap:6px}
.tdl-cls{display:flex;gap:10px;align-items:flex-start;border:1px solid var(--line);border-radius:8px;padding:9px 11px;margin-bottom:6px;font-size:12.5px;cursor:pointer}
.tdl-cls input{margin-top:3px}
.tdl-cls>span{display:flex;flex-direction:column;gap:2px}
.tdl-does{color:var(--ink)}
.tdl-cannot{color:var(--muted);font-size:11.5px}
.tdl-adv{border:1px dashed var(--warn,#c47f00);border-radius:10px;padding:10px 12px;margin:12px 0 6px}
.tdl-adv-h{font-size:12px;font-weight:700;color:var(--warn,#c47f00);text-transform:uppercase;letter-spacing:.04em}
.tdl-log{display:flex;flex-direction:column;gap:6px;max-height:280px;overflow:auto}
.tdl-lrow{border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:12px}
.tdl-lh{display:flex;gap:8px;align-items:baseline}
.tdl-when{color:var(--faint);font-variant-numeric:tabular-nums;flex:none}
.tdl-subj{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.tdl-cl{flex:none;color:var(--accent-ink);font-size:11px}
.tdl-lc{color:var(--ink);margin-top:2px}
.tdl-run{color:var(--muted);font-size:11px;margin-top:2px}

/* What an email is about, on its row (mailRowContextPillsHtml): the client — or, grouped by client,
   the person — and the job. Labels, not buttons: quiet, capped in width, and they truncate rather
   than wrap, because forty rows each grown by a line is a list half as long. */
.mail-ctx{display:inline-block;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:10.5px;font-weight:600;line-height:1.35;padding:2px 7px;border-radius:999px;border:1px solid var(--line);color:var(--muted);background:var(--panel)}
.mail-ctx b{font-weight:700;opacity:.75}
button.mail-ctx{font:inherit;font-size:10.5px;font-weight:600;cursor:pointer}button.mail-ctx:hover{filter:brightness(.95);text-decoration:underline}
.mail-ctx.client{color:var(--accent-ink);border-color:transparent;background:var(--accent-soft)}
.mail-ctx.contact{color:var(--ink)}
.mail-ctx.job{color:var(--muted);border-style:dashed}
/* The same two facts at the top of the open email, as the way in (mailFiledPillsHtml). Buttons here:
   they open the client or job drawer, so they look pressable and say so on hover. */
.mail-filed-pills{display:flex;flex-wrap:wrap;gap:6px;padding:8px 16px 0}
.mail-filed-pill{font:inherit;font-size:12px;font-weight:600;padding:3px 10px;border-radius:999px;cursor:pointer;
  border:1px solid var(--line);background:var(--panel);color:var(--ink);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mail-filed-pill.client{color:var(--accent-ink);border-color:transparent;background:var(--accent-soft)}
.mail-filed-pill.job{border-style:dashed}
.mail-filed-pill:hover{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}

/* Quick triage on a selection (js/mail-quick-triage.js mailBulkQuickTriageHtml) — the same preset rows
   the single-email menu uses, under a heading, so the two read as one feature. */
.mbt-qt{margin:14px 0 16px}
.mbt-qt-h{font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:0 0 8px}

/* A value hidden out of an email (js/credentials.js, js/mail.js mailUnmaskRedactions). Inline and
   quiet: it is part of a sentence, not a banner, and the reveal has to look like something you may
   click rather than something that has gone wrong. */
.cred-hidden{display:inline-flex;align-items:center;gap:5px;padding:1px 6px;border-radius:5px;
  background:var(--warn-bg,rgba(200,140,0,.10));border:1px solid var(--line-2);vertical-align:baseline}
.cred-hidden .ch-dots{letter-spacing:2px;color:var(--faint);font-family:var(--font-mono,monospace)}
/* A role=button SPAN (BUTTON is a blocked tag in the email sanitiser), so it has to look like the thing
   it is: underlined, pointing, and visible on keyboard focus. */
.cred-hidden .ch-reveal{font-size:11.5px;color:var(--accent-ink,#4b45c9);cursor:pointer;text-decoration:underline;
  text-underline-offset:2px;border-radius:3px}
.cred-hidden .ch-reveal:hover{filter:brightness(.85)}
.cred-hidden .ch-reveal:focus-visible{outline:2px solid var(--accent-ink,#4b45c9);outline-offset:1px}
.cred-hidden.shown{background:transparent;border-color:transparent;padding:0}
.cred-hidden.shown .ch-text{font-family:var(--font-mono,monospace);text-decoration:underline dotted;text-underline-offset:2px}
/* Same treatment as "reveal" — a role=button span that has to look like what it is. */
.cred-hidden .ch-hide{margin-left:6px;font-family:var(--font-ui,inherit);font-size:11.5px;color:var(--accent-ink,#4b45c9);
  cursor:pointer;text-decoration:underline;text-underline-offset:2px;border-radius:3px}
.cred-hidden .ch-hide:hover{filter:brightness(.85)}
.cred-hidden .ch-hide:focus-visible{outline:2px solid var(--accent-ink,#4b45c9);outline-offset:1px}

/* Saving attachments for a selection (js/mail-attachments-bulk.js): a bar at the foot of the screen,
   above the dock, painted in place. It outlives the list, so it is not part of any surface. */
.mail-attbar{position:fixed;left:50%;bottom:78px;transform:translateX(-50%);z-index:190;width:min(520px,calc(100vw - 32px));
  background:var(--panel);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow-lg);padding:11px 44px 12px 14px}
.mab-t{font-size:12.5px;font-weight:700;color:var(--ink)}
.mab-sub{font-size:11.5px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:1.3em}
.mab-track{height:6px;border-radius:999px;background:var(--line-2);margin-top:8px;overflow:hidden}
.mab-track i{display:block;height:100%;width:0;background:var(--accent);border-radius:999px;transition:width .15s ease}
.mab-x{position:absolute;top:8px;right:8px;width:28px;height:28px;border:0;border-radius:8px;background:transparent;color:var(--muted);font:inherit;font-size:14px;cursor:pointer}
.mab-x:hover:not(:disabled){background:var(--panel-2);color:var(--ink)}
.mab-x:disabled{opacity:.5;cursor:default}
/* ============================ AI TRIAGE TAB (js/mail-ai-triage-tab.js, 2026-10-06) ============================
   The Triage tab is the person's own assistant's report: a status strip, the assistant's summary, then one
   card per bucket of rows. Built from the tokens only — the status colours mean a status (stale = warn,
   late = crit, applied = good) and the one accent marks the primary verb. */
.ait-status{display:flex;align-items:center;gap:14px;padding:14px 16px;flex-wrap:wrap}
.ait-dot{width:10px;height:10px;border-radius:50%;background:var(--good);flex:none;box-shadow:0 0 0 3px var(--good-soft)}
.ait-dot.stale{background:var(--warn);box-shadow:0 0 0 3px var(--warn-soft)}
.ait-status .t{font-size:13.5px;font-weight:600}
.ait-status .n{color:var(--muted);font-size:12.5px}
.ait-status .sp{flex:1}
.ait-sched{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;color:var(--muted);border:1px dashed var(--line);border-radius:99px;padding:3px 10px}
.ait-sum{padding:12px 16px 14px;border-top:1px solid var(--line-2);font-size:13px;line-height:1.55}
.ait-sum .who{display:flex;align-items:center;gap:6px;color:var(--muted);font-size:11.5px;margin-bottom:6px}
.ait-sum .who i{width:18px;height:18px;border-radius:50%;background:var(--accent-soft);color:var(--accent-ink);display:grid;place-items:center;font-style:normal;font-size:11px;font-weight:700}
.ait-g{margin-top:14px}
.ait-gh{display:flex;align-items:center;gap:10px;padding:10px 15px 8px}
.ait-gh h3{margin:0;font-size:13px;font-weight:700}
.ait-gh .count{font-family:var(--font-num);font-size:12px;color:var(--muted);background:var(--line-2);border-radius:99px;padding:1px 8px}
.ait-gh .why{color:var(--muted);font-size:12px}
.ait-gh .sp{flex:1}
.ait-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 14px;padding:11px 15px;border-top:1px solid var(--line-2);align-items:start;cursor:pointer}
.ait-row:hover{background:var(--panel-2)}
.ait-row.done{opacity:.6}
.ait-l1{display:flex;align-items:center;gap:8px;min-width:0;flex-wrap:wrap}
.ait-subj{font-weight:600;font-size:13px}
.ait-who{color:var(--muted);font-size:12px}
.ait-wait{font-family:var(--font-num);font-size:11.5px;color:var(--muted)}
.ait-wait.late{color:var(--crit);font-weight:700}
.ait-why{font-size:12.5px;color:var(--ink);margin-top:2px}
.ait-why:before{content:"✦ ";color:var(--accent)}
.ait-ans{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px}
.ait-a{display:inline-flex;align-items:center;gap:4px;max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;border-radius:99px;padding:1px 8px;border:1px solid transparent}
button.ait-a{font:inherit;font-size:11px;cursor:pointer}button.ait-a:hover{filter:brightness(.96);border-color:var(--line)}button.ait-a:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.map-modal{width:min(520px,94vw)}.map-cur{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px}
.map-chip{display:inline-flex;align-items:center;gap:4px;font-size:12px;border-radius:99px;padding:2px 4px 2px 10px;background:var(--accent-soft);color:var(--accent-ink)}
.map-x{border:none;background:none;cursor:pointer;font-size:13px;color:inherit;padding:0 5px;border-radius:99px}.map-x:hover{background:var(--panel)}
.map-q{width:100%;margin-bottom:6px}.map-list{display:grid;gap:3px;max-height:320px;overflow:auto}
.map-row{display:flex;justify-content:space-between;gap:10px;text-align:left;border:1px solid var(--line);background:var(--panel);border-radius:8px;padding:7px 10px;font:inherit;font-size:12.5px;cursor:pointer;color:var(--ink)}.map-row:hover{background:var(--panel-2)}.map-row.on{border-color:var(--accent);background:var(--accent-soft)}.map-row span{color:var(--muted);font-size:11px}
.map-none,.map-hint{font-size:12px;color:var(--faint);padding:6px 2px}.map-foot{display:flex;align-items:center;gap:8px;margin-top:10px}.map-foot .sp{flex:1}
.ait-a b{font-weight:700;opacity:.7;font-size:10px;text-transform:uppercase;letter-spacing:.03em}
.ait-a.a-client{background:var(--accent-soft);color:var(--accent-ink)}
.ait-a.a-contact{background:var(--info-soft);color:var(--info)}
.ait-a.a-next{background:var(--warm-soft);color:var(--warm)}
.ait-a.a-assigned{background:var(--good-soft);color:var(--good)}
.ait-a.a-job{background:var(--line-2);color:var(--ink)}
.ait-a.open{background:transparent;border:1px dashed var(--line);color:var(--faint)}
.mqt-replace{display:block;margin-top:3px;font-size:11px;color:var(--warm)}.mqt-replace b{font-weight:700}
.ait-chips{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}
.ait-chip{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;border-radius:99px;padding:2px 9px;border:1px solid var(--line);background:var(--panel)}
.ait-chip .k{color:var(--faint);font-weight:500}.ait-chip .sg{font-weight:500;font-style:italic;opacity:.75}
.ait-chip.next{border-color:transparent;background:var(--info-soft);color:var(--info)}
.ait-chip.own{border-color:transparent;background:var(--warm-soft);color:var(--warm)}
.ait-chip.file{border-color:transparent;background:var(--accent-soft);color:var(--accent-ink)}
.ait-chip.none{border-style:dashed;color:var(--muted)}
.ait-chip.was{background:var(--good-soft);color:var(--good);border-color:transparent}
.ait-acts{display:flex;gap:6px;align-items:center;white-space:nowrap;padding-top:2px}
.ait-acts .btn{padding:5px 10px;font-size:12px}
.ait-acts a.btn{text-decoration:none;cursor:pointer}
.ait-applied{font-size:12px;color:var(--good);font-weight:600}
.ait-dis{font-size:12px;color:var(--faint)}
.ait-foot{display:flex;gap:16px;justify-content:flex-end;margin-top:16px;font-size:12px}
.ait-foot .lnk{color:var(--muted)}
.ait-howto{margin-top:14px;padding:16px 18px;font-size:13px;line-height:1.6}
.ait-howto ol{margin:8px 0 0 18px;padding:0}
.ait-howto li{margin:0 0 6px}
.ait-howto-btns{display:flex;gap:8px;flex-wrap:wrap}
.ait-prompt{font-family:var(--font-num);font-size:12px;line-height:1.55;background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r-sm);padding:12px 14px;margin:8px 0 10px;white-space:pre-wrap;color:var(--ink)}
.ait-prompt .code{background:var(--accent-soft);color:var(--accent-ink);padding:0 4px;border-radius:4px;font-weight:700}
.ait-cannot{font-size:12px;color:var(--muted);border-top:1px solid var(--line-2);padding-top:10px;margin-top:12px}
.ait-empty{padding:34px 20px;text-align:center}
.ait-empty h3{margin:8px 0 4px;font-size:15px}
.ait-empty p{color:var(--muted);font-size:13px;margin:0 0 14px}
@media (max-width:760px){.ait-row{grid-template-columns:minmax(0,1fr)}.ait-acts{flex-wrap:wrap;white-space:normal}.ait-gh{flex-wrap:wrap}}

/* The scheduled prompt (js/mcp-connect.js mcpStandingHtml): shown once, copied whole. Monospace so the
   handle inside it reads as the token it is, and wraps rather than scrolls so nothing is hidden. */
.mcp-prompt{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;line-height:1.5;background:var(--panel-2);border:1px solid var(--line);border-radius:8px;padding:10px 12px;white-space:pre-wrap;word-break:break-word;color:var(--ink);margin:0 0 4px}

/* AI triage -> Applied by your assistant (js/mail-ai-triage-tab.js) */
.ait-ap-ctl{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:4px 15px 10px}
.ait-ap-ctl .f{max-width:230px}
.ait-ap-gh{padding:8px 15px 4px;font-size:12px;font-weight:700;color:var(--muted)}
.ait-ap-more{padding:8px 15px 12px}
.ait-ap-row .ait-chip i{color:var(--muted);font-style:normal}

.ait-tabs{display:flex;gap:4px;flex-wrap:wrap;margin:14px 0 0;border-bottom:1px solid var(--line)}
.ait-tab{border:0;background:transparent;padding:9px 13px;font:inherit;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px}
.ait-tab.on{color:var(--accent-ink);border-bottom-color:var(--accent)}
.ait-tab .count{font-family:var(--font-num);font-size:11.5px;background:var(--line-2);border-radius:99px;padding:1px 7px;margin-left:4px}
.ait-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:6px 15px 10px}
.ait-bar .sp{flex:1}
.ait-bar .btn{padding:5px 10px;font-size:12px}
.ait-selpage{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}
.ait-seln{font-size:12px;color:var(--muted);margin:0 4px}
.ait-sel{margin-right:2px}
.ait-pager{display:flex;align-items:center;justify-content:center;gap:14px;padding:10px 15px;border-top:1px solid var(--line-2);font-size:12px;color:var(--muted)}

.ait-ap-gh{display:flex;align-items:center;gap:8px;cursor:pointer}

.mqt-facts{display:flex;flex-direction:column;gap:1px;margin:3px 0 2px}
.mqt-fact{font-size:11.5px;color:var(--muted)}
.mqt-fact b{font-weight:600;color:var(--ink)}
.mqt-fact.do b{color:var(--good)}
.mqt-fact.maybe b{color:var(--warn)}
.mqt-fact.no{color:var(--faint)}
.mqt-fact.no b{color:var(--faint);font-weight:500}
.mqt-fine{font-size:11px;color:var(--faint);margin-top:2px}

.ait-bar .ait-set{width:auto;padding:4px 8px;font-size:12px}

.ait-top{display:grid;grid-template-columns:minmax(300px,1fr) auto}
/* The tiles are as tall as the tallest of THEM, not as tall as the card: stretching them to the card
   is what made them four empty boxes whenever the summary was long. align-self:center keeps the
   group level with the status beside it. */
.ait-metrics{display:flex;gap:10px;align-items:stretch;align-self:center;padding:12px 16px;border-left:1px solid var(--line-2);flex-wrap:wrap}
/* The row beneath both columns — the summary, or the empty state. */
.ait-under{grid-column:1 / -1}
.ait-met{display:flex;flex-direction:column;justify-content:center;min-width:92px;padding:6px 12px;border:1px solid var(--line);border-radius:10px;background:var(--panel)}
.ait-met b{font-family:var(--font-num);font-size:22px;line-height:1.1}
.ait-met span{font-size:11.5px;color:var(--muted)}
.ait-undo{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:9999;display:flex;align-items:center;gap:12px;background:var(--ink);color:var(--panel);border-radius:10px;padding:8px 10px 8px 16px;font-size:12.5px;box-shadow:0 6px 24px rgba(0,0,0,.25)}.ait-undo .btn{padding:3px 12px}
.ait-thru{min-width:0;padding:6px 8px}.ait-thru .tt{display:flex;align-items:stretch;gap:6px}.ait-thru .tt div{display:flex;flex-direction:column;justify-content:flex-end;padding:5px 12px;border-radius:8px}.ait-thru .t1{background:var(--good-soft)}.ait-thru .t1 b{color:var(--good)}.ait-thru .t2{background:var(--info-soft)}.ait-thru .t2 b{color:var(--info)}.ait-thru .t3{background:var(--accent-soft)}.ait-thru .t3 b{color:var(--accent-ink)}
.ait-thru .t1 b{font-size:30px}.ait-thru .t2 b{font-size:19px}.ait-thru .t3 b{font-size:14px;font-weight:600}.ait-thru .tt span{font-size:10.5px;color:var(--muted)}.ait-thru .tl{font-size:11px;color:var(--faint);margin-top:2px}
.ait-met.good{background:var(--good-soft);border-color:transparent}
.ait-met.good b{color:var(--good)}
.ait-chev{border:0;background:transparent;cursor:pointer;font-size:12px;color:var(--muted);padding:0 4px}
.ait-ap-gh label{display:inline-flex;align-items:center;gap:6px;cursor:pointer}
@media (max-width:900px){.ait-top{grid-template-columns:1fr}.ait-metrics{border-left:0;border-top:1px solid var(--line-2)}}

.ait-state{font-family:var(--font-num);font-size:11.5px;border-radius:99px;padding:1px 8px;background:var(--warn-soft);color:var(--warn);white-space:nowrap}
.ait-state.ready{background:var(--good-soft);color:var(--good);font-weight:600}
.ait-state.done{background:var(--line-2);color:var(--muted)}

.ait-ap-gh.ait-sub{padding-left:34px;font-weight:500;font-size:12px}
.ait-bystate{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);cursor:pointer}

/* ===================== THE READER'S HEADER BAR, AND THE ATTACHMENTS BAR (2026-10-15) =====================
   js/mail.js mailReadingHtml / mailAttachmentsBarHtml. Asked for with a screenshot circling five stacked
   rows: "all this info can be one sleek header bar … and multi attachments also one bar, the first few shown
   with +8, expandable, and download-all as just an icon in the same bar."

   Row 1 is the subject with the reply / reply-all / forward / more icons, and — only inside the dialog —
   Open in Emails and Close. Row 2 is the client pills and the thread people. One container, one
   background, no rule between the rows. */
.mrh{background:var(--panel);border-bottom:1px solid var(--line)}
.mrh .mail-reading-h{display:flex;align-items:center;gap:6px 14px;flex-wrap:wrap;padding:10px 16px 6px;border-bottom:0}
/* Two lines at most, then an ellipsis — the whole subject is in the tooltip. */
.mrh .mail-reading-h .subj{flex:1 1 200px;min-width:0;font-size:15px;line-height:1.3;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.mrh .mail-reading-actions{margin:0;flex:none;justify-content:flex-start;gap:14px;flex-wrap:wrap}
.mrh-sub{display:flex;flex-wrap:wrap;align-items:center;gap:4px 14px;padding:0 16px 9px}
.mrh-sub .mail-filed-pills,.mrh-sub .mail-thread-people{padding:0;margin:0;font-size:12px}
.mrh-note{font-size:11.5px;color:var(--muted)}
/* The dialog's own controls, at the end of row 1. */
.mrh-modal{display:flex;align-items:center;gap:6px;flex:none;margin-left:4px;padding-left:12px;border-left:1px solid var(--line-2)}
.mrh-x{font-size:20px;line-height:1;width:28px;height:28px;padding:0}
.mrh-expand{width:28px;height:28px;padding:0}
/* The conversation page (#thread/<key>, js/thread-page.js): the list of emails beside the same reading pane the dialog uses. */
.thr-wrap{max-width:1500px;width:100%}
.thr-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.thr-title{min-width:0;flex:1 1 320px}
.thr-title h1{margin:0;font-size:20px;line-height:1.25;overflow-wrap:anywhere}
.thr-head .spacer{display:none}
.thr-body{display:grid;grid-template-columns:minmax(240px,330px) minmax(0,1fr);gap:14px;align-items:start}
.thr-rail{position:sticky;top:8px;max-height:calc(100vh - 150px);overflow:auto;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel)}
.thr-row{display:flex;align-items:flex-start;gap:4px;padding:9px 10px;border-bottom:1px solid var(--line-2);cursor:pointer;border-left:3px solid transparent}
.thr-row:last-child{border-bottom:0}
.thr-row:hover{background:var(--panel-2)}
.thr-row:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.thr-row.on{background:var(--accent-soft);border-left-color:var(--accent)}
.thr-row.out .thr-row-who::before{content:"\2192 ";color:var(--faint)}
.thr-row-main{flex:1;min-width:0}
.thr-row-top{display:flex;gap:2px 8px;justify-content:space-between;align-items:baseline;flex-wrap:wrap}
.thr-row-who{font-weight:600;font-size:12.5px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.thr-row-when{font-size:11px;color:var(--faint);flex:none;white-space:nowrap}
.thr-row-snip{font-size:11.5px;color:var(--muted);margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.thr-att{color:var(--accent-ink);font-weight:600}
.thr-row-copy{flex:none;opacity:.55}
.thr-row:hover .thr-row-copy,.thr-row-copy:focus-visible{opacity:1}
.thr-pane{border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel);min-width:0;overflow:hidden}
.thr-pane .mail-reading{min-height:240px}
.thr-flash{animation:thr-flash 2.2s ease-out 1}
@keyframes thr-flash{0%,35%{box-shadow:0 0 0 3px var(--accent-soft),0 0 0 4px var(--accent)}100%{box-shadow:0 0 0 0 transparent}}
.thr-att-hit{outline:2px solid var(--accent);outline-offset:1px;background:var(--accent-soft)}
@media(max-width:860px){
  .thr-body{grid-template-columns:minmax(0,1fr)}
  .thr-rail{position:static;max-height:240px}
}
/* Stays in view while the message scrolls: Close must not scroll away with the first screenful. */
.mail-reader-modal .mrh{position:sticky;top:0;z-index:6}
/* The dialog's original strip stands aside ONLY while the pane carries its controls (mrh-active, set by
   mailPaintReadingArea) — while loading, or once the pane has turned into the composer, it is the only
   way out of the dialog and stays. Visually hidden, not removed: the real buttons are still the ones that
   do the work, and the title is still the dialog's accessible name. */
.mail-reader-modal.mrh-active > .am-h{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ---- attachments: one bar ---- */
.mab{display:flex;align-items:center;gap:6px;margin:0 0 12px;padding:5px 6px 5px 10px;border:1px solid var(--line);
  border-radius:10px;background:var(--panel);min-width:0;flex-wrap:nowrap;overflow:hidden}
.mab.open{flex-wrap:wrap;overflow:visible}
.mab-count{flex:none;font-size:11.5px;color:var(--muted);white-space:nowrap}
/* Chips SHRINK with an ellipsis instead of wrapping, so the bar is one line however long the names are. */
.mab-chip{display:inline-flex;align-items:center;gap:5px;flex:0 1 auto;min-width:84px;max-width:240px;padding:3px 10px;
  border:1px solid var(--line);border-radius:999px;background:var(--panel);font:inherit;font-size:12px;color:var(--ink);cursor:pointer}
.mab-chip:hover{border-color:var(--accent);color:var(--accent-ink)}
.mab-chip .mab-ic{flex:none}
.mab-chip .mab-nm{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mab-more{flex:none;padding:3px 9px;border:1px dashed var(--line);border-radius:999px;background:transparent;font:inherit;
  font-size:12px;color:var(--accent-ink);cursor:pointer;white-space:nowrap}
.mab-more:hover{background:var(--accent-soft)}
.mab-all{flex:none;margin-left:auto}
.mbc{position:relative;flex:1;min-width:0}.mbc-q{width:100%}.mbc-q.chosen{font-weight:600;border-color:var(--accent)}
.mbc-list{position:absolute;left:0;right:0;top:100%;z-index:30;max-height:260px;overflow:auto;background:var(--panel);border:1px solid var(--line);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.14);margin-top:3px;padding:3px}
.mbc-opt{display:flex;justify-content:space-between;gap:10px;width:100%;text-align:left;border:0;background:none;font:inherit;font-size:12.5px;padding:6px 9px;border-radius:6px;cursor:pointer;color:var(--ink)}.mbc-opt:hover{background:var(--accent-soft)}
.mbc-opt span{color:var(--muted);font-size:11px}.mbc-opt.special{font-weight:600;color:var(--accent-ink)}
.mbc-gh{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--faint);padding:7px 9px 2px}.mbc-none{font-size:12px;color:var(--faint);padding:8px 9px}

  /* ---- SharePoint folder browser (js/sharepoint-ui.js spOpenBrowser) — breadcrumb trail, ghost icon buttons, list rows ---- */
  /* A text-only button. Base for class="lnk" / "link" wherever the container does not style it itself — those rules (.mail-aud-line .lnk …) are more specific and still win.
     Without this a bare lnk button is the browser's grey form button, which is what the SharePoint tab's job names and "Check the connection" looked like (2026-11-02). */
  button.lnk,button.link{border:0;background:none;padding:0;margin:0;font:inherit;font-weight:600;color:var(--accent-ink);cursor:pointer;text-align:left;border-radius:4px}
  button.lnk:hover,button.link:hover{text-decoration:underline}
  button.lnk:focus-visible,button.link:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  button.lnk:disabled,button.link:disabled{opacity:.5;cursor:default;text-decoration:none}
  .sp-jobrow{padding:9px 0;border-top:1px solid var(--line-2);font-size:12.5px}.sp-jobrow:first-of-type{border-top:0;padding-top:6px}
  .sp-jobrow .sp-jobpath{margin-top:4px}
  .spb-bar{display:flex;align-items:center;gap:6px;min-width:0}
  .spb-bar .icon-btn.sm{flex:none;border-color:transparent;background:transparent}
  .spb-bar .icon-btn.sm:hover:not(:disabled){background:var(--panel-2);border-color:var(--line)}
  .spb-crumbs{flex:1;min-width:0;display:flex;align-items:center;gap:1px;overflow-x:auto;scrollbar-width:none;white-space:nowrap}
  .spb-crumbs::-webkit-scrollbar{display:none}
  .spb-crumb{flex:none;max-width:180px;overflow:hidden;text-overflow:ellipsis;border:0;background:transparent;padding:3px 6px;border-radius:6px;font:inherit;font-size:12.5px;color:var(--muted);cursor:pointer}
  button.spb-crumb:hover{background:var(--panel-2);color:var(--ink)}
  .spb-crumb.cur{color:var(--ink);font-weight:700;cursor:default}
  .spb-ic{width:14px;height:14px;flex:none;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round}
  .spb-sep{width:12px;height:12px;color:var(--faint)}
  .spb-sub{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:2px 0 -2px}.spb-subtxt{font-size:11.5px;color:var(--faint)}
  .spb-newbtn{flex:none}
  .spb-list{border:1px solid var(--line-2);border-radius:10px;overflow:hidden;max-height:300px;overflow-y:auto;background:var(--panel)}
  .spb-row{display:flex;width:100%;align-items:center;gap:10px;text-align:left;padding:9px 12px;border:0;border-bottom:1px solid var(--line-2);background:transparent;font:inherit;font-size:12.5px;color:var(--ink);cursor:pointer}
  .spb-row:last-child{border-bottom:0}
  .spb-row:hover{background:var(--panel-2)}
  .spb-row:focus-visible,.spb-crumb:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
  .spb-folder{width:16px;height:16px;color:var(--accent-ink)}
  .spb-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .spb-count{font-size:11px;color:var(--faint);flex:none}
  .spb-go{width:13px;height:13px;color:var(--faint)}
  .spb-new{padding:8px 12px;border-bottom:1px solid var(--line-2);background:var(--accent-soft)}
  .spb-new-row{display:flex;align-items:center;gap:8px}
  .spb-new-input{flex:1;min-width:0;padding:6px 9px;border:1px solid var(--line);border-radius:8px;font:inherit;font-size:12.5px;background:var(--panel);color:var(--ink)}
  .spb-new-input:focus{outline:2px solid var(--accent);outline-offset:-1px}
  .spb-new-err{font-size:11.5px;color:var(--crit);margin-top:4px}.spb-new-err:empty{display:none}
  .spb-empty{padding:16px;text-align:center;color:var(--muted);font-size:12.5px}
  .spb-files{font-size:11px;color:var(--faint);margin-top:4px}
  @media (max-width:520px){.spb-crumb{max-width:110px}}
