/* ============================================================
   Nexus 2.0 — The Experience Lab design system
   Brand: orange #FF4000, charcoal "wet slate", yellow accent
   Type:  Bebas Neue (display) + Poppins (UI)
   ============================================================ */
:root{
  /* WCAG AA, measured 2026-07-17 (audit I1/I2/I3). TWO oranges — which one you want depends
     on whether small text touches it:
       --orange     = the BRAND MARK. Logo, nav accent, borders, focus rings, and big display
                      type (the 40px job badge clears AA-large at 3.15). A white label on it is
                      only 3.15, under the 4.5 AA floor — so NEVER put small text on it.
       --orange-600 = the INTERACTIVE orange. TEL "Red Ochre" #BC5026 nudged to #B44A22 so it
                      clears 4.5 on the page bg too, not just on cards. White on it = 5.33; as
                      text 4.85 on --bg / 5.33 on --card / 4.70 on --orange-50. Button fills
                      carrying a white label, and orange text/links, use THIS one.
     Greg supplied the brand palette 2026-07-17. #D46536 "Spicy Paprika" reads like a primary-
     button colour but fails as one (white on it = 3.68) — don't reach for it. Full palette map
     + ratios: memory `tel-brand-orange-fails-wcag-aa`. */
  --orange:#F16622; --orange-600:#B44A22; --orange-700:#953C1C; --orange-50:#fdeee5;
  --yellow:#FFC400;
  /* --ink = TEL "Shadow Grey", the brand black. 14.90; was #222526 at 14.07 — same readability. */
  --ink:#221F20; --slate:#3a3d3e; --slate-2:#5b6770; --grey:#888686;
  --line:#e9e6e4; --line-2:#f1eeec;
  /* --muted was #8a8785 = 3.25, FAILING AA while carrying nearly every field hint on the booking
     form. #6f6c6a = 4.75 on --bg / 5.21 on --card. TEL "Grey" #787777 is NOT enough (4.07) — it
     looks like the obvious muted colour and still fails. */
  --bg:#f6f4f2; --card:#ffffff; --muted:#6f6c6a;
  /* --good was #2e9e5b (3.41, fail). --bad was #e53935 (4.23, fail) — and was LESS accessible
     than the #c62828 templates kept hand-typing, so the token moved TO the literal, not the
     other way round. --warn is a fill/border only; never put small text on it. */
  --good:#237a46; --warn:#f0a300; --bad:#c62828;
  --radius:16px; --radius-sm:11px;
  --shadow:0 1px 2px rgba(20,18,16,.04), 0 8px 24px rgba(20,18,16,.06);
  --shadow-lg:0 12px 40px rgba(20,18,16,.14);
  --nav-w:230px; --header-h:62px;
  /* Height the mobile save rail reserves once it goes position:fixed (see #160). One row of
     buttons + its padding; kept here so the bar and the space it occupies can't drift apart. */
  --saverail-h:54px;
  --sans:'Poppins',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --display:'Bebas Neue','Poppins',sans-serif;
}
/* ============================================================
   SPACING / TYPE / BREAKPOINT TOKENS — roadmap I4/I6/I7, added 2026-07-19.
   Additive only — nothing above this block changed. Derived from what this
   file actually uses (counted first, scaled second), not invented from
   theory. Adopted into tel.css itself immediately below (equivalent
   substitutions only — this pass changes zero rendered pixels); anything
   that didn't fit the scale was left hardcoded on purpose rather than
   rounded onto the nearest step.
   ============================================================ */
:root{
  /* --- SPACING ---------------------------------------------------------
     323 padding/margin/gap declarations in this file before this pass.
     Raw px-number frequency across all of them (each shorthand value
     counted separately, e.g. "padding:14px 16px" counts both 14 and 16):
       8px:52  10px:51  6px:49  12px:49  14px:46  4px:28  16px:26  9px:25
       7px:24  3px:19  18px:19  2px:18  11px:16  5px:14  1px:11  22px:4
       20px:4  15px:3  13px:3  26px:2  34px:1  24px:1
     This file was never built on an 8pt (or any) grid — odd integers
     (5/7/9/11px…) run just as often as even ones, so there's no clean
     ratio to derive. Instead: one named step per raw value used >=4
     times (covers 313 of the ~360 raw numbers above). 13px/15px/24px/
     26px/34px are real but too rare (<=3 uses) to justify a token —
     left hardcoded rather than force-fit onto a neighbouring step. */
  --space-1:1px;  --space-2:2px;  --space-3:3px;  --space-4:4px;
  --space-5:5px;  --space-6:6px;  --space-7:7px;  --space-8:8px;
  --space-9:9px;  --space-10:10px; --space-11:11px; --space-12:12px;
  --space-14:14px; --space-16:16px; --space-18:18px; --space-20:20px;
  --space-22:22px;

  /* --- TYPE --------------------------------------------------------
     201 font-size declarations before this pass, running 9px-48px in
     near-duplicate .5 steps (9/9.5, 10/10.5, 11/11.5, 12/12.5, 13/13.5,
     14/14.5) that read as accidental drift, not deliberate sizes.
     Collapsed to one named step per whole pixel, counting the .5 variant
     into its integer neighbour:
       10px:13(+10 as 10.5)  11px:24(+16 as 11.5)  12px:31(+22 as 12.5)
       13px:32(+9 as 13.5)   14px:9(+1 as 14.5)     16px:2   18px:4
       20px:4   24px:3   32px:1   40px:2   48px:1
     Only the WHOLE-pixel values get a token; the .5 variants and other
     one-off odd sizes (15/17/22/23/26/30/34px) are real but not reused
     enough to name — left hardcoded, not rounded onto this scale. */
  --text-10:10px; --text-11:11px; --text-12:12px; --text-13:13px;
  --text-14:14px; --text-16:16px; --text-18:18px; --text-20:20px;
  --text-24:24px; --text-32:32px; --text-40:40px; --text-48:48px;

  /* --- BREAKPOINTS (documentation + a target for future rules ONLY) ----
     ⚠ CSS custom properties DO NOT resolve inside an @media query
     condition — `@media(max-width:var(--bp-phone))` is invalid CSS and
     silently fails to apply. These three vars are therefore NOT
     substituted into any @media rule below; they exist so a *future*
     rule has a named value to reach for, and so the 11 raw breakpoints
     already in this file have a documented home instead of floating
     free. Existing raw breakpoints, mapped onto a 3-tier phone/tablet/
     desktop split by what each one actually gates:
       Phone   (<=~720px):   520, 600, 601*, 640, 700, 720
       Tablet  (~820-960px): 820, 900, 901*, 960
       Desktop (>960px):     1180
     (* 601 and 901 are the min-width complements of the 600/900
     max-width splits — same boundary, written from the other side.) */
  --bp-phone:600px; --bp-tablet:900px; --bp-desktop:1180px;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
svg.ic{width:18px;height:18px;flex:0 0 auto}
.search svg{width:16px;height:16px}
.btn svg.ic{width:15px;height:15px}
.profile .meta svg.ic,.crumb svg.ic{width:14px;height:14px;vertical-align:-2px}
.hamb svg{width:20px;height:20px}
body{
  font-family:var(--sans); color:var(--ink); background:var(--bg);
  font-size:var(--text-14); line-height:1.5; -webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none}
h1,h2,h3{margin:0;font-weight:600;letter-spacing:-.01em}

/* ---------- Layout shell ---------- */
.shell{display:flex;min-height:100vh}
.side{
  width:var(--nav-w);flex:0 0 var(--nav-w);background:var(--ink);color:#cfccc9;
  position:sticky;top:0;height:100vh;display:flex;flex-direction:column;gap:var(--space-4);
  padding:var(--space-18) var(--space-14);z-index:30;
}
.brand{display:flex;align-items:center;gap:var(--space-10);padding:var(--space-10) var(--space-8) var(--space-16);flex:0 0 auto}
/* Annotation #159 — the phone drawer's close X. Hidden by default: on desktop the sidebar
   is permanent and there's nothing to close (it's turned on in the <=600px block). */
.nav-x{display:none}
.brand img{height:30px;width:auto;max-width:100%}
.nav{display:flex;flex-direction:column;gap:var(--space-2);margin-top:var(--space-2);flex:1 1 auto;min-height:0;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#46494b transparent}
.nav::-webkit-scrollbar{width:6px}
.nav::-webkit-scrollbar-thumb{background:#46494b;border-radius:3px}
.nav::-webkit-scrollbar-track{background:transparent}
/* group header rows read exactly like the nav links above them (uniform text/size/colour) */
.nav-sec{display:flex;align-items:center;gap:var(--space-11);padding:var(--space-11) var(--space-12);border-radius:11px;
  font-size:var(--text-14);font-weight:500;color:#b9b5b1;text-transform:none;letter-spacing:0;transition:.12s}
.nav-sec .ic{width:18px;height:18px;flex:0 0 18px;opacity:.85}
.userbox{display:flex;align-items:center;gap:var(--space-10);padding:var(--space-10);border-radius:12px;background:#2c2f30;margin-bottom:var(--space-8)}
.userbox:hover{background:#34373a}
.userbox .ub-meta{flex:1;min-width:0;line-height:1.25}
.userbox .ub-meta b{color:#fff;font-size:var(--text-13);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.userbox .ub-meta small{color:#9a9692;font-size:var(--text-11)}
.userbox .ic{width:16px;height:16px;color:#9a9692}
.nav a{
  display:flex;align-items:center;gap:var(--space-11);padding:var(--space-11) var(--space-12);border-radius:11px;
  color:#b9b5b1;font-weight:500;transition:.12s;
}
.nav a .ic{width:18px;height:18px;flex:0 0 18px;opacity:.85}
.nav a:hover{background:#2c2f30;color:#fff}
.nav a.on{background:var(--orange-600);color:#fff}
.nav a.on .ic{opacity:1}
.side .spacer{flex:0 0 8px}
.side .foot{font-size:var(--text-11);color:#6f6b67;padding:var(--space-8);border-top:1px solid #34373880;line-height:1.6}
.side .foot a{color:#9a9692;text-decoration:underline}

/* ---------- Collapsible sidebar (icon-only rail) ---------- */
/* Body gets .nav-mini (from JS in base.html) when: desktop toggle/auto-collapse, OR
   tablet (601-900px, always a rail). Phone (<=600px) uses the off-canvas hamburger
   instead, so this whole block is gated to >600px and never fights that nav.
   In mini mode the rail is a FIXED overlay (main reserves a 68px gutter) so that
   expanding it — hover on desktop, tap on touch — floats over content, no reflow. */
.grp-head{display:none}   /* only shown inside the pointer-mini group pop-out panel */
@media(min-width:601px){
  /* --- collapsed rail (icons only) --- */
  body.nav-mini{--nav-w:68px}
  body.nav-mini .side{position:fixed;top:0;left:0;height:100vh;width:68px;
    padding-left:var(--space-10);padding-right:var(--space-10);overflow:visible;z-index:40;
    transition:width .18s ease}
  body.nav-mini .main{margin-left:68px}
  body.nav-mini .brand{display:none}   /* no logo in the collapsed rail */
  body.nav-mini .nav a,
  body.nav-mini .nav .nav-sec{display:flex;align-items:center;justify-content:center;
    gap:0;font-size:0;white-space:nowrap;overflow:hidden;
    padding-left:0;padding-right:0;list-style:none}
  body.nav-mini .nav .nav-sec{color:#b9b5b1}   /* match nav-link icon brightness */
  body.nav-mini .nav .nav-sec .ic{width:18px;height:18px;opacity:.85}
  body.nav-mini .nav-sec{padding-top:var(--space-12);padding-bottom:var(--space-4)}
  body.nav-mini .userbox{justify-content:center;padding:var(--space-10) 0}
  body.nav-mini .userbox .ub-meta{display:none}
  body.nav-mini .userbox>.ic{display:none}
  body.nav-mini .side .foot{display:none}
  /* group children hidden in the rail — pointer shows them as a hover pop-out,
     touch reveals them inline when the rail is expanded (peek) */
  body.nav-mini .nav-group>.grp-items{display:none}

  /* --- TOUCH: tap expands the whole rail (peek); tap outside / Esc collapses.
         (Floating labels below need hover, which touch lacks, so touch gets the
         full slide-out instead.) --- */
  body.nav-mini.nav-peek .side{width:230px;box-shadow:var(--shadow-lg)}
  body.nav-mini.nav-peek .side .brand{display:flex;justify-content:flex-start;padding:var(--space-10) var(--space-8) var(--space-16)}   /* peek → wordmark back */
  body.nav-mini.nav-peek .side .brand img{height:30px}
  body.nav-mini.nav-peek .side .nav a,
  body.nav-mini.nav-peek .side .nav .nav-sec{justify-content:flex-start;gap:var(--space-11);
    font-size:var(--text-14);padding-left:var(--space-12);padding-right:var(--space-12)}
  body.nav-mini.nav-peek .side .nav .nav-sec{font-size:10.5px;padding-left:var(--space-12)}
  body.nav-mini.nav-peek .side .userbox{justify-content:flex-start;padding:var(--space-10)}
  body.nav-mini.nav-peek .side .userbox .ub-meta{display:block}
  body.nav-mini.nav-peek .side .userbox>.ic{display:block}
  body.nav-mini.nav-peek .side .foot{display:block}
  body.nav-mini.nav-peek .side .nav-group>.grp-items{display:block}   /* reveal sub-items inline */
  body.nav-mini.nav-peek .scrim{position:fixed;inset:0;background:rgba(0,0,0,.28);z-index:35;display:block}

  /* --- POINTER: keep the rail collapsed; on hover show a small floating label
         box per icon (fade + grow), and a pop-out submenu panel for the groups.
         The whole menu never slides out. --- */
  @media(hover:hover){
    body.nav-mini .nav{overflow:visible}                 /* let flyouts escape the rail */
    /* simple top-level links → floating label (overflow must be visible or the
       collapsed-rail overflow:hidden clips the ::after box) */
    body.nav-mini .nav>a{position:relative;overflow:visible}
    body.nav-mini .nav>a::after{
      content:attr(data-label);position:absolute;left:100%;top:50%;margin-left:var(--space-10);
      transform:translateY(-50%) scale(.9);transform-origin:left center;
      background:var(--ink);border:1px solid #34373a;color:#fff;font-weight:500;
      font-size:var(--text-13);padding:var(--space-7) var(--space-11);border-radius:9px;white-space:nowrap;
      box-shadow:var(--shadow-lg);opacity:0;pointer-events:none;
      transition:opacity .13s ease,transform .13s ease;z-index:50}
    body.nav-mini .nav>a:hover::after{opacity:1;transform:translateY(-50%) scale(1)}
    /* collapsible groups → pop-out submenu panel listing the child links */
    body.nav-mini .nav-group{position:relative}
    body.nav-mini .nav-group>.grp-items{
      position:absolute;left:100%;top:0;margin-left:var(--space-10);min-width:190px;display:block;
      background:var(--ink);border:1px solid #34373a;border-radius:12px;padding:var(--space-6);
      box-shadow:var(--shadow-lg);opacity:0;visibility:hidden;transform:scale(.96);
      transform-origin:left top;z-index:50;
      transition:opacity .13s ease,transform .13s ease,visibility 0s .13s}
    body.nav-mini .nav-group>.grp-items::before{content:"";position:absolute;
      left:-12px;top:0;bottom:0;width:12px}       /* invisible bridge across the gap */
    body.nav-mini .nav-group:hover>.grp-items{opacity:1;visibility:visible;
      transform:scale(1);transition-delay:0s}
    body.nav-mini .nav-group>.grp-items>a{justify-content:flex-start;gap:var(--space-11);
      font-size:var(--text-14);padding:var(--space-9) var(--space-12);white-space:nowrap;overflow:visible}
    body.nav-mini .nav-group>.grp-items>a::after{display:none}   /* panel already labels them */
    body.nav-mini .grp-head{display:block;font-size:10.5px;text-transform:uppercase;
      letter-spacing:.08em;color:#8a8683;font-weight:600;padding:var(--space-5) var(--space-12) var(--space-6)}
  }

}

/* Full (wide, non-collapsed) menu: keep groups compact — only the section header
   shows — and reveal their children as a hover pop-out, same as the rail, so the
   top-level items are never pushed down/off. Phone drawer (hover:none) is excluded
   and keeps children inline. */
@media(hover:hover) and (min-width:901px){
  body:not(.nav-mini) .nav{overflow:visible}      /* let the flyout escape the nav scroll box */
  body:not(.nav-mini) .nav-group{position:relative}
  body:not(.nav-mini) .nav-group>.grp-items{
    position:absolute;left:100%;top:0;margin-left:var(--space-10);min-width:190px;
    background:var(--ink);border:1px solid #34373a;border-radius:12px;padding:var(--space-6);
    box-shadow:var(--shadow-lg);opacity:0;visibility:hidden;transform:scale(.96);
    transform-origin:left top;z-index:50;
    transition:opacity .13s ease,transform .13s ease,visibility 0s .13s}
  body:not(.nav-mini) .nav-group>.grp-items::before{content:"";position:absolute;
    left:-12px;top:0;bottom:0;width:12px}          /* invisible hover bridge across the gap */
  body:not(.nav-mini) .nav-group:hover>.grp-items{opacity:1;visibility:visible;
    transform:scale(1);transition-delay:0s}
  body:not(.nav-mini) .nav-group>.grp-items>a{white-space:nowrap}
  body:not(.nav-mini) .nav-group:hover>.nav-sec{background:#2c2f30;color:#fff}   /* header feedback on hover */
}

.main{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{
  height:var(--header-h);display:flex;align-items:center;gap:var(--space-14);padding:0 var(--space-22);
  background:rgba(246,244,242,.85);backdrop-filter:saturate(1.4) blur(8px);
  border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20;
}
.topbar h1{font-size:var(--text-18)}
.topbar .crumb{color:var(--muted);font-size:12.5px;font-weight:500}
.search{flex:1;max-width:420px;margin-left:auto;position:relative}
.search input{
  width:100%;border:1px solid var(--line);background:var(--card);border-radius:11px;
  padding:var(--space-9) var(--space-12) var(--space-9) 34px;font-family:inherit;font-size:13.5px;color:var(--ink);
}
.search input:focus{outline:none;border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-50)}
.search svg{position:absolute;left:11px;top:50%;transform:translateY(-50%);opacity:.45}
/* #64: the topbar search icon is wrapped in a real <button> (base.html #tel-search-toggle)
   so it's tappable on phones. On desktop/tablet it must look identical to the old plain
   svg, so reset the button chrome and hand its position/size back to the svg via the
   shared ".search svg" rule above — this only changes at the phone breakpoint below. */
#tel-search-toggle{all:unset;position:absolute;left:11px;top:50%;transform:translateY(-50%);
  display:flex;align-items:center;justify-content:center;cursor:pointer}
#tel-search-toggle svg{position:static;left:auto;top:auto;transform:none}
.content{padding:var(--space-22);max-width:1320px;width:100%}
.hamb{display:none}

/* ---------- Bits ---------- */
.pill{display:inline-flex;align-items:center;gap:var(--space-6);font-size:11.5px;font-weight:600;
  padding:var(--space-3) var(--space-9);border-radius:999px;background:#eee;color:#444;white-space:nowrap}
.pill .dot{width:7px;height:7px;border-radius:50%;background:currentColor}
.pill.lead{background:var(--orange-50);color:var(--orange-600)}
.btn{display:inline-flex;align-items:center;gap:var(--space-8);font-weight:600;font-size:var(--text-13);
  padding:var(--space-9) 15px;border-radius:11px;border:1px solid var(--line);background:var(--card);
  color:var(--ink);cursor:pointer;transition:.12s}
.btn:hover{border-color:#d8d4d1;box-shadow:var(--shadow)}
.btn.primary{background:var(--orange-600);border-color:var(--orange-600);color:#fff}
.btn.primary:hover{background:var(--orange-700);border-color:var(--orange-700)}
.btn.ghost{background:transparent;border-color:transparent;color:var(--slate-2)}
.btn.danger{color:var(--bad)}
.btn.danger:hover{background:var(--bad);border-color:var(--bad);color:#fff}
.muted{color:var(--muted)}
.sec-title{font-family:var(--display);letter-spacing:.04em;font-size:15px;
  text-transform:uppercase;color:var(--slate-2);margin:26px var(--space-2) var(--space-12)}

/* ---------- Cards & grids ---------- */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.grid{display:grid;gap:var(--space-16)}
/* Let grid/flex tracks shrink below their content's intrinsic width so nowrap
   text ellipses instead of forcing the whole card wider than the viewport. */
.grid>*,.fin-grid>*,.cs-body>*,.form-grid>*,.bkedit>*{min-width:0}
.kpis{grid-template-columns:repeat(4,1fr)}
.kpi{padding:var(--space-18) var(--space-18) var(--space-16);position:relative;overflow:hidden}
.kpi .lab{font-size:var(--text-12);font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;position:relative;z-index:1}
.kpi .num{font-family:var(--display);font-size:var(--text-48);line-height:.95;margin-top:var(--space-8);color:var(--ink);white-space:nowrap;position:relative;z-index:1}
.kpi .num.cur{font-size:34px;letter-spacing:.01em}
.kpi .sub{font-size:var(--text-12);color:var(--muted);margin-top:var(--space-4);position:relative;z-index:1}
.kpi .accent{position:absolute;right:-18px;top:-18px;width:84px;height:84px;border-radius:50%;
  background:var(--orange-50);z-index:0}
.kpi.k-orange .num{color:var(--orange-600)}
.cols-2{grid-template-columns:1fr 1fr}
.cols-3{grid-template-columns:repeat(3,1fr)}
/* #62: client-detail financial strip is always 5 tiles (fixed period list from
   finance.py:client_summary) — its own class so it doesn't collide with the
   dashboard's 4-column .kpis, which must keep its own layout unchanged. */
.fin-strip{grid-template-columns:repeat(5,1fr)}

/* ---------- Lists / rows ---------- */
.rowlist{display:flex;flex-direction:column}
.row{display:flex;align-items:center;gap:var(--space-12);padding:var(--space-12) var(--space-16);border-top:1px solid var(--line-2)}
.row:first-child{border-top:0}
.row:hover{background:#fbfaf9}
/* One line, "Tue 21 Jul" (annotation #150) — day number still emphasised for scanning. */
.row .date-chip{flex:0 0 auto;white-space:nowrap;text-align:center;background:#f4f1ef;border-radius:10px;padding:var(--space-6) var(--space-9);align-self:flex-start}
.row .date-chip .d{font-family:var(--display);font-size:var(--text-20);line-height:1;color:var(--ink)}
.row .date-chip .w,.row .date-chip .m{font-size:var(--text-10);text-transform:uppercase;color:var(--muted);letter-spacing:.06em}
.row .main-txt{flex:1;min-width:0}
.row .main-txt b{display:block;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row .main-txt small{color:var(--muted)}
.card-head{display:flex;align-items:center;justify-content:space-between;padding:15px var(--space-16);border-bottom:1px solid var(--line)}
.card-head h3{font-size:14.5px}
.card-head .count{font-size:var(--text-12);color:var(--muted)}

/* ---------- Alerts ---------- */
.alert-grp .row .ic{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex:0 0 28px}
.ic.warn{background:#fff6e0;color:#b07c00}
.ic.bad{background:#fde7e7;color:var(--bad)}
.ic.info{background:var(--orange-50);color:var(--orange-600)}
.empty{padding:26px var(--space-16);text-align:center;color:var(--muted);font-size:var(--text-13)}

/* ---------- Pipeline / board ---------- */
.legend{display:flex;flex-wrap:wrap;gap:var(--space-14);margin:var(--space-4) var(--space-2) 0}
.legend span{display:inline-flex;align-items:center;gap:var(--space-6);font-size:var(--text-12);color:var(--slate-2)}
.legend i{width:10px;height:10px;border-radius:50%}
.board{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(270px,1fr);gap:var(--space-14);overflow-x:auto;padding-bottom:var(--space-8)}
.col{background:#efece9;border-radius:14px;padding:var(--space-10);display:flex;flex-direction:column;gap:var(--space-9);min-height:120px}
.col-head{display:flex;align-items:center;justify-content:space-between;padding:var(--space-4) var(--space-6) var(--space-2)}
.col-head .nm{display:flex;align-items:center;gap:var(--space-8);font-weight:600;font-size:var(--text-13)}
.col-head .tot{font-size:var(--text-12);color:var(--muted);background:var(--card);border-radius:999px;padding:var(--space-1) var(--space-9)}
.bk{background:var(--card);border-radius:12px;padding:var(--space-12);box-shadow:var(--shadow);border:1px solid var(--line);transition:.12s;display:block}
.bk:hover{box-shadow:var(--shadow-lg);transform:translateY(-1px)}
.bk .job{font-family:var(--display);font-size:var(--text-18);letter-spacing:.03em;color:var(--orange-600)}
.bk .cli{font-weight:600;margin:var(--space-1) 0 var(--space-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bk .meta{font-size:var(--text-12);color:var(--muted);display:flex;flex-wrap:wrap;gap:var(--space-4) var(--space-10);margin-top:var(--space-6)}
.bk .quote{font-weight:600;color:var(--ink)}
.col .more{text-align:center;font-size:var(--text-12);color:var(--muted);padding:var(--space-4)}

/* ---------- Tables (actors / detail) ---------- */
.tbl{width:100%;border-collapse:collapse}
.tbl th{font-size:var(--text-11);text-transform:uppercase;letter-spacing:.05em;color:var(--muted);text-align:left;padding:var(--space-10) var(--space-14);border-bottom:1px solid var(--line)}
.tbl td{padding:var(--space-12) var(--space-14);border-bottom:1px solid var(--line-2);vertical-align:top}
.tbl tr:hover td{background:#fbfaf9}
.tbl th.sortable{cursor:pointer;user-select:none}
.tbl th.sortable:hover{color:var(--orange-600)}
.sort-ind{color:var(--orange-600);font-size:var(--text-10)}
/* Annotation #239 — "these should still be circles, not ovals". `flex:0 0 34px` pinned the
   flex-basis, and flex-basis beats `width` on a row's main axis — so every override that
   resized an avatar (the 46px dashboard faces from #235, the 24px whereabouts ones) got its
   new HEIGHT and kept the old 34px width. `flex:0 0 auto` hands control back to `width`, so
   width and height always agree and a resize can't make an oval again. */
.avatar{width:34px;height:34px;border-radius:50%;background:var(--ink);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:var(--text-13);flex:0 0 auto}
.chips{display:flex;flex-wrap:wrap;gap:var(--space-6)}

/* ---------- Booking detail ---------- */
.detail-head{display:flex;align-items:flex-start;gap:var(--space-18);flex-wrap:wrap}
.detail-head .job-badge{font-family:var(--display);font-size:var(--text-40);letter-spacing:.03em;color:#fff;background:var(--orange);border-radius:14px;padding:var(--space-8) var(--space-18);line-height:1}
.detail-head .ttl h1{font-size:23px}
.detail-head .ttl .sub{color:var(--muted);margin-top:var(--space-3)}
.detail-head .actions{margin-left:auto;display:flex;gap:var(--space-8);flex-wrap:wrap}
.facts{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-1);background:var(--line);border-radius:14px;overflow:hidden;margin-top:var(--space-18)}
.facts .f{background:var(--card);padding:var(--space-14) var(--space-16)}
.facts .f .k{font-size:var(--text-11);text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.facts .f .v{font-weight:600;margin-top:var(--space-3)}
.svc{padding:0;overflow:hidden}
.svc .svc-top{display:flex;align-items:center;gap:var(--space-12);padding:var(--space-14) var(--space-16);background:#faf8f6;border-bottom:1px solid var(--line)}
.svc .svc-top .num{font-family:var(--display);font-size:var(--text-20);color:var(--slate-2)}
.svc .svc-body{padding:var(--space-6) var(--space-16) var(--space-14)}
.kv{display:flex;gap:var(--space-8);font-size:var(--text-13);padding:var(--space-5) 0}
.kv .k{color:var(--muted);min-width:96px}
.team{display:flex;flex-direction:column;gap:var(--space-6);margin-top:var(--space-8)}
.team .m{display:flex;align-items:center;gap:var(--space-10);font-size:var(--text-13)}
.team .m .role{color:var(--muted);font-size:var(--text-12)}
.strike{text-decoration:line-through;opacity:.6}

/* Draft rows (/drafts/, _draft_rows.html) — annotation #178. These used to borrow
   `.svc-top`, but that rule is scoped to `.svc .svc-top` and /drafts/ has no `.svc`
   ancestor, so the rows landed with NO padding and NO flex: text flush against the card
   edge and the Open/Delete buttons stacked under the title instead of sitting right.
   Their own component rather than widening `.svc .svc-top`, which would restyle every
   session card on the booking pages. */
.draft-row{display:flex;align-items:center;gap:var(--space-12);padding:var(--space-14) var(--space-16)}
.draft-row .draft-main{display:flex;gap:var(--space-12);flex:1;min-width:0;text-decoration:none;color:inherit}
.draft-row .draft-ttl{display:flex;align-items:center;gap:var(--space-8);flex-wrap:wrap}
.draft-row .draft-meta{color:var(--muted);font-size:12.5px;margin-top:var(--space-3);line-height:1.45}
.draft-row .draft-acts{display:flex;gap:var(--space-6);align-items:center;flex-shrink:0}
@media (max-width:600px){
  /* Phone: the actions can't share the line with a wrapping title — drop them onto their
     own full-width row, separated by a rule so they don't read as part of the meta text. */
  .draft-row{align-items:stretch;flex-direction:column;gap:var(--space-10);padding:13px var(--space-14)}
  .draft-row .draft-acts{border-top:1px solid var(--line);padding-top:var(--space-10)}
}

/* ---------- Calendar ---------- */
#cal{--fc-border-color:var(--line);--fc-today-bg-color:var(--orange-50);background:var(--card);border-radius:var(--radius);
  border:1px solid var(--line);box-shadow:var(--shadow);padding:var(--space-14)}
.fc .fc-toolbar-title{font-family:var(--display);letter-spacing:.03em;font-weight:400;font-size:var(--text-24)}
.fc .fc-button{background:var(--card)!important;border:1px solid var(--line)!important;color:var(--ink)!important;
  text-transform:capitalize;font-weight:600;box-shadow:none!important}
.fc .fc-button-primary:not(:disabled).fc-button-active{background:var(--orange-600)!important;border-color:var(--orange-600)!important;color:#fff!important}
.fc .fc-daygrid-day-number{color:var(--slate-2);font-weight:500}
.fc-event{border:none;border-radius:6px;padding:var(--space-1) var(--space-5);font-size:11.5px;font-weight:600;cursor:pointer}
.fc-event:hover{filter:brightness(.94)}
/* Public-holiday background band (nexus2/views.py) — FullCalendar's own default opacity
   for display:"background" events is .3, which read as barely-there (Greg 2026-07-17).
   Bumping the CSS var only affects background events, so school holidays (a normal
   all-day event, not display:"background") are untouched. */
.fc-bg-event{--fc-bg-event-opacity:.55}

/* ---------- Money / finance ---------- */
.money-tile{padding:var(--space-16) var(--space-18)}
.money-tile .lab{font-size:var(--text-12);font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
.money-tile .big{font-family:var(--display);font-size:var(--text-32);margin-top:var(--space-6);color:var(--ink)}
.money-tile .big.in{color:var(--good)} .money-tile .big.out{color:var(--orange-600)}
.periods{display:flex;flex-wrap:wrap;gap:var(--space-8);margin-bottom:var(--space-16)}
.periods a{padding:var(--space-7) 13px;border-radius:999px;border:1px solid var(--line);background:var(--card);font-size:12.5px;font-weight:600;color:var(--slate-2)}
.periods a.on{background:var(--orange-600);border-color:var(--orange-600);color:#fff}
.barchart{display:flex;align-items:flex-end;gap:var(--space-10);height:200px;padding:var(--space-10) var(--space-4) 0}
.barchart .mb{flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:var(--space-3);height:100%}
.barchart .pair{display:flex;align-items:flex-end;gap:var(--space-3);height:100%;width:100%;justify-content:center}
.barchart .bar{width:42%;border-radius:5px 5px 0 0;min-height:2px}
.barchart .bar.in{background:var(--good)} .barchart .bar.out{background:var(--orange)}
.barchart .mlab{font-size:10.5px;color:var(--muted);white-space:nowrap}
.fin-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-16);margin-top:var(--space-16)}

/* ---------- Actor cards ---------- */
.actor-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:var(--space-14)}
.actor-card{background:var(--card);border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);padding:var(--space-18) var(--space-14);text-align:center;transition:.12s;display:block}
.actor-card:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}
.headshot{width:84px;height:84px;border-radius:50%;object-fit:cover;background:var(--ink);margin:0 auto var(--space-10);display:flex;align-items:center;justify-content:center;color:#fff;font-family:var(--display);font-size:30px;overflow:hidden}
.headshot img{width:100%;height:100%;object-fit:cover;background:var(--card)}
/* Transparent circle PNGs: white backdrop so they match the opaque jpeg headshots
   (no grey rim where the circle is transparent inside the round clip). */
.headshot:has(img){background:var(--card)}
.actor-card .nm{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.actor-card .sub{font-size:var(--text-12);color:var(--muted);margin-top:var(--space-2)}
.actor-card .badge{display:inline-block;margin-top:var(--space-8);font-size:var(--text-11);font-weight:600;color:var(--orange-600);background:var(--orange-50);border-radius:999px;padding:var(--space-2) var(--space-10)}
.inactive-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:#c0c0c0;margin-right:var(--space-5)}

/* ---------- Team directory list (actor rows) ---------- */
.hs-row{display:flex;align-items:center;gap:var(--space-10)}
.headshot.hs-sm{width:40px;height:40px;font-size:15px;margin:0;flex:none}
.pill.st-active{background:#e7f4ec;color:var(--good)}
.pill.st-occasional{background:#fbf1da;color:#b57b00}
.pill.st-inactive{background:#fdeaea;color:var(--bad)}

/* ---------- Profile header ---------- */
.profile{display:flex;gap:var(--space-20);align-items:center;flex-wrap:wrap}
.profile .headshot{width:110px;height:110px;font-size:var(--text-40);margin:0}
.profile h1{font-size:var(--text-24)}
.profile .meta{display:flex;flex-wrap:wrap;gap:var(--space-6) var(--space-16);color:var(--slate-2);font-size:var(--text-13);margin-top:var(--space-6)}
.profile .actions{margin-left:auto;display:flex;gap:var(--space-8);flex-wrap:wrap}

/* ---------- Forms ---------- */
.form-card{max-width:780px}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-14) var(--space-18)}
/* Per-ROW widths inside a form card (session editor: the four times on one line, venue
   beside its room). Deliberately NOT the global .cols-2/.cols-3 above — those are declared
   before .form-grid, so .form-grid's own 1fr 1fr would win and silently flatten the row
   back to 2-up. These carry .form-grid too, so they out-specify it. */
.form-grid.fg-1{grid-template-columns:1fr}
.form-grid.fg-2{grid-template-columns:1fr 1fr}
.form-grid.fg-3{grid-template-columns:repeat(3,1fr)}
.form-grid.fg-4{grid-template-columns:repeat(4,1fr)}
.form-grid+.form-grid{margin-top:var(--space-14)}
/* Annotation #157 — "put this under Cohort, not Title". Title and Cohort share a row, and
   .form-grid flows row-by-row, so the disclosure that follows Cohort dropped back into
   column 1 and read as if it belonged to Title. Pin it to Cohort's column. On mobile the
   grid is one column, where an explicit grid-column:2 would invent a phantom second one —
   so it reverts to simply following Cohort. */
.fgroup.under-col2{grid-column:2}
/* 2026-07-17 restructure — a group heading inside the booking form's field grid. The form
   had grown to ~25 fields in one undifferentiated run; these split it by the question each
   block answers, so the eye can skip to the part it wants. Full-width, ruled above, and NOT
   a card — the card is the accordion; another box inside it would just add walls. */
/* Annotation #173 — "can the horizontal lines dividing sections be orange so that it's more
   clear you're moving into a new section?" A hairline in the same grey as every field border
   read as just more furniture; brand orange makes the boundary a signal. */
/* Annotations #236/#237/#238 — "this goes next to 'The job'". The heading's info "i" was
   rendering on its own line UNDER the heading. Not a markup bug: this rule is written
   `.bk-grp` (0,1,0) but `.fgroup` further down the file (also 0,1,0) sets
   flex-direction:column, and the later rule wins a specificity tie. Every element carrying
   .bk-grp also carries .fgroup, so qualify the selector rather than reorder the file. */
.fgroup.bk-grp{grid-column:1/-1;flex-direction:row;align-items:baseline;gap:var(--space-9);flex-wrap:wrap;
  margin:var(--space-18) 0 var(--space-2);padding-top:var(--space-12);border-top:2px solid var(--orange)}
.fgroup.bk-grp:first-child{margin-top:var(--space-2);padding-top:0;border-top:none}
.bk-grp-t{font-family:var(--display);font-size:var(--text-16);letter-spacing:.4px;color:var(--ink)}
.bk-grp-s{font-size:11.5px;color:var(--muted)}
/* Annotation #158 — fields that only exist BECAUSE a tick is on, shown as a box hanging off
   that tick so the connection is visible instead of inferred. Indented + ruled on the left,
   which is the "submenu" read. */
.submenu-box{margin:var(--space-8) 0 var(--space-2) 24px;padding:var(--space-10) var(--space-12);border:1px solid var(--line);
  border-left:3px solid var(--orange);border-radius:0 9px 9px 0;background:#faf9f7}
/* The "role on this job" add-row, inside the Contacts box (2026-07-17 restructure). As a flex
   row its .fld controls collapsed one-per-line, because .fld carries width:100% and this box
   is narrower than the full-width accordion the row came from. An explicit grid sizes them
   instead of fighting that; one column on a phone, where four controls never fit. */
.role-add{display:grid;grid-template-columns:2fr 1.2fr 1.2fr 1fr auto;gap:var(--space-6);
  margin-top:var(--space-10);align-items:center}
/* The fold-away optional field (Access) — a "+ Add" link standing exactly where the field
   itself will appear. .fgroup is a flex column, so the &nbsp; label is a spacer that drops
   the link past the labels of the fields beside it; the padding then centres it on their
   inputs (.fld is ~38px tall) instead of leaving it floating at the label line.
   Revealing is ONE-WAY on purpose — re-hiding a field that now holds a value would take it
   off the form while still saving it, which is precisely the surprise this all exists to
   avoid. A field with a value or an error is never given this treatment (see the view). */
.fld-optional>.fld-optional-w{display:none}
.fld-optional.on>label,.fld-optional.on>a{display:none}
.fld-optional.on>.fld-optional-w{display:block}
.fld-optional>a{align-self:start;padding-top:var(--space-10)}
.fgroup{display:flex;flex-direction:column;gap:var(--space-5)}
.fgroup.full{grid-column:1 / -1}
.fgroup label{font-size:var(--text-12);font-weight:600;color:var(--slate-2)}
.fgroup .help{font-size:var(--text-11);color:var(--muted)}
.fgroup .err{font-size:11.5px;color:var(--bad)}
.fld{width:100%;border:1px solid var(--line);border-radius:10px;padding:var(--space-9) var(--space-11);font-family:inherit;font-size:13.5px;background:var(--card);color:var(--ink)}
.fld:focus{outline:none;border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-50)}
textarea.fld{min-height:74px;resize:vertical}
.fld-check{width:18px;height:18px;accent-color:var(--orange)}
/* Type-ahead job/booking picker (BookingPickerWidget) — replaces a giant <select>. */
.bkpick{position:relative}
.bkpick-box{display:flex;align-items:center;gap:var(--space-6)}
.bkpick-box .bkpick-q{flex:1;min-width:0}
.bkpick-clear{flex-shrink:0;width:34px;height:34px;border:1px solid var(--line);border-radius:9px;background:var(--card);color:var(--muted);font-size:var(--text-18);line-height:1;cursor:pointer}
.bkpick-clear:hover{color:var(--ink);border-color:var(--muted)}
.bkpick-results{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:40;background:var(--card);border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 24px #0002;max-height:260px;overflow:auto}
.bkpick-opt{padding:var(--space-9) var(--space-12);font-size:var(--text-13);cursor:pointer;border-bottom:1px solid var(--line-2)}
.bkpick-opt:last-child{border-bottom:none}
.bkpick-opt:hover{background:var(--orange-50)}
.bkpick-none{color:var(--muted);cursor:default}
.bkpick-none:hover{background:none}
.check-row{display:flex;align-items:center;gap:var(--space-9);padding:var(--space-6) 0}
.form-actions{display:flex;gap:var(--space-10);margin-top:var(--space-20);padding-top:var(--space-16);border-top:1px solid var(--line)}
.msg{padding:var(--space-11) var(--space-14);border-radius:11px;margin-bottom:var(--space-14);font-size:var(--text-13);font-weight:500}
.msg.success{background:#e7f6ec;color:#1c7a3f} .msg.error{background:#fde7e7;color:var(--bad)}

/* ---------- Simple list pages ---------- */
.list-head{display:flex;align-items:center;gap:var(--space-12);margin-bottom:var(--space-16);flex-wrap:wrap}
.list-head .search{margin:0;max-width:320px}
.list-head .btn{margin-left:auto}
/* Year break-line — a subtle divider where a dated list (e.g. the actor call sheet)
   crosses a calendar-year boundary, so a January row doesn't read as "this year". */
.year-break{display:flex;align-items:center;gap:var(--space-10);margin:var(--space-18) 0 var(--space-12);color:var(--muted);
  font-size:var(--text-12);font-weight:700;letter-spacing:.04em}
.year-break::before,.year-break::after{content:'';flex:1 1 auto;height:1px;background:var(--line)}
.formset-row{border:1px solid var(--line);border-radius:12px;padding:var(--space-12);margin-bottom:var(--space-10);background:#fbfaf9}
.tabs{display:flex;gap:var(--space-6);margin-bottom:var(--space-16);border-bottom:1px solid var(--line);overflow-x:auto;-webkit-overflow-scrolling:touch}
.tabs a{padding:var(--space-9) var(--space-14);font-weight:600;font-size:var(--text-13);color:var(--muted);border-bottom:2px solid transparent;cursor:pointer;white-space:nowrap;flex:0 0 auto}
.tabs a.on{color:var(--orange-600);border-bottom-color:var(--orange)}

/* ---------- Call sheet ---------- */
.cs-row.cancelled{opacity:.6}
.cs-head{display:flex;align-items:center;gap:var(--space-14);padding:var(--space-14) var(--space-16);background:#faf8f6;border-bottom:1px solid var(--line)}
.cs-date{flex:0 0 54px;text-align:center;background:var(--ink);color:#fff;border-radius:11px;padding:var(--space-7) var(--space-4)}
.cs-date .d{font-family:var(--display);font-size:var(--text-24);line-height:1}
.cs-date .m{font-size:var(--text-10);text-transform:uppercase;letter-spacing:.06em}
.cs-date .y{font-size:9.5px;color:#b9b5b1;text-transform:uppercase}
.cs-fee{font-family:var(--display);font-size:22px;color:var(--orange-600);white-space:nowrap}
.cs-body{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-14);padding:var(--space-14) var(--space-16)}
.cs-times{display:flex;gap:var(--space-8);flex-wrap:wrap;margin-bottom:var(--space-10)}
.cs-times>div{background:#f4f1ef;border-radius:9px;padding:var(--space-6) var(--space-11);font-weight:600;font-size:var(--text-13)}
.cs-times>div span{display:block;font-size:9.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;font-weight:600}

/* ---------- Yellow delta (v1 parity) ----------
   A call-sheet / master-list field that changed since the last ObjectHistory
   snapshot is flagged yellow so the actor/office spots what moved. */
.chg{background:var(--yellow)!important;border-radius:6px;
  box-shadow:0 0 0 2px var(--yellow)}
.chg-inline{background:var(--yellow);border-radius:5px;padding:0 var(--space-4);
  box-shadow:0 0 0 1px var(--yellow)}
/* On a yellow "date changed" chip the muted greys go dark so they stay legible. */
.date-chip.chg .m,.date-chip.chg .w{color:var(--ink)}

@media print{
  .side,.topbar,.periods,.list-head,.no-print{display:none!important}
  .content{padding:0;max-width:none}.shell{display:block}.main{display:block}
  body{background:var(--card)}
  .card{box-shadow:none;border:1px solid #ccc;break-inside:avoid}
  .travel-cards{grid-template-columns:1fr 1fr!important;gap:var(--space-8)}
  .detail-head .job-badge{print-color-adjust:exact;-webkit-print-color-adjust:exact}
  .cs-fee,.headshot,.pill{print-color-adjust:exact;-webkit-print-color-adjust:exact}
  .chg,.chg-inline{print-color-adjust:exact;-webkit-print-color-adjust:exact}
  a[href]:after{content:""}
}

/* ---------- HR matrix table ---------- */
.hr-table{font-size:12.5px;white-space:nowrap}
.hr-table thead th{position:sticky;top:0;background:var(--card);z-index:2;user-select:none}
.hr-table th.sortable{cursor:pointer}
.hr-table th.sortable:hover{color:var(--orange-600)}
.hr-table .sort-ind{color:var(--orange-600);font-size:var(--text-10)}
.hr-table .filter-row td{position:sticky;top:35px;background:var(--card);z-index:3;padding:var(--space-5) var(--space-8);overflow:visible}
.hr-table tbody td{max-width:240px;overflow:hidden;text-overflow:ellipsis}
.dd{position:relative}
.dd-btn{width:100%;min-width:96px;text-align:left;border:1px solid var(--line);border-radius:7px;background:var(--card);padding:var(--space-4) var(--space-22) var(--space-4) var(--space-8);font-family:inherit;font-size:11.5px;color:var(--muted);cursor:pointer;white-space:nowrap;position:relative}
.dd-btn:after{content:"▾";position:absolute;right:7px;top:4px;color:var(--muted)}
.dd-btn.active{color:var(--orange-600);border-color:var(--orange);font-weight:600}
.dd-panel{position:absolute;top:calc(100% + 3px);left:0;z-index:40;width:230px;max-width:80vw;background:var(--card);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow-lg);padding:var(--space-8)}
.dd-search{width:100%;border:1px solid var(--line);border-radius:7px;padding:var(--space-5) var(--space-8);font-family:inherit;font-size:var(--text-12);margin-bottom:var(--space-6)}
.dd-list{max-height:230px;overflow:auto;display:flex;flex-direction:column;gap:var(--space-1)}
.dd-list label{display:flex;align-items:center;gap:var(--space-7);font-size:var(--text-12);font-weight:400;padding:var(--space-3) var(--space-4);border-radius:6px;cursor:pointer;white-space:nowrap}
.dd-list label:hover{background:#f4f1ef}
.dd-list input{accent-color:var(--orange)}
.dd-foot{border-top:1px solid var(--line);margin-top:var(--space-6);padding-top:var(--space-6);text-align:right}
.dd-clear{font-size:11.5px;color:var(--orange-600);cursor:pointer;font-weight:600}

/* ---------- Email actions ---------- */
.email-actions{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--space-8) var(--space-10);margin-top:var(--space-10);padding-top:var(--space-12);border-top:1px dashed var(--line)}
.email-btn{display:inline-flex;flex-direction:column;gap:var(--space-3)}
.email-btn .sent-marker{text-align:center;min-height:13px}
.email-btn .sent{font-size:10.5px;color:var(--good);font-weight:600}
.email-btn .unsent{font-size:10.5px;color:var(--muted)}

/* ---------- Subscribe popover ---------- */
.subscribe{position:relative;display:inline-block}
.subscribe>summary{list-style:none;cursor:pointer}
.subscribe>summary::-webkit-details-marker{display:none}
.subscribe[open]>summary{border-color:var(--orange);color:var(--orange-600)}
.sub-pop{position:absolute;right:0;top:calc(100% + 6px);z-index:60;width:330px;max-width:86vw;background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow-lg);padding:var(--space-14)}
.sub-pop input{width:100%}

/* ---------- Scroll to top ---------- */
#scrolltop{position:fixed;right:22px;bottom:22px;width:42px;height:42px;border-radius:50%;border:none;background:var(--ink);color:#fff;font-size:var(--text-18);cursor:pointer;box-shadow:var(--shadow-lg);opacity:0;pointer-events:none;transform:translateY(8px);transition:.18s;z-index:50}
#scrolltop.show{opacity:1;pointer-events:auto;transform:translateY(0)}
#scrolltop:hover{background:var(--orange-600)}
@media print{#scrolltop{display:none}}

/* ---------- Calendar category toggles ---------- */
.cat-toggles{display:flex;flex-wrap:wrap;gap:var(--space-8);margin-left:auto}
.cat-chip{display:inline-flex;align-items:center;gap:var(--space-7);border:1.6px solid var(--c,#ccc);background:var(--card);border-radius:999px;padding:var(--space-6) var(--space-14);font-family:inherit;font-size:12.5px;font-weight:600;color:var(--c,#888);cursor:pointer;transition:.12s}
.cat-chip:before{content:"";width:9px;height:9px;border-radius:50%;background:var(--c,#ccc)}
.cat-chip.on{background:var(--c,#ccc);color:#fff;border-color:var(--c,#ccc)}
.cat-chip.on:before{background:var(--card)}
.cat-chip:not(.on){opacity:.85}
.fc-ev{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:11.5px}

/* ---------- Compare chart ---------- */
.compare-wrap{position:relative;height:300px}

/* ---------- Responsive ---------- */
/* Wide data tables get their own horizontal scroll on smaller screens instead
   of pushing the whole page sideways. Scoped to the card so the table keeps its
   normal layout and only the table scrolls. */
@media(max-width:820px){
  .tbl{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
  /* FullCalendar's toolbar (view buttons) wraps instead of overflowing */
  .fc .fc-header-toolbar{flex-wrap:wrap;gap:var(--space-8) var(--space-12)}
  .fc .fc-toolbar-chunk{display:flex;flex-wrap:wrap;gap:var(--space-6)}
}
/* Layout reflow for tablet & down (nav handled separately below). */
@media(max-width:900px){
  .kpis{grid-template-columns:repeat(2,1fr)}
  .cols-2,.cols-3{grid-template-columns:1fr}
  /* #62: 5-across financial strip squishes on tablet; 3-across wraps the 5th tile
     onto its own row instead. */
  .fin-strip{grid-template-columns:repeat(3,1fr)}
  .fin-grid,.cs-body,.form-grid{grid-template-columns:1fr}
  .role-add{grid-template-columns:1fr}   /* four controls never fit a phone */
  /* One column here, so pinning to column 2 would create a phantom one (see under-col2). */
  .fgroup.under-col2{grid-column:auto}
  /* Session-editor rows on a phone: stack like everything else, EXCEPT the times — four
     narrow time fields read fine 2-up and stacking them makes the card needlessly long.
     These need .form-grid on them to beat the single-column rule immediately above. */
  .form-grid.fg-2,.form-grid.fg-3{grid-template-columns:1fr}
  .form-grid.fg-4{grid-template-columns:1fr 1fr}
  .facts{grid-template-columns:repeat(2,1fr)}
  /* #16: on tablet the search bar was squishing down to nothing. Hold its size with a
     min-width + no-shrink; the topbar title/crumb block (min-width:0, ellipsis) yields
     and truncates first instead of the search input. */
  .search{max-width:none;flex:1 1 220px;min-width:220px}
  .topbar > div{min-width:0;flex:1 1 0}
}
/* Phone only: off-canvas hamburger drawer. Tablet (601-900px) uses the icon rail
   (see the .nav-mini overlay block above), so the drawer is scoped to <=600px. */
@media(max-width:600px){
  :root{--nav-w:0px}
  /* #63: full-screen drawer on phones (not a narrow 240px strip). Slide from fully
     off-screen (-100%) to full width/flex-basis when open. .side's own z-index:30
     (base rule above) already sits above .scrim's z-index:25 below, so no stacking
     change needed there. The base.html @media(max-width:700px) rule that gives the
     drawer 100dvh + safe-area padding for the logout box still applies on top of
     this — it only touches height/overflow/padding, not width/position. */
  .side{position:fixed;left:-100%;width:100vw;flex-basis:100vw;transition:.2s;box-shadow:var(--shadow-lg)}
  body.nav-open .side{left:0}
  /* #159 — the way OUT of the full-screen drawer. Sits in the brand row, opposite the logo,
     where the hamburger that opened it would be if the drawer weren't covering it. */
  /* #161 — "it should be a grey x on a transparent background and only appear in the top
     right of the menu on mobile... like a clean/modern website". No chip, no border: just
     the mark. */
  .nav-x{display:flex;align-items:center;justify-content:center;position:absolute;
    top:16px;right:14px;width:38px;height:38px;
    background:none;border:none;color:#8f8c88;font-size:26px;line-height:1;
    cursor:pointer;font-family:inherit;z-index:1}
  .nav-x:active{color:#fff}
  .hamb{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:10px;border:1px solid var(--line);background:var(--card);cursor:pointer}
  .scrim{position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:25;display:none}
  body.nav-open .scrim{display:block}

  /* Phase T — nav submenus (Team/Directory/Financials/etc.) collapsed-with-tap-to-
     expand on phone. Desktop only ever collapses .grp-items via the ≥601px
     collapsed-rail rule or the ≥901px (hover:hover) pop-out rule above — neither
     applies below 601px, so on phone .grp-items fell through to its un-media-queried
     default (a plain <div>, always visible) and every submenu rendered fully
     expanded. Start collapsed here and reveal on tap of the header (.nav-sec),
     toggled by JS adding/removing .tel-open on the parent .nav-group (see base.html). */
  .nav-group>.grp-items{display:none}
  .nav-group.tel-open>.grp-items{display:block}
  .nav-group>.nav-sec{position:relative;cursor:pointer}
  .nav-group>.nav-sec::after{content:'';position:absolute;right:12px;top:50%;
    width:7px;height:7px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;
    transform:translateY(-50%) rotate(-45deg);opacity:.55;transition:transform .15s}
  .nav-group.tel-open>.nav-sec::after{transform:translateY(-50%) rotate(45deg)}

  /* #64: collapse the topbar search to just its icon button so the page title/
     heading is visible; tapping it (telSearchToggle() in base.html) expands it to
     a full-width row across the topbar, hiding the other topbar items to make room. */
  .search{flex:0 0 auto;max-width:none;min-width:0}
  #tel-search-toggle{position:static;transform:none;width:38px;height:38px;
    border-radius:10px;border:1px solid var(--line);background:var(--card);opacity:1}
  #tel-search-input{display:none}
  body.search-open .hamb,body.search-open #tel-back,
  body.search-open .topbar>div,body.search-open .topbar>a{display:none}
  body.search-open .search{display:flex;align-items:center;gap:var(--space-8);flex:1 1 auto;max-width:none}
  body.search-open #tel-search-input{display:block;flex:1;padding-left:var(--space-12)}
}
@media(max-width:520px){
  .kpis{grid-template-columns:1fr 1fr}
  /* #62: 2-across on phone (5th tile wraps to its own row) instead of squished-5. */
  .fin-strip{grid-template-columns:repeat(2,1fr)}
  /* #65: shrink the kpi tiles further on phone so the dashboard's first row of
     tiles doesn't eat the whole screen (see the dash-money rules below for the
     matching money-tile row). */
  .kpi{padding:var(--space-12) var(--space-14) var(--space-10)}
  .kpi .num{font-size:34px}
  .kpi .num.cur{font-size:22px}
  .facts{grid-template-columns:1fr}
  .detail-head .job-badge{font-size:30px}
  .content{padding:var(--space-16)}
  /* break long emails / handles instead of overflowing (not table number cells —
     those stay whole and the table scrolls instead) */
  .profile .meta span,.kv span{overflow-wrap:anywhere}
  .fc .fc-toolbar-title{font-size:var(--text-20)}
}

/* Plays library — cast chips */
.chip{display:inline-flex;align-items:center;gap:var(--space-4);background:var(--orange-50,#fdebe0);
  border:1px solid var(--orange-200,#f6c9ad);color:#7a3a16;border-radius:14px;
  padding:var(--space-3) var(--space-9);font-size:12.5px;line-height:1.4}
.chip a{color:inherit;text-decoration:none}
.chip a:hover{text-decoration:underline}
.chip-note{cursor:help;opacity:.7;font-size:var(--text-11)}
.chip-x{background:none;border:none;color:#b0613a;cursor:pointer;font-size:var(--text-14);
  line-height:1;padding:0 0 0 var(--space-2);font-family:inherit}
.chip-x:hover{color:var(--bad)}

/* Plays library — cast as headshot circles */
.castrow{display:flex;flex-wrap:wrap;gap:var(--space-14);margin-top:var(--space-10);align-items:flex-start}
.castface{width:88px;text-align:center;position:relative}
.castface .headshot.castface-hs{width:56px;height:56px;font-size:var(--text-20);margin:0 auto var(--space-4)}
.castface-nm{font-size:11.5px;line-height:1.25;color:var(--ink);overflow-wrap:anywhere}
.castface-note{font-size:var(--text-10);color:var(--muted);line-height:1.25;margin-top:var(--space-3);cursor:help;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.castface-actions{position:absolute;top:-2px;right:6px;z-index:2;display:flex;gap:var(--space-3);align-items:center;
  opacity:0;transition:opacity .12s;pointer-events:none}
.castface:hover .castface-actions,.castface:focus-within .castface-actions{opacity:1;pointer-events:auto}
.cf-note-btn{background:var(--card);border:1px solid var(--line,#e3e3e3);border-radius:50%;width:18px;height:18px;
  line-height:1;box-shadow:0 1px 2px rgba(0,0,0,.15);color:var(--ink)}
.cf-note-dot{font-size:var(--text-10);color:var(--muted);cursor:help;margin-left:var(--space-2);position:relative}
.cf-note-pop{display:none;position:absolute;bottom:140%;left:50%;transform:translateX(-50%);
  background:#222;color:#fff;padding:var(--space-6) var(--space-9);border-radius:6px;font-size:var(--text-11);line-height:1.35;
  width:170px;white-space:normal;text-align:left;z-index:10;box-shadow:0 2px 8px rgba(0,0,0,.25)}
.cf-note-pop::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);
  border:5px solid transparent;border-top-color:#222}
.cf-note-dot:hover .cf-note-pop{display:block}
/* Plays — character cards 2-up so dragging people between roles is easier */
.char-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-10);margin-bottom:var(--space-10);align-items:start}
@media(max-width:720px){.char-grid{grid-template-columns:1fr}}
.castface-edit{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;
  background:var(--card);border:1px solid var(--line,#e3e3e3);border-radius:50%;font-size:var(--text-10);
  text-decoration:none;color:var(--ink);box-shadow:0 1px 2px rgba(0,0,0,.15)}
.castface-rm .chip-x{background:var(--card);border:1px solid var(--line,#e3e3e3);border-radius:50%;
  width:18px;height:18px;line-height:1;box-shadow:0 1px 2px rgba(0,0,0,.15)}

/* Plays — character cast tick-box picker */
.cast-pick{max-height:360px;overflow:auto;border:1px solid var(--line,#e3e3e3);border-radius:10px;
  padding:var(--space-8);display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4)}
.cast-pick-item{display:flex;align-items:center;gap:var(--space-8);padding:var(--space-4) var(--space-6);border-radius:8px;cursor:pointer;font-size:var(--text-13)}
.cast-pick-item:hover{background:var(--orange-50,#fdebe0)}
.cast-pick-item input{margin:0;flex:none}
.cast-pick-item .headshot{flex:none}
@media (max-width:640px){.cast-pick{grid-template-columns:1fr}}

/* ---------- Site-wide search dropdown (top bar, base.html) ---------- */
#tel-search-pop{position:absolute;top:100%;left:0;right:0;margin-top:var(--space-6);background:var(--card);
  border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 32px rgba(0,0,0,.14);
  overflow-y:auto;max-height:min(620px,80vh);z-index:60;padding:0 0 var(--space-6)}
/* Each category gets a banded header so Clients / Upcoming / Historical read as
   separate blocks rather than one long list (Greg, 2026-07-31). :first-child keeps
   the top of the popover clean. */
#tel-search-pop .tsr-head{position:sticky;top:0;z-index:1;
  padding:var(--space-7) var(--space-14) var(--space-5);font-size:10.5px;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  background:#f6f4f2;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
#tel-search-pop .tsr-head:first-child{border-top:0;margin-top:0}
#tel-search-pop .tsr-item{display:block;padding:var(--space-7) var(--space-14);color:inherit;text-decoration:none}
#tel-search-pop .tsr-item b{display:block;font-size:13.5px;font-weight:600;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#tel-search-pop .tsr-item small{display:block;color:var(--muted);font-size:var(--text-12);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#tel-search-pop .tsr-item.on,#tel-search-pop .tsr-item:hover{background:var(--orange-50)}
#tel-search-pop .tsr-more{font-size:12.5px;font-weight:600;color:var(--orange-600);
  padding-top:var(--space-6);padding-bottom:var(--space-8)}

/* ---------- Session edit page: grouped cards + collapsible team blocks ---------- */
.svc-edit .svc-sec{padding:var(--space-18) var(--space-20);margin-bottom:var(--space-14)}
.svc-sec-h{font-size:var(--text-14);margin:0 0 var(--space-12)}
.svc-team-head{display:flex;align-items:baseline;gap:var(--space-10);margin-bottom:var(--space-12);flex-wrap:wrap}
.svc-slots{margin-top:var(--space-16);padding-top:var(--space-12);border-top:1px dashed var(--line)}
.svc-slots-h{font-size:var(--text-12);font-weight:600;color:var(--slate-2);margin-bottom:var(--space-8)}
details.more-opts>summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:var(--space-8);
  font-size:13.5px;font-weight:600;color:var(--slate-2);user-select:none}
details.more-opts>summary::-webkit-details-marker{display:none}
.eng-block{border:1px solid var(--line);border-radius:12px;background:#fbfaf9;margin-bottom:var(--space-10)}
.eng-block[open]{background:var(--card);box-shadow:0 1px 4px rgba(0,0,0,.05)}
.eng-block.deleting{opacity:.55}
.eng-block.deleting .eng-who{text-decoration:line-through}
.eng-head{display:flex;align-items:center;gap:var(--space-9);padding:var(--space-11) var(--space-14);min-height:44px;cursor:pointer;
  list-style:none;user-select:none;flex-wrap:wrap}
.eng-head::-webkit-details-marker{display:none}
.eng-dot{width:9px;height:9px;border-radius:50%;background:#9aa0a6;flex:none}
.eng-who{font-weight:600;font-size:13.5px}
.eng-meta{color:var(--muted);font-size:12.5px}
.eng-meta:empty,.eng-fee:empty{display:none}
.eng-fee{font-weight:600;font-size:12.5px;color:var(--slate-2);white-space:nowrap}
.eng-warn{font-size:var(--text-13);cursor:help}
.eng-spacer{flex:1}
.eng-tbc{font-size:var(--text-11)}
/* Trash-icon "remove this row" toggle — replaces the raw DELETE checkbox (still in the
   DOM, just hidden via .eng-del-hidden, so it keeps submitting with the form). */
.eng-del-hidden{display:none}
.eng-del-btn{background:none;border:0;padding:var(--space-3);cursor:pointer;color:var(--muted);
  display:inline-flex;opacity:.55;line-height:0;border-radius:6px;flex:none}
.eng-del-btn:hover{opacity:1;color:var(--bad);background:#fbeaea}
.eng-del-btn .ic{width:15px;height:15px}
.eng-block.deleting .eng-del-btn{opacity:1;color:var(--bad);background:#fbeaea}
.eng-chev{color:var(--muted);font-size:var(--text-12);flex:none;transition:transform .15s ease}
details[open]>summary .eng-chev{transform:rotate(90deg)}
/* Inside a team block the old formset-row chrome is redundant — the block is the card. */
.eng-block .formset-row{border:0;background:none;border-radius:0;margin:0;
  padding:var(--space-12) var(--space-14) var(--space-14);border-top:1px dashed var(--line)}
/* S3 — cast-picker conflict statuses: pill under the who-picker for the selected
   person, plus greyed/tinted dropdown options carrying a " — ⚠ booked (26-078)" suffix. */
.tel-cast-badge{display:inline-block;margin-top:var(--space-4);padding:var(--space-1) var(--space-8);border-radius:9px;
  font-size:11.5px;font-weight:600;max-width:100%;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;vertical-align:top}
.tel-cast-available{background:#e5f3e8;color:#20794a}
.tel-cast-soft{background:#fdf3d7;color:#8a6d00}
.tel-cast-conflict,.tel-cast-unavailable{background:#fbe4e4;color:#b02a2a}
select option.tel-opt-soft{color:#8a6d00}
select option.tel-opt-conflict,select option.tel-opt-unavailable{color:var(--muted)}

/* ---------- Booking form: one-page accordion + progress checklist (2026-07-02 #6/#6b) ---------- */
.bkedit{display:grid;grid-template-columns:minmax(0,880px) 260px;gap:var(--space-18);align-items:start}
/* Annotation #147 — this used to stick at top:14px, i.e. UNDER the sticky .topbar, so its
   head sat behind the search bar; and with no height cap a long checklist simply scrolled
   off the top. Clear the header, and never let the rail exceed the viewport — it scrolls
   internally instead. */
.bkedit-side{
  position:sticky;top:calc(var(--header-h) + 14px);
  max-height:calc(100vh - var(--header-h) - 28px);
  overflow-y:auto;display:flex;flex-direction:column;gap:var(--space-10);
}
/* Annotation #148 — the page's save/cancel live up here, always in view, above the
   progress card. The buttons sit OUTSIDE <form id="bkform">, so they carry form="bkform"
   to submit it (HTML5 form-owner attribute). */
.bkedit-actions{display:flex;flex-direction:column;gap:var(--space-8);background:var(--card);
  border:1px solid var(--line);border-radius:12px;padding:var(--space-12) var(--space-14)}
.bkedit-actions .btn{width:100%;justify-content:center}
/* Annotation #148 — the autosave status line under the Save button. It now carries real
   sentences ("Not saved — Client needs fixing"), so it has to wrap legibly instead of
   being the old one-word min-height:14px strip. `.dirty` = amber: unsaved OR rejected —
   the two states where the user still has work the server hasn't got. */
.save-state{font-size:11.5px;color:var(--muted);text-align:center;min-height:14px;
  line-height:1.45;overflow-wrap:break-word}
.save-state.dirty{color:#b8860b;font-weight:600}
/* The same status line next to an INLINE save button (travel terms, and any section that
   autosaves through its own form) rather than under the pinned rail — it sits in the
   button's flex row, so centring it would read as detached from the button it reports on. */
.form-actions .save-state{text-align:left;align-self:center;min-height:0}
.acc{background:var(--card);border:1px solid var(--line);border-radius:14px;margin-bottom:var(--space-14)}
.acc>summary{display:flex;align-items:center;gap:var(--space-10);cursor:pointer;list-style:none;
  padding:var(--space-14) var(--space-18);user-select:none}
.acc>summary::-webkit-details-marker{display:none}
.acc>summary .acc-ttl{font-family:var(--display);letter-spacing:.05em;font-size:var(--text-16);
  text-transform:uppercase;color:var(--slate)}
.acc>summary .acc-sub{font-size:11.5px;font-weight:400}
.acc>summary .chev{margin-left:auto;transition:transform .15s;font-size:var(--text-11);color:var(--muted)}
.acc[open]>summary .chev{transform:rotate(90deg)}
.acc-body{padding:var(--space-4) var(--space-18) var(--space-18);border-top:1px solid var(--line)}
.cdot{width:10px;height:10px;border-radius:50%;flex:none;display:inline-block}
.cdot.st-green{background:var(--good)}
.cdot.st-amber{background:#d99a06}
.cdot.st-red{background:var(--bad)}
/* Sessions inside the edit accordion: one compact row each, edit stays on its own page */
.sess-row{border:1px solid var(--line);border-radius:10px;margin-bottom:var(--space-8);background:var(--card)}
.sess-row>summary{display:flex;align-items:center;gap:var(--space-10);padding:var(--space-9) var(--space-12);cursor:pointer;
  list-style:none;font-size:var(--text-13);user-select:none}
.sess-row>summary::-webkit-details-marker{display:none}
.sess-row>summary .ell{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sess-row>summary .chev{margin-left:auto;font-size:var(--text-10);color:var(--muted);transition:transform .15s}
.sess-row[open]>summary{border-bottom:1px solid var(--line)}
.sess-row[open]>summary .chev{transform:rotate(90deg)}
.sess-body{padding:var(--space-10) var(--space-12)}
.sess-ticks{display:flex;flex-wrap:wrap;gap:var(--space-4) var(--space-16);font-size:12.5px}
.sess-ticks .tick{color:var(--bad)}
.sess-ticks .tick.ok{color:var(--good)}
.sess-ticks .tick.amb{color:#b8860b}
/* Right-hand checklist */
.chk-card{padding:var(--space-12) var(--space-14)}
.chk-head{font-size:var(--text-11);text-transform:uppercase;letter-spacing:.06em;color:var(--muted);
  font-weight:700;margin-bottom:var(--space-4)}
.chk-row{border-bottom:1px solid var(--line)}
.chk-row:last-child{border-bottom:none}
.chk-row>summary{display:flex;align-items:center;gap:var(--space-8);padding:var(--space-8) var(--space-2);font-size:var(--text-13);
  font-weight:600;cursor:pointer;list-style:none;user-select:none}
.chk-row>summary::-webkit-details-marker{display:none}
.chk-row>summary .cchev{margin-left:auto;color:var(--muted);font-size:var(--text-10);transition:transform .15s}
.chk-row[open]>summary .cchev{transform:rotate(90deg)}
.chk-ticks{padding:0 var(--space-2) var(--space-8) var(--space-18);font-size:var(--text-12)}
.chk-ticks .tick{color:var(--bad);padding:var(--space-1) 0}
.chk-ticks .tick.ok{color:var(--good)}
.chk-ticks .tick.amb{color:#b8860b}
/* Additional contacts collapse (#7) */
.addl-wrap>summary{cursor:pointer;list-style:none;font-size:var(--text-12);font-weight:600;
  color:var(--slate-2);user-select:none}
.addl-wrap>summary::-webkit-details-marker{display:none}
.addl-wrap>summary:before{content:'▸ ';color:var(--muted)}
.addl-wrap[open]>summary:before{content:'▾ '}
@media(max-width:960px){
  .bkedit{grid-template-columns:1fr}
  /* Annotation #154 — on a phone the stacked rail burned ~230px before the first field.
     "needs to hold sticky at the top of the page just below the search bar, but not taking
     up too much real estate — flatten it into one row." The rail stops being a column and
     the actions become one sticky strip under the header; the progress card follows it. */
  /* Annotation #160 — "this is only sticky until the progress bar disappears, not across
     the whole page". position:sticky can never escape its PARENT's box, and the rail's
     parent (.bkedit-side) is only as tall as the buttons + progress card — so the save bar
     came unstuck the moment you scrolled past them, which on a phone is almost immediately.
     Fixed to the viewport instead, so it holds for the whole form. The aside then reserves
     the bar's height (it's out of flow now), or the progress card would sit under it. */
  .bkedit-side{position:static;order:-1;max-height:none;overflow:visible;display:block;
    padding-top:var(--saverail-h)}
  .bkedit-actions{position:fixed;top:var(--header-h);left:0;right:0;z-index:15;
    flex-direction:row;align-items:center;gap:var(--space-8);padding:var(--space-8) var(--space-10);
    border-radius:0 0 12px 12px;margin-bottom:var(--space-10);
    box-shadow:0 2px 8px rgba(0,0,0,.06)}
  .bkedit-actions .btn{width:auto;flex:0 0 auto;padding:var(--space-7) var(--space-14);font-size:var(--text-13)}
  .bkedit-actions .btn.primary{flex:1 1 auto}
  .save-state{margin-left:auto;text-align:right;white-space:nowrap;font-size:var(--text-11)}
  .chk-card{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-16);align-items:center;padding:var(--space-8) var(--space-12)}
  .chk-head{flex-basis:100%;margin-bottom:var(--space-2)}
  .chk-row{border-bottom:none}
  .chk-row>summary{padding:var(--space-4) 0;font-size:var(--text-12)}
  .chk-row:not([open]) .chk-ticks{display:none}
}

/* --- S2 availability overlay (availability_overlay.html) ------------------ */
.avail-form{padding:var(--space-14) var(--space-16);margin-bottom:var(--space-14)}
.avail-form-row{display:flex;flex-wrap:wrap;gap:var(--space-10) var(--space-16);align-items:flex-end;margin-bottom:var(--space-10)}
.avail-field{display:flex;flex-direction:column;gap:var(--space-4);font-size:var(--text-12);font-weight:600;color:var(--slate-2)}
.avail-field input{border:1px solid var(--line);border-radius:8px;padding:var(--space-7) var(--space-10);font:inherit;font-size:var(--text-13);background:var(--card)}
.avail-tgl{display:inline-flex;align-items:center;gap:var(--space-7);font-size:var(--text-13);color:var(--slate-2);padding-bottom:var(--space-8);cursor:pointer}
.avail-pick-head{display:flex;align-items:center;gap:var(--space-12);margin-bottom:var(--space-8)}
.avail-pick-head input{flex:0 1 260px;border:1px solid var(--line);border-radius:8px;padding:var(--space-7) var(--space-10);font:inherit;font-size:var(--text-13)}
.avail-pick-head input:focus{outline:none;border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-50)}
.avail-pick{display:flex;flex-wrap:wrap;gap:var(--space-6);max-height:170px;overflow-y:auto;padding:var(--space-2)}
.avail-pk{display:inline-flex;align-items:center;gap:var(--space-7);font-size:12.5px;border:1px solid var(--line);
  border-radius:999px;padding:var(--space-4) var(--space-12) var(--space-4) var(--space-9);cursor:pointer;background:var(--card);user-select:none}
.avail-pk:hover{border-color:#d8d4d1}
.avail-pk.sel{background:var(--orange-50);border-color:var(--orange);color:var(--orange-600);font-weight:600}
.avail-pk input{accent-color:var(--orange)}
.avail-pk em{font-style:normal;font-size:var(--text-11)}
.avail-findtime{padding:var(--space-14) var(--space-16);margin-bottom:var(--space-14)}
.avail-findtime .ft-head{font-weight:700;font-size:13.5px;margin-bottom:var(--space-8)}
.avail-findtime .ft-days{display:flex;flex-wrap:wrap;gap:var(--space-6);margin-bottom:var(--space-4)}
.pill.ft-free{background:#e7f4ec;color:var(--good)}
.pill.ft-almost{background:#fbf1da;color:#b57b00}
.ft-near{margin-top:var(--space-8);display:flex;flex-direction:column;gap:var(--space-5)}
.ft-near-row{font-size:12.5px;color:var(--slate-2);display:flex;align-items:center;gap:var(--space-8)}
.avail-legend{display:flex;flex-wrap:wrap;gap:var(--space-6);margin:0 0 var(--space-10)}
.avail-legend .pill{background:var(--card);border:1px solid var(--line);color:var(--slate-2)}
.avail-dot-tent{background:repeating-linear-gradient(45deg,var(--orange),var(--orange) 2px,#fff 2px,#fff 4px)}
.avail-wrap{overflow-x:auto;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.avail{border-collapse:separate;border-spacing:0;width:max-content;min-width:100%}
.avail th,.avail td{border-bottom:1px solid var(--line-2);border-right:1px solid var(--line-2);
  padding:var(--space-6);vertical-align:top;min-width:108px;max-width:150px}
.avail thead th{font-size:var(--text-11);text-transform:uppercase;letter-spacing:.04em;color:var(--muted);
  text-align:left;background:#fbfaf9;position:sticky;top:0;z-index:2}
.avail thead th span{display:block;font-weight:600}
.avail thead th b{font-size:var(--text-12);color:var(--slate);letter-spacing:0;text-transform:none}
.avail thead th.today b{color:var(--orange-600)}
.avail th.who,.avail td.who{position:sticky;left:0;background:var(--card);z-index:3;min-width:150px;
  border-right:1px solid var(--line);font-size:var(--text-13);font-weight:600}
.avail thead th.who{background:#fbfaf9;z-index:4}
.avail td.who a{color:inherit;text-decoration:none}
.avail td.who a:hover{color:var(--orange-600)}
.avail th.we,.avail td.we{background:#f3f0ed}
.avail td.free{background:#eef7f0}
.avail .evt{display:block;font-size:var(--text-11);line-height:1.3;border-radius:7px;padding:var(--space-3) var(--space-7);margin:0 0 var(--space-3);
  background:var(--orange-50);color:var(--orange-600);border:1px solid transparent;border-left:3px solid var(--orange)}
.avail .evt a{color:inherit;text-decoration:none;font-weight:700}
.avail .evt a:hover{text-decoration:underline}
.avail .evt small{display:block;font-size:var(--text-10);opacity:.8}
.avail .evt.tent{background:var(--card);border:1px dashed var(--orange);border-left:3px solid var(--orange);opacity:.85}
.avail .evt.blk{background:#f4f3f2;color:#555;border-left:3px solid #9aa0a6;font-weight:600}

/* ---------- Casting sheet grid (S2+) — adopts the Team Availability look ---------- */
.cs-wrap{overflow-x:auto;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.cs-grid{border-collapse:separate;border-spacing:0;width:max-content;min-width:100%;font-size:12.5px}
.cs-grid th,.cs-grid td{border-bottom:1px solid var(--line-2);border-right:1px solid var(--line-2);padding:0}
.cs-grid thead th{background:#fbfaf9;padding:var(--space-7) var(--space-8);text-align:center;white-space:nowrap;font-weight:600;
  font-size:11.5px;color:var(--slate)}
.cs-grid thead th.who,.cs-grid td.who{position:sticky;left:0;z-index:2;background:var(--card);text-align:left;
  padding:var(--space-7) var(--space-10);min-width:190px}
.cs-grid thead th.who{background:#fbfaf9;z-index:4}
.cs-grid td.who a{color:inherit;text-decoration:none;font-weight:600}
.cs-grid td.who a:hover{color:var(--orange-600)}
.cs-grid td.who small{display:block;color:var(--muted);font-weight:400;font-size:var(--text-11)}
.cs-grid td.who .hs-row{gap:var(--space-8)}
/* #23 — role/character group header row with a clear visual break. */
.cs-grouprow td{background:#f6f2ee;border-top:2px solid var(--orange-200,#f0d9c0)}
.cs-grouphead{font-weight:700;font-size:11.5px;letter-spacing:.02em;text-transform:uppercase;
  color:var(--orange-600);padding:var(--space-6) 10px!important}
.cs-grouphead.who{position:sticky;left:0;z-index:2;background:#f6f2ee}
.cs-cell{width:100%;height:42px;min-width:56px;border:0;cursor:pointer;font-weight:700;
  font-size:var(--text-12);color:#20303a;font-family:inherit;position:relative}
.cs-cell small{display:block;font-weight:500;font-size:9.5px;opacity:.8}
.cs-cell.auto{opacity:.72}
.cs-cell.auto::after{content:'';position:absolute;top:3px;right:3px;width:4px;height:4px;
  border-radius:50%;background:#8a97a0}
.cs-cell:hover{box-shadow:inset 0 0 0 2px var(--orange)}
.cs-green{background:#d9f2e2}.cs-red{background:#f6d6d6}.cs-amber{background:#fbe6c6}
.cs-tent{background:#fdf3c4}.cs-grey{background:#e6e6e6;color:#8a8a8a}
/* I5 — "unasked": we have NOT asked, so no availability is assumed. A faint grey, quieter
   than "not interested", with a dot marker (see .auto::after) so it reads as blank/pending. */
.cs-unasked{background:#f0f0f0;color:#b5b5b5}
/* #113 — a known baseline conflict (booked elsewhere / time off) with no manual reply now
   renders as its real cs-red/cs-tent colour (see views_casting._auto_status_cell), not a
   grey "unasked" cell with a corner-dot marker — the old cs-clash-hard/soft rules are gone. */
/* Lifecycle states beyond a plain reply. */
.cs-await{background:#e7ecf1;color:#5b6b7a}          /* availability check sent — awaiting */
/* #30/#114 — "Selected (pre-offer)": a distinct BLUE, clearly not green/red/orange/yellow;
   an OUTLINE star (☆) tells it apart from Confirmed's filled star at a glance. */
.cs-cast{background:#cfe4fb;color:#12406e}
.cs-offer{background:#e6dcfb;color:#4a2f8f}          /* work offer SENT — purple */
/* #115 — the ✉ glyph renders visibly smaller than ★ at the same font-size in most system
   fonts; bump just this state's marker so the two read as the same visual weight. */
.cs-cell.cs-offer{font-size:17px}
/* #116 — "Confirmed": a FILLED star (★), still on the locked-in green. */
.cs-confirmed{background:#bfe9cf;color:#0f5a33;font-weight:700}  /* confirmed / locked */
.cs-dcol-off{color:var(--muted);font-weight:400;font-size:10.5px}
/* #29 — quieter remove control (a muted trash icon that reddens on hover). */
.cs-remove{background:none;border:0;padding:var(--space-2);cursor:pointer;color:var(--muted);
  display:inline-flex;opacity:.55;line-height:0;border-radius:6px}
.cs-remove:hover{opacity:1;color:var(--bad);background:#fbeaea}
.cs-remove .ic{width:14px;height:14px}
/* #30 — push-to-session button on a session-linked date column. */
.cs-push{display:inline-flex;align-items:center;gap:var(--space-3);background:#eaf2fc;border:1px solid #bcd6f4;
  color:#12406e;border-radius:7px;padding:var(--space-2) var(--space-6);font-size:10.5px;font-weight:600;cursor:pointer;
  font-family:inherit}
.cs-push:hover{background:#dcebfb}
.cs-push .ic{width:12px;height:12px}
/* I7 — the per-date "Send offers" button reads as the work-offer (purple) action. */
.cs-push-offer{background:#efe8fc;border-color:#c9b6f2;color:#4a2f8f}
.cs-push-offer:hover{background:#e5dafa}
/* #24 — floating "why" tooltip. */
.cs-tip{position:absolute;z-index:60;max-width:240px;background:#20303a;color:#fff;font-size:11.5px;
  line-height:1.35;padding:var(--space-7) var(--space-9);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.22);pointer-events:none}
/* Coverage badge — merged into the grid header `<th>` from the old coverage strip
   (S2g/h). Compact full/part/none states reuse the same colour scheme. */
.cs-covbadge{display:inline-flex;align-items:center;gap:var(--space-3);border:1px solid var(--line);
  border-radius:7px;padding:var(--space-1) var(--space-5);font-size:10.5px;margin-top:var(--space-2)}
.cs-covbadge .cov-count b{font-size:var(--text-11)}
.cs-covbadge.cov-full{background:#e7f7ee;border-color:#b8e3ca}
.cs-covbadge.cov-part{background:#fdf3e2;border-color:#eecfa0}
.cs-covbadge.cov-none{background:#faf8f6}
.cs-covbadge .pinch{color:var(--bad);font-size:var(--text-11);cursor:help;line-height:1}
/* S2j — client scheduling-rule flag on a casting-sheet date column (advisory only). */
.cs-clientflag{display:inline-flex;align-items:center;gap:var(--space-3);background:#fdf0e2;border:1px solid #f0cfa0;
  color:#8a5a10;border-radius:7px;padding:var(--space-1) var(--space-6);font-size:var(--text-10);font-weight:600;margin-top:var(--space-2);cursor:help}
.cs-clientflag .ic{width:10px;height:10px}
/* S2l — "Suggest 5 to offer" advisory highlight (client-side only, never a DB fact). */
th.cs-suggested{background:#e7f7ee;box-shadow:inset 0 0 0 2px var(--good)}
.cs-legend{display:flex;gap:var(--space-12);flex-wrap:wrap;font-size:var(--text-12);margin:var(--space-10) 0}
.cs-legend span{display:inline-flex;align-items:center;gap:var(--space-5)}
.cs-legend i{width:13px;height:13px;border-radius:3px;display:inline-block}
.cs-legend i.cs-green{background:#d9f2e2}.cs-legend i.cs-cast{background:#cfe4fb}
.cs-legend i.cs-red{background:#f6d6d6}.cs-legend i.cs-amber{background:#fbe6c6}
.cs-legend i.cs-tent{background:#fdf3c4}.cs-legend i.cs-grey{background:#e6e6e6}
.cs-editor{position:absolute;z-index:50;background:var(--card);border:1px solid var(--line);
  border-radius:12px;box-shadow:0 12px 34px rgba(0,0,0,.18);padding:var(--space-12);width:238px}
.cs-editor h4{margin:0 0 var(--space-8);font-size:var(--text-13)}
.cs-editor label{display:block;font-size:11.5px;color:var(--muted);margin:var(--space-7) 0 var(--space-2)}
.cs-editor select,.cs-editor input[type=time],.cs-editor input[type=text]{width:100%;font-family:inherit;
  font-size:12.5px;padding:var(--space-6) var(--space-7);border:1px solid var(--line);border-radius:8px}
.cs-editor .row2{display:flex;gap:var(--space-6)}
.cs-editor .acts{display:flex;gap:var(--space-6);margin-top:var(--space-10)}
.addpanel summary{cursor:pointer;font-weight:600;font-size:13.5px;padding:var(--space-6) 0}

/* ---------- Whereabouts (Phase W) ---------- */
.wa-chip{display:inline-flex;align-items:center;gap:var(--space-4);font-size:var(--text-11);font-weight:600;
  border-radius:999px;padding:var(--space-2) var(--space-9);border:1px solid transparent;white-space:nowrap;cursor:default;
  max-width:130px;overflow:hidden;text-overflow:ellipsis;margin:var(--space-1) 0}
a.wa-chip{text-decoration:none;cursor:pointer}
a.wa-chip:hover{filter:brightness(.96)}
/* Kind colours mirror the calendar's category legend (CATEGORY_META + travel/time-off
   in views.py) so a category reads the same on the whereabouts grid and the calendar. */
.wa-chip.office{background:#fbeeda;color:#9a5b12;border-color:#f2d6ac}    /* Office  #f0a85c */
.wa-chip.remote{background:#efe6f7;color:#6b3fa0;border-color:#ddccef}    /* Remote — purple, distinct from travel */
.wa-chip.travel{background:#e6eef7;color:#2b5c88;border-color:#cadcee}    /* Travel  #3d7fb8 */
.wa-chip.delivery{background:#e6f3ec;color:#3a7a58;border-color:#cbe7d6}  /* Delivery #5fae84 */
.wa-chip.off{background:#ececec;color:#5f5f5f;border-color:#d8d8d8}       /* Time off — grey, distinct from office */
.wa-chip.holiday{background:#fbe4e4;color:#8a2f53;border-color:#f3c6d8}   /* Public holiday */
.wa-chip.overcap{background:#fbe4e0;color:var(--bad);border-color:#f3c6bd}  /* Over desk capacity — advisory only */
/* Hold = tentative (W9a): hatched + translucent, everywhere. */
.wa-chip.tentative{opacity:.72;border-style:dashed;
  background-image:repeating-linear-gradient(45deg,transparent,transparent 4px,rgba(255,255,255,.65) 4px,rgba(255,255,255,.65) 8px)}
/* Held sessions on the main FullCalendar read tentative, not solid-busy: a diagonal
   hatch layered over the (inline-set) tint + a dashed border. Applies in month/week/day;
   day + agenda also get a 'HOLD' text marker (calendar.html eventDidMount). */
.fc-event.tentative{border-style:dashed!important;
  background-image:repeating-linear-gradient(45deg,transparent,transparent 5px,rgba(255,255,255,.6) 5px,rgba(255,255,255,.6) 10px)}
.fc-event.tentative:hover{filter:none}
/* Team-today (dashboard) + roster avatars for people whose day is only tentatively held. */
.avatar.tentative{opacity:.7;outline:1.5px dashed #b8860b;outline-offset:1px}
/* Suggested/pattern rows: grey until the member confirms. */
.wa-chip.suggested{opacity:.6;border-style:dotted}
/* Add / edit your own whereabouts straight from the grid (W5). */
.wa-cell{position:relative}
.wa-add,.wa-chip-edit{display:inline-block}
.wa-add>summary{list-style:none;cursor:pointer;width:18px;height:18px;line-height:16px;
  text-align:center;border-radius:50%;border:1px dashed var(--line,#d8d8d8);color:#9a938a;
  font-size:var(--text-12);margin-top:var(--space-2);opacity:0;transition:opacity .1s}
.wa-add>summary::-webkit-details-marker{display:none}
.wa-cell:hover .wa-add>summary,.wa-add[open]>summary{opacity:1}
.wa-add[open]>summary{background:#f0a85c;color:#fff;border-style:solid;border-color:#f0a85c}
/* Own pill = click-to-edit; pencil only on hover, never persistent. */
.wa-chip-edit>summary{list-style:none;cursor:pointer}
.wa-chip-edit>summary::-webkit-details-marker{display:none}
.wa-pencil{margin-left:var(--space-4);font-size:var(--text-10);opacity:0;transition:opacity .1s}
.wa-chip-edit>summary:hover .wa-pencil,.wa-chip-edit[open] .wa-pencil{opacity:.85}
/* The add/edit popover. */
.wa-add-menu{position:absolute;z-index:50;margin-top:var(--space-4);background:var(--card);
  border:1px solid var(--line,#e3e0db);border-radius:9px;box-shadow:0 8px 24px rgba(0,0,0,.16);
  padding:var(--space-8);display:flex;flex-direction:column;gap:var(--space-6);min-width:172px;max-width:90vw;text-align:left}
/* Right-hand day columns: open the popover leftward so it stays on-screen. */
td.wa-cell:nth-last-child(-n+2) .wa-add-menu{left:auto;right:0}
/* The grid scrolls on narrow screens, but on desktop let popovers overflow the
   card instead of being clipped by overflow-x:auto. */
.wa-scroll{overflow-x:auto}
@media(min-width:900px){.wa-scroll{overflow:visible}}
/* Headshots inside the small round avatars (dashboard 'Team today' etc.). */
.avatar{overflow:hidden}
.avatar img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}
/* Transient toast (e.g. the one-click "Report a problem" confirmation) — created ONLY
   by waToast() in base.html, which is the site-wide notification component. The bare
   .tel-toast rules below keep the standalone/fallback case working; inside the
   #tel-toasts stack the container owns the positioning. */
.tel-toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%) translateY(10px);
  background:#23211f;color:#fff;padding:var(--space-11) var(--space-18);border-radius:11px;font-size:var(--text-13);font-weight:500;
  z-index:10000;opacity:0;transition:opacity .25s,transform .25s;box-shadow:0 10px 30px rgba(0,0,0,.28);
  max-width:90vw;text-align:center}
.tel-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
/* Stack: newest nearest the bottom edge, so toasts queue instead of overlapping.
   Spans the viewport (left:0;right:0) and centres its children rather than sitting at
   left:50% — a shrink-to-fit box at left:50% only has HALF the viewport to lay out in,
   which wrapped a short toast onto three lines on a phone. */
.tel-toasts{position:fixed;bottom:22px;left:0;right:0;z-index:10000;
  display:flex;flex-direction:column-reverse;align-items:center;gap:var(--space-8);
  padding:0 var(--space-12);pointer-events:none}
.tel-toasts .tel-toast{position:static;transform:translateY(10px);max-width:100%;
  display:flex;align-items:center;gap:var(--space-12);text-align:left;pointer-events:auto;
  padding:var(--space-11) var(--space-12) var(--space-11) var(--space-18)}
.tel-toasts .tel-toast.show{transform:translateY(0)}
.tel-toast-msg{flex:1 1 auto;min-width:0}
/* Action (Undo). Orange on the near-black toast measures 5.2:1 — passes AA (the same
   orange on white does NOT; don't reuse this pairing on a light surface). */
.tel-toast-act{flex:none;background:none;border:0;font:inherit;font-weight:700;cursor:pointer;
  color:#f16622;padding:var(--space-3) var(--space-6);border-radius:6px;text-decoration:underline;text-underline-offset:2px}
.tel-toast-act:hover{background:rgba(255,255,255,.1)}
.tel-toast-x{flex:none;background:none;border:0;cursor:pointer;color:#d8d3ce;
  font-size:17px;line-height:1;padding:var(--space-3) var(--space-6);border-radius:6px}
.tel-toast-x:hover{background:rgba(255,255,255,.1);color:#fff}
.tel-toast-act:focus-visible,.tel-toast-x:focus-visible{outline:2px solid #fff;outline-offset:1px}
/* Error variant — orange would fall to 2.8:1 on this red, so the action goes pale. */
.tel-toast.err{background:#8e1f1f}
.tel-toast.err .tel-toast-act{color:#ffd9c9}
/* On a phone the toast is nearly full-width, so it runs under the two fixed bottom
   controls — .tel-anno-fab (bottom-left, admins) and #scrolltop (bottom-right, 42px).
   The Annotate pill was covering the job number, i.e. WHICH booking moved. Lift the
   stack clear of both; on desktop the toast is centred and never reaches them. */
@media(max-width:700px){.tel-toasts{bottom:76px}}
details.past-log>summary::-webkit-details-marker{display:none}
details.past-log[open]>summary .chev{transform:rotate(90deg)}
/* Report-a-problem confirm dialog (attaches context automatically, but the user
   writes a note + confirms Send, so a stray click never files a blank report). */
.wa-modal{position:fixed;inset:0;z-index:10001;background:rgba(20,18,16,.5);
  display:flex;align-items:center;justify-content:center;padding:var(--space-20)}
.wa-modal[hidden]{display:none}   /* class display:flex would otherwise beat the hidden attr */
.wa-modal-card{background:var(--card);border-radius:14px;box-shadow:0 20px 60px rgba(0,0,0,.3);
  padding:var(--space-20) var(--space-22);max-width:440px;width:100%}
.wa-modal-card h3{display:flex;align-items:center;gap:var(--space-8);margin:0 0 var(--space-6)}
.wa-modal-card h3 .ic{width:20px;height:20px}
.wa-modal-card p{font-size:var(--text-13);margin:0 0 var(--space-12)}
.wa-modal-card textarea{width:100%;box-sizing:border-box;min-height:96px;font-family:inherit;
  font-size:var(--text-14);padding:var(--space-10);border:1px solid var(--line);border-radius:9px;resize:vertical}
.wa-modal-actions{display:flex;justify-content:flex-end;gap:var(--space-10);margin-top:var(--space-14)}
.wa-add-menu .wa-f{width:100%;font-family:inherit;font-size:12.5px;padding:var(--space-5) var(--space-7);
  border:1px solid var(--line,#e3e0db);border-radius:6px;background:var(--card);color:#23211f}
.wa-times{display:flex;align-items:center;gap:var(--space-5)}
.wa-times input{flex:1;font-family:inherit;font-size:12.5px;padding:var(--space-4) var(--space-5);
  border:1px solid var(--line,#e3e0db);border-radius:6px}
.wa-hint{font-size:10.5px;color:#9a938a}
.wa-row2{display:flex;gap:var(--space-6)}
.wa-add-menu .wa-save{flex:1;border:none;background:#5fae84;color:#fff;font-family:inherit;
  font-size:12.5px;font-weight:600;padding:var(--space-6) var(--space-9);border-radius:6px;cursor:pointer}
.wa-add-menu .wa-save:hover{filter:brightness(.95)}
.wa-add-menu .wa-del{border:1px solid #e0b4b4;background:var(--card);color:#b23b3b;font-family:inherit;
  font-size:12.5px;font-weight:600;padding:var(--space-6) var(--space-9);border-radius:6px;cursor:pointer}
.wa-add-menu .wa-del:hover{background:#fbeaea}

/* Set-my-week day cards (mobile-first) */
.wa-weekform{display:flex;flex-direction:column;gap:var(--space-10)}
.wa-day{padding:var(--space-12) var(--space-14)}
.wa-day.wa-weekend{background:#fbfaf9}
.wa-day-head{display:flex;align-items:center;gap:var(--space-8);flex-wrap:wrap;font-size:var(--text-14)}
.wa-derived{display:flex;gap:var(--space-6);flex-wrap:wrap;margin-top:var(--space-7)}
.wa-derived .wa-chip{filter:grayscale(.25)}
.wa-pick{display:flex;gap:var(--space-6);flex-wrap:wrap;margin-top:var(--space-9)}
.wa-opt input{position:absolute;opacity:0;pointer-events:none}
.wa-opt span{display:inline-block;border:1px solid var(--line);border-radius:999px;
  padding:var(--space-7) var(--space-14);font-size:12.5px;font-weight:600;color:var(--slate);cursor:pointer;
  background:var(--card);transition:.1s;user-select:none}
.wa-opt input:checked + span{border-color:currentColor}
.wa-opt.office input:checked + span{background:#e7f4ec;color:#1f7a45}
.wa-opt.remote input:checked + span{background:#e8f1f8;color:#2a628f}
.wa-opt.travel input:checked + span{background:#f1eaf8;color:#6a3fb5}
.wa-opt.off input:checked + span{background:#ececec;color:#5f5f5f}
.wa-opt:not(.office):not(.remote):not(.travel) input:checked + span{background:#f4f3f2}
.wa-times{display:flex;gap:var(--space-8);flex-wrap:wrap;align-items:flex-end;margin-top:var(--space-9)}
.wa-times.off{display:none}
.wa-times label{font-size:var(--text-11);color:var(--muted);display:flex;flex-direction:column;gap:var(--space-2)}
.wa-times input{border:1px solid var(--line);border-radius:8px;padding:var(--space-7);font-family:inherit;font-size:var(--text-13)}
.wa-confirm{position:sticky;bottom:0;background:linear-gradient(transparent,var(--bg) 30%);
  padding:var(--space-12) 0 var(--space-8);display:flex;align-items:center;gap:var(--space-12);flex-wrap:wrap}
.wa-pattern-row{display:flex;gap:var(--space-6);align-items:center;flex-wrap:wrap}
.wa-pattern-row select,.wa-pattern-row input{border:1px solid var(--line);border-radius:8px;
  padding:var(--space-6);font-family:inherit;font-size:12.5px}

/* Team grid (W5a) */
.wa-grid{width:100%;border-collapse:collapse;font-size:var(--text-12)}
.wa-grid th,.wa-grid td{border-bottom:1px solid var(--line);border-right:1px solid #f0eeea;
  padding:var(--space-7) var(--space-8);vertical-align:top;min-width:104px}
.wa-grid thead th{font-size:var(--text-11);text-transform:uppercase;letter-spacing:.03em;color:var(--muted);
  text-align:left;background:#fbfaf9}
.wa-grid td.wa-name,.wa-grid th:first-child{position:sticky;left:0;background:var(--card);z-index:2;
  min-width:150px;font-weight:600;font-size:var(--text-13);white-space:nowrap;border-right:1px solid var(--line)}
.wa-grid td.wa-name .avatar{display:inline-flex;align-items:center;justify-content:center;
  border-radius:50%;background:#cfc9c0;color:#fff;margin-right:var(--space-6);vertical-align:middle}
.wa-grid th.wa-today,.wa-grid td.wa-today{background:#fdf6ef}
.wa-grid td.wa-holiday-col{background:#fdf5f7}
.wa-grid td .wa-chip{display:inline-flex;margin:0 var(--space-3) var(--space-3) 0;cursor:pointer}
.wa-legend{display:flex;gap:var(--space-5);flex-wrap:wrap;margin-left:auto}
.wa-tip{position:fixed;z-index:9999;pointer-events:none;max-width:300px;background:var(--card);
  border:1px solid var(--line);border-left:3px solid var(--orange);border-radius:8px;
  box-shadow:0 8px 28px rgba(0,0,0,.18);padding:var(--space-8) var(--space-11);font-size:var(--text-12);line-height:1.45;
  color:var(--ink);opacity:0;transition:opacity .08s ease}
.wa-tip.show{opacity:1}

/* Dashboard "Team today" card (W5c) */
.wa-today-card{margin-top:var(--space-16);padding:var(--space-12) var(--space-16);display:flex;align-items:center;gap:var(--space-14);flex-wrap:wrap}
.wa-today-card h3{font-size:var(--text-14);margin:0}
.wa-today-grp{display:flex;align-items:center;gap:var(--space-6);font-size:12.5px}
.wa-today-grp .avatar{width:24px;height:24px;font-size:var(--text-10);display:inline-flex;align-items:center;
  justify-content:center;border-radius:50%;background:#cfc9c0;color:#fff;margin-left:-6px;border:2px solid #fff}
.wa-today-grp .avatar:first-of-type{margin-left:0}

/* --- Q2: element annotation toolbar (Vercel-toolbar style) ------------------ */
body.tel-anno-on, body.tel-anno-on * { cursor: crosshair !important; }
.tel-anno-outline{ position:fixed; z-index:2147483000; pointer-events:none;
  border:2px solid #7c4dff; background:rgba(124,77,255,.10); border-radius:4px;
  display:none; box-shadow:0 0 0 2px rgba(124,77,255,.15); transition:all .04s linear; }
/* Symbol only, and round — the pen, then the x (Greg, 2026-07-31, the same call he made on
   the messages bubble). Sized to match .ccw-fab in the opposite corner so the two floating
   controls read as a pair rather than as two unrelated widgets. */
.tel-anno-fab{ position:fixed; left:16px; bottom:16px; z-index:2147483001; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center;
  width:46px; height:46px; padding:0; line-height:1;
  border:1px solid #7c4dff; background:var(--card); color:#7c4dff; font-weight:700;
  font-size:var(--text-18);
  border-radius:50%; box-shadow:0 4px 14px rgba(0,0,0,.14); }
.tel-anno-fab.on{ background:#7c4dff; color:#fff; }
.tel-anno-dialog{ position:fixed; z-index:2147483002; width:300px; background:var(--card);
  border:1px solid var(--line); border-radius:12px; box-shadow:0 12px 34px rgba(0,0,0,.22);
  padding:var(--space-12); cursor:auto; }
.tel-anno-dialog .tel-anno-picked{ font-size:var(--text-12); color:var(--muted); background:#f6f5f2;
  border-radius:8px; padding:var(--space-7) var(--space-9); margin-bottom:var(--space-8); max-height:52px; overflow:auto;
  word-break:break-word; }
.tel-anno-dialog textarea{ width:100%; min-height:64px; border:1px solid var(--line);
  border-radius:8px; padding:var(--space-8); font:inherit; font-size:var(--text-13); resize:vertical; box-sizing:border-box; }
.tel-anno-actions{ display:flex; justify-content:flex-end; gap:var(--space-8); margin-top:var(--space-9); }
.tel-anno-btn{ cursor:pointer; border-radius:8px; font-size:var(--text-13); padding:var(--space-7) 13px; border:1px solid var(--line); }
.tel-anno-btn.ghost{ background:var(--card); color:var(--slate-2); }
.tel-anno-btn.primary{ background:#7c4dff; color:#fff; border-color:#7c4dff; font-weight:600; }
.tel-anno-btn:disabled{ opacity:.6; cursor:default; }

/* ---- Parity Phase 2: list filter bars + active pill state + raw-JSON editor ---- */
.filterbar{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-7); margin:0 0 var(--space-14); }
.filterbar-lbl{ font-size:11.5px; font-weight:700; text-transform:uppercase;
  letter-spacing:.03em; color:var(--muted); }
a.pill{ text-decoration:none; border:1px solid var(--line); cursor:pointer; }
a.pill:hover{ background:#e4e4e4; }
.pill.on{ background:var(--orange-600); border-color:var(--orange-600); color:#fff; }
.pill.on:hover{ background:var(--orange-700); }
.json-adv{ margin:var(--space-18) 0 var(--space-4); border:1px dashed var(--line); border-radius:11px;
  padding:var(--space-12) var(--space-14); background:#fafafa; }
.json-adv>summary{ cursor:pointer; font-weight:600; font-size:var(--text-13); }
.json-adv-help{ font-size:var(--text-12); margin:var(--space-10) 0 var(--space-6); }
.json-adv-ta{ width:100%; font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12.5px; line-height:1.5; }

/* ---- Collapsed "+ add" field groups (entity_form.html, opt-in via a form class's
   field_groups — see nexus2.forms.Styled) ---- */
.field-group{ margin:var(--space-14) 0 0; border:1px dashed var(--line); border-radius:11px;
  padding:var(--space-12) var(--space-14); background:#fafafa; }
.field-group>summary{ cursor:pointer; font-weight:600; font-size:var(--text-13); color:var(--slate-2); }
.field-group-hint{ font-size:var(--text-12); color:var(--muted); margin:var(--space-8) 0 0; }
.field-group .form-grid{ margin-top:var(--space-10); }

/* --- Annotate button show/hide (nav toggle) --- */
.annotate-hide-toggle .anno-show-lbl{ display:none; }
body.tel-anno-hidden .annotate-hide-toggle .anno-hide-lbl{ display:none; }
body.tel-anno-hidden .annotate-hide-toggle .anno-show-lbl{ display:inline; }
body.tel-anno-hidden .tel-anno-fab{ display:none !important; }

/* --- Dashboard KPI tiles: keep to two rows on tablet/phone --- */
@media (max-width:1180px){ .grid.kpis{ grid-template-columns:repeat(3,1fr); } }
@media (max-width:640px){ .grid.kpis{ grid-template-columns:repeat(2,1fr); } }

/* --- #65: dashboard money row (Income/Outgoings/Margin) stays 2-up + compact on
   phone instead of inheriting .cols-3's single-column stack — combined with the
   .kpi shrink above, this keeps the first 7 dashboard tiles from taking over the
   whole screen on mobile. Scoped to .dash-money (dashboard.html only) so the
   shared .cols-3 class keeps its normal look on financials/cost_report/resources. */
@media (max-width:600px){
  .dash-money{ grid-template-columns:1fr 1fr; gap:var(--space-10); }
  .dash-money .money-tile{ padding:var(--space-10) var(--space-12); }
  .dash-money .money-tile .big{ font-size:22px; }
}

/* --- Team-today card as a compact status table (Office/Remote/… columns) --- */
.wa-today-tbl{ width:100%; border-collapse:collapse; margin-top:var(--space-6); }
.wa-today-tbl th{ text-align:left; font-size:var(--text-11); font-weight:600; color:var(--slate-2);
  padding:0 var(--space-10) var(--space-6) 0; white-space:nowrap; vertical-align:bottom; }
.wa-today-tbl td{ vertical-align:top; padding:var(--space-8) var(--space-10) var(--space-4) 0; border-top:1px solid var(--line); }
.wa-today-col{ display:flex; flex-wrap:wrap; gap:var(--space-4); max-width:180px; }
.wa-today-tbl .wa-empty{ color:var(--slate-2); font-size:var(--text-12); }

/* ============================================================================
   MOBILE HARDENING (phones, <=700px) — 2026-07-13
   Two long-standing phone complaints, fixed at the source:
     1. Pages were wider than the screen, so the browser zoomed the whole page
        out to fit → tiny text + a jittery half-collapsed menu. Cause: long
        unbreakable text (emails, URLs, org names) and the odd wide element
        pushing the layout past the viewport.
     2. A page's top action-button row (booking detail, profiles, calendar
        filters) wrapped into 5-6 lines and shoved the real content off-screen.
   ============================================================================ */

/* --- 1. Never let the page pan sideways; keep text inside its box. ---------- */
@media (max-width:700px){
  /* overflow-x:clip stops the page itself from ever being wider than the phone
     (so no pinch-to-fit), WITHOUT creating a scroll container — sticky topbar &
     the off-canvas nav still work. Wide data tables/calendars keep their own
     inner overflow-x:auto scrollers, so nothing real is lost. */
  html,body{max-width:100%;overflow-x:clip}
  /* Long words / emails / URLs / names wrap onto the next line instead of
     spilling past the right edge. Doesn't touch white-space:nowrap table cells
     (those live in scrollable tables). */
  /* overflow-wrap (not word-break): breaks long emails/URLs when they'd overflow,
     but keeps each element's min-content intact — so table cells don't collapse to
     one-character-per-line the way word-break:break-word made them. */
  .content{overflow-wrap:break-word;overflow-x:clip}
  /* Media & embeds never exceed the column. */
  .content img,.content video,.content iframe,.content table{max-width:100%}
}

/* --- 2. Fold tall action-button clusters behind a compact toggle. ----------
   base.html tags known clusters (.detail-head/.profile .actions, #cat-toggles)
   with .m-collapsed + inserts a .m-collapse-btn before them. Everything here is
   phone-scoped, so desktop/tablet is untouched: the toggle stays hidden and the
   cluster shows exactly as it always has. */
.m-collapse-btn{display:none}
@media (max-width:700px){
  .m-collapse-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-8);
    width:100%;margin:var(--space-4) 0;padding:var(--space-11) 15px;border-radius:11px;border:1px solid var(--line);
    background:var(--card);color:var(--ink);font-family:inherit;font-weight:600;font-size:15px;cursor:pointer}
  .m-collapse-btn::after{content:'';width:8px;height:8px;border-right:2px solid currentColor;
    border-bottom:2px solid currentColor;transform:rotate(45deg);opacity:.5;
    margin-top:-3px;transition:transform .15s}
  .m-collapse-btn[aria-expanded=true]::after{transform:rotate(-135deg);margin-top:var(--space-3)}
  .m-collapsed{display:none !important}
  /* When an .actions cluster is expanded, stack its buttons full-width so each is
     an easy tap target instead of a cramped wrapping row. */
  .m-actions-open{display:flex;flex-direction:column;align-items:stretch;gap:var(--space-8);width:100%}
  .m-actions-open > a,.m-actions-open > button,.m-actions-open > form,
  .m-actions-open > .subscribe,.m-actions-open > details{width:100%}
  .m-actions-open > a.btn,.m-actions-open > button.btn,
  .m-actions-open .subscribe > summary,.m-actions-open form > button,
  .m-actions-open form > .btn{width:100%;justify-content:center}
}

/* --- 3. Record/log tables that stack into cards on phones. ------------------
   Multi-column tables (booking-form history, emails, edits log) were bleeding
   off the right edge — and once text-wrapping kicked in, a squeezed column
   rendered one character per line. On phones, mark such a table `.mtbl` and its
   rows become stacked cards: each cell on its own full-width line, readable, no
   sideways scroll. Inline width:/white-space:nowrap on the cells are overridden.
   Desktop keeps the normal table. */
@media (max-width:600px){
  table.mtbl,table.mtbl tbody,table.mtbl tr,table.mtbl td{display:block;width:auto}
  table.mtbl tr{border:1px solid var(--line);border-radius:12px;padding:var(--space-8) var(--space-12);
    margin:0 var(--space-12) var(--space-10);background:var(--card)}
  table.mtbl tr + tr{margin-top:0}
  table.mtbl td{width:auto !important;max-width:100% !important;
    white-space:normal !important;border:none !important;padding:var(--space-3) 0 !important;
    vertical-align:top}
  table.mtbl td:empty{display:none}          /* drop blank spacer cells */
}

/* ---------- Session accordion (shared) ------------------------------------
   ONE accordion, two surfaces: the booking detail page's "Services & delivery"
   list and the bookings list (`.svc` cards, _ml_card.html). The class contract
   is the detail page's, reused verbatim rather than reinvented:
     .svc-acc   a session panel (<details>);  .is-past  a delivered one
     summary    the header row;  .chev  the ▶ that rotates when open
     .svc-sec   the session EDITOR's own internal cards — never a session panel
   These rules were the detail page's inline <style>; they live here so the two
   surfaces can't drift apart.

   Governing rule (Greg, annotation #176): "past sessions don't need to be
   hidden, just collapsed accordions." Collapse only — nothing may become
   unreachable, and print re-opens the lot (see the @media print block below). */
.svc-acc>summary{cursor:pointer;list-style:none;display:flex;gap:var(--space-12);align-items:center;padding:var(--space-10) var(--space-14);font-size:var(--text-13);user-select:none}
.svc-acc>summary::-webkit-details-marker{display:none}
.svc-acc[open]>summary{border-bottom:1px solid var(--line)}
.svc-acc>summary .chev{margin-left:auto;transition:transform .15s;font-size:var(--text-11);color:var(--muted)}
.svc-acc[open]>summary .chev{transform:rotate(90deg)}
.svc-acc .cdot{width:9px;height:9px;border-radius:50%;flex:none;display:inline-block}
.cdot.st-green{background:var(--good)}.cdot.st-amber{background:#d99a06}.cdot.st-red{background:var(--bad)}
/* A delivered session reads quieter, but stays a normal row you can open. */
.svc-acc.is-past>summary{opacity:.72}
.svc-acc.is-past>summary:hover{opacity:1}

/* --- Bookings-list card header (.ml-head) ----------------------------------
   The list card's header IS the <summary>. It carries `.svc-top` to keep the
   look it always had, plus `.ml-head` — its OWN hook, because `.svc .svc-top` is
   shared with _session_card / _booking_svc_card / _draft_rows and none of the
   rules below should reach those.
   `.svc .svc-top` already out-specifies `.svc-acc>summary` on padding and
   background; font-size is the one thing it never set, so hand that back — else
   the header takes the detail page's compact 13px and the client name shrinks. */
.svc-acc>.ml-head{font-size:inherit}
/* Collapsed = no rule under the header (there's no body below it to divide). */
.svc-acc:not([open])>.ml-head{border-bottom:none}
/* The header row is also the link into the job — clicking the date/client opens
   the booking, clicking anywhere else (or the chevron) expands the card. */
.svc-acc>.ml-head .ml-open{display:flex;gap:var(--space-12);flex:1 1 0;min-width:0;
  text-decoration:none;color:inherit}
.svc-acc>.ml-head .ml-times{flex:0 0 auto;margin:0}
/* The header row is mostly that link, so the chevron is the deliberate tap
   target for expanding — give it a real 34px hit area. */
.svc-acc>.ml-head .chev{flex:none;display:inline-flex;align-items:center;justify-content:center;
  min-width:34px;min-height:34px;margin-left:0}

/* Phone: the header row MUST wrap ------------------------------------------
   Measured at 390px before this rule: the times block is ~180px and never
   shrinks (flex:0 0 auto), the date chip ~73px — between them they ate the 357px
   row and left the client/title column TWO PIXELS wide, so it wrapped one
   character per line and the "collapsed" card stood 800px tall. Collapsing
   bought nothing on the surface this list is used on most.
   So on phones the row wraps: line 1 is the date + who (full width), line 2 the
   times with the chevron pushed out right. Measured after: an 800px "collapsed"
   card becomes ~180px, and a week goes from ~5100px to ~1200px of scroll. */
@media(max-width:600px){
  .svc-acc>.ml-head{flex-wrap:wrap;row-gap:var(--space-8);padding:var(--space-12) var(--space-14)}
  .svc-acc>.ml-head .ml-open{flex:1 1 100%;min-width:0}
  .svc-acc>.ml-head .ml-times{flex:1 1 auto}
  .svc-acc>.ml-head .chev{margin-left:auto}
  /* The chip sizes to its text; it must not stretch to the wrapped row's height. */
  .svc-acc>.ml-head .date-chip{align-self:flex-start}
}

/* --- Print ----------------------------------------------------------------
   A collapsed accordion MUST NOT print blank. The bookings list is carried on
   paper — that is the whole point of it — so print force-opens every session
   panel whatever is collapsed on screen. Scoped to the accordion classes so it
   can't spring open unrelated <details> (nav popovers, the edits log).
   ::details-content is the only CSS handle on a closed <details>'s contents;
   sessacc.js also opens them on beforeprint for engines without it. */
@media print{
  .svc-acc::details-content{content-visibility:visible!important;
    block-size:auto!important;opacity:1!important}
  .svc-acc>summary{border-bottom:1px solid var(--line)}
  .svc-acc>summary .chev{display:none}
  .svc-acc.is-past>summary{opacity:1}
  .svc-acc{break-inside:avoid}
}

/* "What's new" release log (settings/whats_new.html) — one accordion row per
   NEXUS2.md/ROADMAP.md section. .rl-body renders releaselog.render_markdown()'s
   output, so it needs its own light type styles (headings/lists/quote/code)
   since it's the only page in the app rendering hand-parsed Markdown bodies. */
.rl-entry{border-top:1px solid var(--line)}
.rl-entry:first-of-type{border-top:none}
.rl-entry>summary{list-style:none;cursor:pointer;padding:var(--space-14) var(--space-16);display:flex;
  align-items:center;justify-content:space-between;gap:var(--space-12);flex-wrap:wrap}
.rl-entry>summary::-webkit-details-marker{display:none}
.rl-entry>summary .chev{margin-left:auto;font-size:var(--text-11);color:var(--muted);
  transition:transform .15s;flex:none}
.rl-entry[open]>summary .chev{transform:rotate(90deg)}
.rl-summary-main{display:flex;align-items:center;gap:var(--space-10);min-width:0}
.rl-summary-meta{display:flex;align-items:center;gap:var(--space-10);flex:none}
.rl-body{padding:0 var(--space-16) var(--space-16);font-size:13.5px;line-height:1.55}
.rl-body p{margin:0 0 var(--space-10)}
.rl-body p:last-child{margin-bottom:0}
.rl-body h4{font-size:13.5px;margin:var(--space-14) 0 var(--space-6)}
.rl-body h4:first-child{margin-top:0}
.rl-body ul,.rl-body ol{margin:0 0 var(--space-10);padding-left:var(--space-22)}
.rl-body li{margin-bottom:var(--space-5)}
.rl-body li:last-child{margin-bottom:0}
.rl-body code{font-family:ui-monospace,monospace;font-size:var(--text-12);background:var(--bg-soft,#f7f7f7);
  padding:var(--space-1) var(--space-5);border-radius:5px}
.rl-quote{margin:0 0 var(--space-10);padding:var(--space-6) var(--space-12);border-left:3px solid var(--orange);
  color:var(--muted);font-style:italic}
.rl-quote:last-child{margin-bottom:0}

/* ---- What's-new "View" button + the tour glow it lands on ---------------- */
/* The per-line-item View button on the release log. Sits inline at the end of
   the bullet it belongs to, so it reads as "this item -> that place". */
.rl-view{display:inline-block;margin-left:var(--space-6);padding:1px var(--space-8);
  border:1px solid var(--orange-600);border-radius:999px;
  font-size:var(--text-11);font-weight:600;line-height:1.7;
  color:var(--orange-600);text-decoration:none;white-space:nowrap;vertical-align:baseline}
.rl-view:hover{background:var(--orange-600);color:#fff}

/* The glow tour.js pulses around whatever the reader was sent to look at.
   Outline + box-shadow only: both paint OUTSIDE the box, so a highlighted
   element never shifts the layout of the page it's sitting in. */
@keyframes tour-glow-pulse{
  0%{box-shadow:0 0 0 0 rgba(241,102,34,.55),0 0 0 0 rgba(241,102,34,.28)}
  55%{box-shadow:0 0 0 6px rgba(241,102,34,0),0 0 22px 8px rgba(241,102,34,.30)}
  100%{box-shadow:0 0 0 0 rgba(241,102,34,0),0 0 0 0 rgba(241,102,34,0)}
}
.tour-glow{
  outline:2px solid var(--orange);
  outline-offset:3px;
  border-radius:var(--radius-sm);
  animation:tour-glow-pulse 1.7s ease-out 3;
  scroll-margin-top:calc(var(--header-h) + var(--space-16));
}
/* Respect a reader who has asked for less motion: keep the outline (which is
   the actual "look here" signal) and drop the pulsing. */
@media (prefers-reduced-motion:reduce){
  .tour-glow{animation:none}
}

/* ============================================================
   RUN SHEETS — the five row kinds, plus the editor grid.

   TEL's own printed documents carry a "Background Colour Key" legend (found
   verbatim in 113 of the 270 corpus documents) classifying every row as
   facilitator point / activity / discussion / scene / break. That legend is
   the business's agreed vocabulary, so the NAMES are fixed. The COLOURS are
   not: the real files use 5 different greys and 4 different oranges for those
   same 5 meanings, so persisting the documents' inconsistency would bake in
   the mess. These tokens are the one answer.

   Each kind gets a pale FILL (the "background colour" the legend names, kept
   light enough that black body text on it clears AA comfortably) and a
   saturated EDGE used for the row's left bar, the legend swatch border and
   the editor's kind chip. Print-safe: all five fills survive greyscale as
   distinguishable tints, because these documents are printed in black and
   white as often as not.
   ============================================================ */
:root{
  /* Facilitator point = white in the legend. A literal white fill would be
     invisible as a "kind", so the fill stays page-white and the EDGE carries
     the identity — the row still reads as unmarked, which is the intent. */
  --run-fac_point:#ffffff;      --run-fac_point-edge:#b9b4b0;
  --run-activity:#e6f4ea;       --run-activity-edge:#237a46;  /* = --good */
  --run-discussion:#e4eefb;     --run-discussion-edge:#1f5fa8;
  --run-scene:#fdeee5;          --run-scene-edge:#b44a22;     /* = --orange-600 */
  --run-break:#eeecea;          --run-break-edge:#6f6c6a;     /* = --muted */
}

/* --- legend (screen + print) ------------------------------------------- */
.run-legend{display:flex;flex-wrap:wrap;gap:var(--space-8) var(--space-14);
  align-items:center;margin:var(--space-10) 0}
.run-legend .lg{display:flex;align-items:center;gap:var(--space-6);
  font-size:12px;color:var(--ink)}
.run-legend .sw{width:16px;height:16px;border-radius:4px;flex:0 0 auto;
  border:2px solid var(--line)}
.run-legend .sw.k-fac_point{background:var(--run-fac_point);border-color:var(--run-fac_point-edge)}
.run-legend .sw.k-activity{background:var(--run-activity);border-color:var(--run-activity-edge)}
.run-legend .sw.k-discussion{background:var(--run-discussion);border-color:var(--run-discussion-edge)}
.run-legend .sw.k-scene{background:var(--run-scene);border-color:var(--run-scene-edge)}
.run-legend .sw.k-break{background:var(--run-break);border-color:var(--run-break-edge)}

/* --- the tally header -------------------------------------------------- */
.run-tally{display:flex;align-items:baseline;gap:var(--space-8);flex-wrap:wrap}
.run-tally .big{font-family:var(--display);font-size:34px;line-height:1;
  color:var(--ink);letter-spacing:.5px}
.run-tally .sub{color:var(--muted);font-size:13px}

/* --- editor rows ------------------------------------------------------- */
.run-rows{display:flex;flex-direction:column;gap:var(--space-8)}
.run-row{border:1px solid var(--line);border-left:5px solid var(--run-fac_point-edge);
  border-radius:var(--radius-sm);background:var(--run-fac_point);
  padding:var(--space-10) var(--space-12)}
.run-row.k-fac_point{background:var(--run-fac_point);border-left-color:var(--run-fac_point-edge)}
.run-row.k-activity{background:var(--run-activity);border-left-color:var(--run-activity-edge)}
.run-row.k-discussion{background:var(--run-discussion);border-left-color:var(--run-discussion-edge)}
.run-row.k-scene{background:var(--run-scene);border-left-color:var(--run-scene-edge)}
.run-row.k-break{background:var(--run-break);border-left-color:var(--run-break-edge)}
/* Switched OFF: still visible and still editable — it keeps its place in the
   running order — but visibly not counting. Opacity alone would read as
   "disabled"; the dashed edge says "deliberately parked". */
.run-row.is-off{opacity:.55;border-left-style:dashed}
.run-row.is-off .run-cum{visibility:hidden}
.run-row.dragging{opacity:.4}
.run-rowhead{display:flex;align-items:center;gap:var(--space-8);flex-wrap:wrap}
.run-drag{cursor:grab;color:var(--muted);font-size:15px;line-height:1;
  padding:0 var(--space-2);flex:0 0 auto;user-select:none}
.run-cum{margin-left:auto;font-variant-numeric:tabular-nums;font-size:12px;
  color:var(--muted);flex:0 0 auto}
.run-mins{width:66px}
.run-row .run-topic{flex:1 1 220px;min-width:0}
.run-body{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-8);
  margin-top:var(--space-8)}
.run-body label, .run-body .rf-label{display:block;font-size:11px;color:var(--muted);
  margin-bottom:var(--space-2)}
.run-body textarea{width:100%;min-height:62px;resize:vertical}
.run-opt{font-size:12px;color:var(--muted);display:flex;align-items:center;
  gap:var(--space-4)}
.run-flag{font-size:11px;padding:1px var(--space-6);border-radius:999px;
  border:1px solid var(--run-break-edge);color:var(--slate-2)}
/* One column on phones: two side-by-side textareas at 390px are unusable, and
   the row's own horizontal scroll is what causes page-level overflow. */
@media (max-width:720px){
  .run-body{grid-template-columns:1fr}
  .run-cum{margin-left:0;width:100%}
  .run-mins{width:58px}
}
/* Annotation #183 — the run-sheet editor's inputs/selects/textareas were bare
   browser defaults, off-brand next to the rest of the site. Give them the same
   look as the site-wide .fld control (border/radius/padding/font/orange focus),
   but WITHOUT .fld's width:100%, which would break the row-head flex layout
   (.run-mins is 66px, .run-topic flexes). */
.run-row input:not([type=checkbox]),.run-row select,.run-row textarea,
#rsheader input,#rsheader select,#rsheader textarea,#rs-block{
  border:1px solid var(--line);border-radius:10px;
  padding:var(--space-9) var(--space-11);font-family:inherit;font-size:13.5px;
  background:var(--card);color:var(--ink)}
.run-row input:not([type=checkbox]):focus,.run-row select:focus,
.run-row textarea:focus,#rsheader input:focus,#rsheader select:focus,
#rsheader textarea:focus,#rs-block:focus{
  outline:none;border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-50)}

/* ---------- Dashboard tweaks (annotations #194 / #224 / #225, 2026-07-27) ----------
   Appended block, kept separate from the shared rules above so nothing else on the
   site shifts. `.dash-kpis` scopes the smaller stat tiles to the dashboard only —
   .kpi on its own (reports, actor/event detail) is untouched. */

/* #224/#225 — "these boxes can be smaller." Tighter padding + smaller number so the
   four dashboard stat tiles read as a compact strip instead of dominating the page. */
.dash-kpis.kpis .kpi{padding:var(--space-12) var(--space-14) var(--space-10)}
.dash-kpis.kpis .kpi .num{font-size:30px;margin-top:var(--space-4)}
.dash-kpis.kpis .kpi .num.cur{font-size:22px}
.dash-kpis.kpis .kpi .lab{font-size:11px}
.dash-kpis.kpis .kpi .sub{font-size:11px;margin-top:var(--space-2)}
.dash-kpis.kpis .kpi .accent{width:60px;height:60px;right:-12px;top:-12px}

/* "Team today" card — same request, one notch tighter than the default .card padding,
   and the table itself pulled in a little. */
.wa-today-card{padding:var(--space-10) var(--space-14)}
.wa-today-tbl th{padding:0 var(--space-8) var(--space-2) 0}
.wa-today-tbl td{padding:var(--space-6) var(--space-8) var(--space-2) 0}

/* #194 — "separate office staff from the rest so it's easy to see who's who." A small
   caps subheading above each office/other block within a "Team today" column
   (nexus2/views.py splits the people list; markup in dashboard.html). */
.wa-today-col-wrap{display:flex;flex-direction:column;gap:var(--space-4);max-width:180px}
.wa-today-sub{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;
  color:var(--muted)}
.wa-today-col-wrap .wa-today-col{max-width:none}

/* ---------- Run sheet / FAC guide readability (annotations #201/#202, 2026-07-27) ----------
   Appended block, kept separate from the shared rules above so nothing else on the site
   shifts. The rich-text boxes (Key points / Content / Facilitator notes / Overall objective /
   Learning objectives) were rendering at the 13.5-14px base — fine for a short label, too
   small for a box someone types a real talk-track into. This is the belt-and-braces copy of
   the same figure the run-sheet template also sets inline on .run-rich (that inline rule wins
   the cascade on this page; this one covers .run-rich anywhere else it gets reused, and lifts
   the plain textareas/inputs in the run-sheet body up to match so nothing reads smaller than
   the rich boxes beside it). */
.run-rich{font-size:var(--text-16);line-height:1.5}
.run-body textarea,.run-body input:not([type=checkbox]){font-size:var(--text-14)}
.run-rtb .rtb{font-size:var(--text-13)}

/* ---------- Engagement status/money extras (2026-07-27) ----------
   The show-only-when-relevant blocks in _eng_form.html (reason for a "no", and the
   cancellation block with the two independent pay/charge %s). One appended block, kept
   separate so nothing else shifts. Visibility is driven by the `hidden` attribute in JS
   (telSyncStatusExtras) — this only styles the cancellation reminder note. */
.eng-status-extra{margin-top:2px}
.eng-cancel-note{font-size:12px;line-height:1.4;color:var(--bad);background:color-mix(in srgb, var(--bad) 7%, transparent);
  border:1px solid color-mix(in srgb, var(--bad) 22%, transparent);border-radius:8px;padding:7px 10px;margin:2px 0 8px;font-weight:600}

/* Per-session Invoicing / pay panel (nexus2 SessionPricing) on the booking session card. */
.sess-pricing{margin-top:6px}
.sess-pricing .sp-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 12px}
.sess-pricing label{font-size:11px;font-weight:600;color:var(--muted);display:block;margin-bottom:2px}
.sess-pricing input[type=text],.sess-pricing input[type=number],.sess-pricing input:not([type]){width:100%;box-sizing:border-box;
  border:1px solid var(--line);border-radius:8px;padding:7px 8px;font-family:inherit;font-size:13.5px}
.sess-pricing .sp-full{grid-column:1 / -1}
.sess-pricing .sp-check{display:flex;gap:7px;align-items:center;font-size:13px;font-weight:600;color:var(--fg)}
.sess-pricing .sp-approve{margin-top:8px;padding:8px 10px;border-radius:8px;
  background:color-mix(in srgb, var(--good, #2e7d32) 7%, transparent);border:1px solid color-mix(in srgb, var(--good, #2e7d32) 22%, transparent)}
.sess-pricing .sp-hourly{display:none}
.sess-pricing.is-hourly .sp-hourly{display:block}
.sess-pricing.is-hourly .sp-fixed{display:none}

/* ===== 2026-07-28 annotation sweep — appended block, late so it wins the cascade ===== */

/* #235 "these photos seem awfully small" — the Team-today faces on the dashboard were the
   generic 34px .avatar. They are the point of that card, so they get their own size here
   rather than changing .avatar everywhere. Columns widen to match so the same number of
   faces still fit per row. */
.wa-today-tbl .avatar{width:46px;height:46px;font-size:14px}
.wa-today-col{max-width:236px}
.wa-today-col-wrap{max-width:236px}
@media (max-width:700px){
  .wa-today-tbl .avatar{width:38px;height:38px;font-size:12.5px}
  .wa-today-col,.wa-today-col-wrap{max-width:150px}
}

/* #216 "let the pill stretch big enough to see the details and times" — .wa-chip was
   clamped to max-width:130px with nowrap + ellipsis, so a real course title was always
   cut. On the PERSONAL week page (.wa-derived) the chips now wrap onto two lines and run
   the full column width. The team GRID keeps its clamp: that one is a dense matrix where
   130px is load-bearing. */
.wa-derived .wa-chip{max-width:100%;white-space:normal;overflow:visible;text-overflow:clip;
  line-height:1.35;align-items:flex-start;text-align:left}

/* ===== Info tips (#188) — see nexus2/static/nexus2/infotip.js ===================== */
/* The trigger: a little "i" in a circle sitting where a paragraph of grey help text
   used to. Sized off the surrounding text so it never dominates a field label. */
.infotip{display:inline-block;line-height:0;vertical-align:middle}
.infotip-btn{display:inline-flex;align-items:center;justify-content:center;
  width:15px;height:15px;padding:0;margin:0 0 1px 5px;border-radius:50%;
  border:1.3px solid var(--muted);background:transparent;color:var(--muted);
  font-family:inherit;font-size:10.5px;font-weight:700;font-style:italic;line-height:1;
  cursor:help;flex:0 0 auto;transition:color .12s,border-color .12s,background .12s}
.infotip-btn:hover,.infotip-btn:focus-visible,.infotip.is-open .infotip-btn{
  color:#fff;background:var(--orange);border-color:var(--orange);outline:none}
/* The help text itself stays in the DOM (so it is still there for a screen reader and
   for the JS to copy) but never renders in the flow. */
.infotip-body{display:none}
/* The single global bubble. position:absolute + a body-level parent is deliberate: it
   escapes every .card, table and accordion that would otherwise clip it. */
.infotip-pop{position:absolute;z-index:9000;background:var(--ink,#242424);color:#fff;
  border-radius:10px;padding:9px 11px;font-size:12.5px;line-height:1.45;font-weight:400;
  box-shadow:0 6px 24px rgba(0,0,0,.22);pointer-events:auto;max-width:320px}
.infotip-pop[hidden]{display:none}
.infotip-pop a{color:#ffb37a;text-decoration:underline}
.infotip-pop b{color:#fff}
.infotip-arrow{position:absolute;top:-5px;width:10px;height:10px;margin-left:-5px;
  background:var(--ink,#242424);transform:rotate(45deg);border-radius:2px}
.infotip-pop.flip .infotip-arrow{top:auto;bottom:-5px}
@media (max-width:700px){
  /* Bigger tap target on a phone, where there is no hover at all. */
  .infotip-btn{width:19px;height:19px;font-size:12px}
  .infotip-pop{font-size:13.5px}
}

/* Arrangements tick list (#223) — job- and session-level. Reads as a row of chips rather
   than a stack of checkboxes: six of them stacked would dominate a session card. */
.logcheck{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.logcheck-item{display:inline-flex;align-items:center;gap:6px;cursor:pointer;
  font-size:12px;font-weight:600;color:var(--muted);background:var(--paper,#fff);
  border:1px solid var(--line);border-radius:999px;padding:4px 10px 4px 8px}
.logcheck-item input{margin:0;width:13px;height:13px;flex:0 0 auto;accent-color:#2e7d32}
.logcheck-item.on{color:#2e7d32;border-color:color-mix(in srgb,#2e7d32 35%,transparent);
  background:color-mix(in srgb,#2e7d32 8%,transparent)}
.logcheck-item input:disabled{cursor:default}
.logcheck-item:has(input:disabled){cursor:default;opacity:.75}

/* Logistics files card — grouped by the folder a file actually lives in (#186). */
.log-grp{border-top:1px solid var(--line);padding:6px 0}
.log-grp:first-of-type{border-top:none}
.log-grp > summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:8px;
  flex-wrap:wrap;padding:5px 0;font-size:12.5px}
.log-grp > summary::-webkit-details-marker{display:none}
.log-grp > summary::before{content:'▸';opacity:.45;font-size:11px}
.log-grp[open] > summary::before{content:'▾'}
.log-grp-t{font-weight:600}

/* ---------- Run-sheet rows collapse to one line (annotation #200, 2026-07-27) ----------
   Appended block, kept separate from the run-sheet rules further up so nothing else on
   the site shifts. #200: "can these blocks collapse into the first row... that will be
   easier to move everything around. So when collapsed you see the title of the row."
   Collapsed IS the default (the class is on the server-rendered markup, so there is no
   expand-then-snap-shut flash on load). The summary line keeps the row's kind colour —
   the colour key is how these documents have always been read — plus its minutes, its
   topic and its running total, which is everything you need to drag it into place. */
.run-rowbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--line)}

.run-rowtoggle{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;padding:0;border:1px solid transparent;border-radius:8px;
  background:transparent;color:var(--muted);cursor:pointer;line-height:1}
.run-rowtoggle:hover{background:rgba(0,0,0,.06);color:var(--ink)}
.run-rowtoggle:focus-visible{outline:none;border-color:var(--orange);
  box-shadow:0 0 0 3px var(--orange-50)}
.run-rowtoggle .chev{font-size:13px;transition:transform .12s ease}
.run-row:not(.is-collapsed) .run-rowtoggle .chev{transform:rotate(90deg)}
/* Screen-reader-only label for the chevron, scoped to this control so the two-letter
   class name isn't claimed site-wide. */
.run-rowtoggle .vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* The per-row action strip (library flag / "Save to library" / "Remove row"). Its layout
   lives HERE rather than inline on the element: an inline `display:flex` outranks any
   stylesheet rule, so it would have survived the collapse below and left a floating strip
   under every closed row. */
.run-rowfoot{margin-top:var(--space-8);display:flex;gap:var(--space-8);
  align-items:center;flex-wrap:wrap}

/* The collapse itself. Everything below the summary line goes; the row shrinks to the
   height of one control, which is what makes a 37-row sheet draggable. */
.run-row.is-collapsed .run-body,
.run-row.is-collapsed .run-rowfoot{display:none}
.run-row.is-collapsed{padding-top:var(--space-6);padding-bottom:var(--space-6)}

/* A save the server REFUSED (e.g. Resources over its 300-character column). The row is
   force-opened and pinned open by the JS; this is where the reason is printed. */
.run-rowerr{margin-top:var(--space-6);padding:var(--space-6) var(--space-8);
  border-radius:var(--radius-sm);border:1px solid #c62828;color:#c62828;
  background:rgba(198,40,40,.06);font-size:12px}
.run-row[data-err]{box-shadow:0 0 0 2px rgba(198,40,40,.35)}

/* On a phone a collapsed row drops the secondary controls too, so the summary really is
   one line and the whole sheet fits on a screen you can drag on. They are all still in
   the DOM with their values — expanding the row brings them straight back. */
@media (max-width:720px){
  .run-row.is-collapsed .run-rowhead select,
  .run-row.is-collapsed .run-rowhead .run-opt{display:none}
  .run-row.is-collapsed .run-cum{margin-left:auto;width:auto}
  /* The row-wide rule gives Topic a 220px flex basis, which on a 390px screen forces the
     summary onto a second line. Collapsed, it may shrink — it is the only thing left
     that wants the space. */
  .run-row.is-collapsed .run-topic{flex:1 1 90px;min-width:0}
  /* Every px reclaimed here is a px of the topic you can actually read on a phone. */
  .run-row.is-collapsed .run-mins{width:46px;padding-left:var(--space-6);
    padding-right:var(--space-6);text-align:center}
  .run-row.is-collapsed .run-topic{padding-left:var(--space-8);padding-right:var(--space-8)}
}

/* ── Kit board tiles ───────────────────────────────────────────────────────────
   The office /kit/ page reuses _kit_tile.html, the same partial the wall iPad
   renders. The kiosk template is standalone and carries its own copy of these
   rules; this is the version for pages inside base.html. Kept deliberately
   finger-sized on both — the office page gets opened on a phone in the store
   room as often as on a desk. */
/* THE GAP BELONGS TO THE GROUP, not to the tile. It used to sit on `.tile` as a
   margin-bottom, and `.tile.kid` resets `margin` wholesale to indent itself — so a case with
   a laptop packed in it ended on a kid with no bottom margin and sat flush against the next
   case, while every other pair had a gap (Greg, 2026-07-30). A case plus its contents is ONE
   block on the board; spacing it as one is also what the kiosk already does. */
.grp{margin-bottom:var(--space-8)}
.grp:last-child{margin-bottom:0}
.tile{display:flex;align-items:center;width:100%;text-align:left;border:1px solid var(--line);
  background:#fff;border-radius:var(--radius-10,10px);padding:var(--space-10) var(--space-12);
  font:inherit;cursor:pointer;min-height:52px}
.tile:hover{border-color:var(--orange)}
.tile:active{background:var(--wash,#f2ede6)}
.tile b{font-size:var(--text-14);display:block;line-height:1.25}
.tile .meta{font-size:var(--text-12);color:var(--muted);margin-top:var(--space-2);display:block}
/* WHO HAS IT, AS A FACE (Greg, 2026-07-30) — the circle sets the tile's height and the two
   lines of text sit beside it. Mirrors the copy in kit_board.html; the kiosk is standalone
   and cannot share a stylesheet, so the two have to be kept in step by hand. */
.kit-av{width:42px;height:42px;border-radius:50%;overflow:hidden;margin-right:var(--space-11);
  flex:0 0 auto;background:var(--line);color:var(--muted);font-size:var(--text-13);
  font-weight:700;display:flex;align-items:center;justify-content:center}
.kit-av img{width:100%;height:100%;display:block;object-fit:cover}
/* Nobody assigned yet — an empty ring, not a broken photo. */
.kit-av.none{background:transparent;border:2px dashed var(--line)}
.kit-txt{flex:1 1 auto;min-width:0}
/* Something packed inside a case: indented on the left, but its RIGHT edge lines up with
   its case (Greg, 2026-07-29). `.tile` sets width:100%, so a left margin alone pushed the
   whole 100% across and the nested tile overhung its parent by exactly the indent —
   width:auto lets a block fill only what's left. */
.tile.kid{display:block;margin:var(--space-5) 0 0 22px;width:auto;min-height:0;
  padding:var(--space-8) var(--space-11);border-left:2px solid var(--line);
  border-radius:var(--radius-9,9px)}
.tile.kid b{font-size:var(--text-13);color:var(--muted)}
/* "then → Thu 6 Aug · IGO" — where this unit is promised NEXT. Quieter than the line above
   it: where it is now is the fact you read first. */
/* ONE LINE, not two (Greg, 2026-07-30). The chain is a glance — the case it moves to and
   the day — and anything longer belongs on the Kit runs page, so it clips rather than wraps. */
.tile .meta.next{color:var(--orange-600,#B44A22);font-size:var(--text-11,11px);margin-top:1px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
/* A REQUIREMENT THIS CASE IS STILL WAITING FOR. Dashed because there is nothing physical
   here yet — it deliberately does NOT look like a tile, since there is nothing to drag. */
.kit-slot{margin:var(--space-5) 0 0 22px;padding:var(--space-6) var(--space-11);
  border:1px dashed var(--line);border-radius:var(--radius-9,9px);color:var(--muted);
  font-size:var(--text-12)}
.kit-slot span{opacity:.8}
/* A left edge in the status colour, so "ready" and "out" read at a glance from
   across the room rather than by reading the words. */
.tile.ready{border-left:4px solid var(--good)}
.tile.out{border-left:4px solid var(--warn,#b8860b)}

/* ── Kit runs: the forward plan ────────────────────────────────────────────────
   One row per unit, one column per day (nexus2/templates/nexus2/kit_runs.html). Narrow,
   dense columns on purpose: the whole point is seeing a month of movement at once, so a
   cell says only WHO, and the full session is in the title/link. */
.kit-grid{border-collapse:separate;border-spacing:0;font-size:var(--text-12)}
.kit-grid th,.kit-grid td{border-bottom:1px solid var(--line);padding:var(--space-4) var(--space-6);
  text-align:center;white-space:nowrap}
.kit-grid thead th{font-size:var(--text-11,11px);color:var(--muted);font-weight:600;
  text-transform:uppercase;letter-spacing:.03em;line-height:1.15}
.kit-grid thead th span{display:block;font-size:9.5px;opacity:.75}
/* The unit name has to stay readable while the dates scroll under it. */
.kit-grid .stick{position:sticky;left:0;background:#fff;text-align:left;font-weight:600;
  min-width:150px;box-shadow:1px 0 0 0 var(--line)}
.kit-grid .stick .muted{display:block;font-weight:400;font-size:var(--text-11,11px)}
.kit-grid td.on{background:var(--wash,#fdf6f1)}
.kit-grid td.on a{color:var(--ink);text-decoration:none;font-weight:600}
/* Two jobs, one unit, one day — the thing this page exists to catch. */
.kit-grid td.clash{background:#fdecea;box-shadow:inset 0 0 0 2px var(--bad)}
.kit-grid td.clash a{color:var(--bad);font-weight:700;text-decoration:none}
.kit-grid .wk{background:#faf8f5}
.kit-grid td.on.wk{background:var(--wash,#fdf6f1)}

/* ── Kit board: the shared gestures + action sheet ─────────────────────────────
   Greg, 2026-07-30: "everything that I can do in the case-tracker page, I should also be
   able to do in the /kit/ office-side page" — and, before that, "right now I can't drag
   anything onto the board in my view". The office page runs the SAME script as the wall
   iPad now (nexus2/templates/nexus2/_kit_js.html); these are the class names that script
   uses, in the app's own visual language. The kiosk keeps its own copy of the equivalent
   rules because it is a standalone page that cannot load this stylesheet. */
.tile{touch-action:pan-y}                 /* a swipe still scrolls; the drag is behind a hold */
.kit-lane{transition:background .12s,border-color .12s}
/* The column you're dragging over. */
.kit-lane.over{background:var(--wash,#fdf6f1);border-color:var(--orange)}
/* A case you're about to drop something INTO — a different thing from a lane highlight, and
   both can be under the pointer at once. */
.tile.drop{outline:3px solid var(--orange);outline-offset:2px;background:var(--wash,#fdf6f1)}
/* WHERE IT WILL LAND when reordering: a line above the tile, or along the bottom of the
   column for last. box-shadow, not a border — a border resizes the tile mid-drag and shoves
   the very thing you're aiming at out from under the cursor. */
.grp.ins{box-shadow:0 -3px 0 0 var(--orange)}
.kit-lane.tail{box-shadow:inset 0 -4px 0 0 var(--orange)}
.tile.arming{background:var(--wash,#fdf6f1);border-color:var(--orange)}
/* ⚠️ NAMESPACED, and it must stay that way. These were `.lifted` and `.ghost` for one
   deploy, and `.ghost` is ALREADY this app's secondary button (`class="btn ghost"`, used on
   nearly every page) — every one of them turned into a half-transparent tile floating over
   the screen, on booking pages that have nothing to do with the Case Tracker. A class name
   added to tel.css is global to the whole app: prefix anything page-specific with `kit-`. */
.kit-lifted{opacity:.35}
.kit-ghost{position:fixed;z-index:80;pointer-events:none;opacity:.92;transform:rotate(-1.5deg);
  box-shadow:0 10px 26px rgba(0,0,0,.22);border-radius:var(--radius-10,10px);background:#fff;
  border:1px solid var(--orange);padding:var(--space-10) var(--space-12);min-width:180px}
.kit-ghost b{font-size:var(--text-14)}
/* The store room is ordinary tiles in a GRID, which does its own spacing with `gap` — the
   stacking margin the lanes want would double it. */
.kit-store .grp{margin:0}
/* LONG TERM — the fold at the foot of the Out lane. Greg, 2026-07-31: the cases that are
   out for eternity "are just hidden behind the long term drop down", rather than in a
   separate "Hidden from the board" list that implied they had left the board. Mirrors the
   wall iPad's `.lt` rules (kit_board.html), which cannot load this stylesheet. */
/* min-height, and it earns it: the fold is a DROP TARGET (Greg, 2026-07-31 — dragging a
   case down into it is the obvious way to park one), and a closed summary alone is a 20px
   strip to aim at with a whole hand. */
.kit-lt{margin-top:11px;border-top:1px dashed var(--line);padding-top:9px;min-height:46px}
/* Dragging a case over it. Deliberately louder than a lane highlight: this drop changes
   what the case IS on the board, not just which column it sits in. */
.kit-lt.over{background:var(--wash,#fdf6f1);border-top-color:var(--orange);
  box-shadow:inset 0 0 0 2px var(--orange);border-radius:var(--radius-10,10px)}
.kit-lt.over>summary{color:var(--orange-600,#B44A22)}
.kit-lt>summary{font-size:var(--text-12);text-transform:uppercase;letter-spacing:.05em;
  color:var(--muted);font-weight:700;cursor:pointer;padding:var(--space-4) var(--space-2);
  list-style:none}
.kit-lt>summary::-webkit-details-marker{display:none}
.kit-lt>summary:before{content:"\25B8\00a0"}         /* ▸ */
.kit-lt[open]>summary:before{content:"\25BE\00a0"}   /* ▾ */
.kit-lt .grp{margin-top:var(--space-8,8px);margin-bottom:0}
/* Parked, not amber: a long-term case can be sitting in any lane, and the fold lives under
   Out because that is where nearly all of them are, not because the colour would be true. */
.kit-lt .tile{border-left:4px solid var(--muted)}
/* THE ACTION SHEET. Slides up from the bottom on a phone in the store room and sits there
   on a desk; same shape as the kiosk's so the two surfaces feel like one tool. */
#kit-card{background:#fff;color:var(--ink);border-radius:18px 18px 0 0;width:100%;
  max-width:620px;padding:18px 18px 26px;max-height:88vh;overflow:auto}
#kit-card h3{font-size:var(--text-18,18px);margin-bottom:2px}
#kit-card .where{color:var(--muted);font-size:var(--text-13);margin-bottom:var(--space-12)}
.kit-act{display:block;width:100%;border:0;border-radius:12px;padding:14px;font:inherit;
  font-size:var(--text-15,15px);font-weight:600;margin-top:var(--space-10);cursor:pointer}
.kit-act.go{background:var(--orange-600,#B44A22);color:#fff}
.kit-act.ok{background:var(--good,#1b7f4b);color:#fff}
.kit-act.plain{background:#efe9e1;color:#171512}
label.kit-pick{display:block;font-size:var(--text-12);color:var(--muted);
  margin-top:var(--space-12);font-weight:400}
label.kit-pick .fld{margin-top:var(--space-4)}
/* Confirmation without a page-jump. The sheet's actions post in the background, so this is
   the only thing that says the server heard you. */
#kit-toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);background:#171512;
  color:#faf8f5;padding:13px 20px;border-radius:999px;font-size:var(--text-14);opacity:0;
  transition:opacity .2s;pointer-events:none;z-index:90}
#kit-toast.on{opacity:1}

/* ── Tick lists (CheckboxSelectMultiple) ──────────────────────────────────────
   Annotations #240/#241/#242. Django renders these as unclassed
   <div><label><input> text</label></div>, so they inherited `.fgroup label`
   (display:block, 600 weight) and the box sat on a different line from its
   words. One flex row per option fixes the alignment for every tick list on the
   site at once — the play form, booking contacts, skills, sensitivity tags. */
.chk-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
  gap:var(--space-2) var(--space-12);margin-top:var(--space-4)}
.chk-grid label{display:flex;align-items:center;gap:var(--space-8);
  font-size:var(--text-13);font-weight:400;color:var(--ink);padding:var(--space-3) 0;
  cursor:pointer;line-height:1.25}
/* THE actual cause of #240. forms.Styled puts class="fld" on every widget, and
   `.fld{width:100%}` was making each checkbox 237px WIDE — an invisible input
   stretched across the column with its tick drawn at one end, which is why the
   box looked like it belonged to a different label. Undo `.fld` here; a tick box
   is the one control that must stay its intrinsic size. */
.chk-grid input[type=checkbox]{flex:0 0 auto;margin:0;width:auto;min-width:0;
  padding:0;border:0;border-radius:0;background:none;accent-color:var(--orange)}
/* Django wraps each option in its own div; it must not fight the grid. */
.chk-grid > div{display:contents}

/* A long tick list starts collapsed — see _field.html for the 12-option rule. */
.chk-box > summary{cursor:pointer;list-style:none;display:flex;align-items:center;
  gap:var(--space-8);font-size:var(--text-12);font-weight:600;color:var(--slate-2)}
.chk-box > summary::-webkit-details-marker{display:none}
.chk-box > summary::before{content:"▸";color:var(--muted);font-size:11px}
.chk-box[open] > summary::before{content:"▾"}
.chk-box .chk-count{color:var(--muted);font-weight:400}

/* ---- Address type-ahead (addrsuggest.js) --------------------------------------
   The list is appended to <body>, not next to the input, because the address field
   usually sits inside a panel with its own overflow which would clip it. That means it
   is positioned in page coordinates and must sit above everything else on the page. */
.addr-suggest{
  z-index:9999; background:#fff; border:1px solid var(--line); border-radius:10px;
  box-shadow:0 8px 24px rgba(0,0,0,.13); overflow:hidden; max-height:270px;
  overflow-y:auto; font-size:13px;
}
.addr-suggest-row{ padding:8px 11px; cursor:pointer; border-bottom:1px solid var(--line); }
.addr-suggest-row:last-child{ border-bottom:0; }
.addr-suggest-row:hover,.addr-suggest-row.on{ background:var(--orange-50); }

/* ---- Messaging centre (/messages/) ---------------------------------------------
   Greg, 2026-07-30: "laid out like iMessage … the person's image and name and the
   conversation". Two panes on a desktop; ONE at a time on a phone, chosen by .cc-open
   (present only when a conversation is actually open) rather than by duplicating markup.

   THE TWO CHANNEL COLOURS ARE BOTH EXISTING TOKENS, on purpose — this page invents no
   palette. SMS = --orange-600 (the interactive brand orange; white on it measures 5.33,
   clears AA). Email = --slate-2 (#5b6770; white on it measures 5.49, clears AA). They are
   different HUES, not two shades of one, because "was that a text or an email?" has to be
   answerable at a glance and by someone who doesn't see colour the way you do — which is
   also why every bubble carries the word Text or Email as well as the colour.

   Inbound messages keep the channel colour as a left edge + label instead of a fill: their
   words are theirs, and a page where half the bubbles are solid brand orange stops reading
   as a conversation. */
.cc{display:grid;grid-template-columns:320px minmax(0,1fr);gap:var(--space-14);align-items:start}
.cc-list{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;max-height:calc(100vh - 190px);overflow-y:auto}
.cc-search{display:flex;gap:var(--space-6);padding:var(--space-10);border-bottom:1px solid var(--line);
  position:sticky;top:0;background:var(--card);z-index:2}
.cc-search input{flex:1;min-width:0;font-size:var(--text-13);padding:var(--space-8) var(--space-10);
  border:1px solid var(--line);border-radius:10px;background:var(--bg);font-family:var(--sans)}
.cc-alert{margin:var(--space-10);padding:var(--space-8) var(--space-10);border-radius:10px;
  background:#fde7e7;color:var(--bad);font-size:var(--text-12);font-weight:600}
.cc-sub{padding:var(--space-10) var(--space-12) var(--space-4);font-size:var(--text-11);
  text-transform:uppercase;letter-spacing:.06em;color:var(--muted);font-weight:700}
.cc-item{display:flex;gap:var(--space-10);align-items:center;padding:var(--space-10) var(--space-12);
  border-bottom:1px solid var(--line-2);text-decoration:none;color:var(--ink)}
.cc-item:hover{background:var(--bg)}
.cc-item.on{background:var(--orange-50)}
.cc-item-body{flex:1;min-width:0;display:block}
.cc-item-top{display:flex;justify-content:space-between;gap:var(--space-8);align-items:baseline}
.cc-item-top b{font-size:var(--text-13)}
.cc-item-top small{color:var(--muted);font-size:var(--text-11);white-space:nowrap}
.cc-prev{display:block;font-size:var(--text-12);color:var(--muted);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;margin-top:var(--space-2)}
.cc-chan{display:inline-block;font-size:var(--text-10);font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;padding:0 var(--space-5);border-radius:5px;margin-right:var(--space-5);
  color:#fff;background:var(--slate-2)}
.cc-chan.sms{background:var(--orange-600)}
.cc-needs{background:#fde7e7;color:var(--bad);font-size:var(--text-11);font-weight:700}

/* ---- The live half -------------------------------------------------------------
   Type-ahead results REPLACE the list rather than floating over it. .cc-list is a
   scrolling box with clipped overflow, so an absolutely-positioned dropdown inside it
   would be cut off at the box's edge — and "typing filters the list" is what a messaging
   app does anyway. Every hidden selector is a DIRECT child (>) because .cc-sub and
   .cc-item are reused INSIDE .cc-pop for the results themselves; an unqualified
   descendant selector here would hide the very thing it was opened to show. */
.cc-list.cc-searching > .cc-rows,
.cc-list.cc-searching > .cc-alert,
.cc-list.cc-searching > .cc-sub,
.cc-list.cc-searching > .cc-item{display:none}
.cc-pop .cc-sub{padding-top:var(--space-8)}
/* Keyboard highlight. Its own class rather than reusing .on, which means "this is the
   conversation you have open" — during a search those are two different rows. */
.cc-item.hi{background:var(--orange-50)}

/* A message that arrived while you were looking at the page announces itself and then
   stops: a 2.4s fade on the row, and a dot that stays until you open it. Nothing steals
   focus and nothing pops up — you are already reading, and being interrupted mid-sentence
   by your own inbox is the behaviour people turn off. Literal hex in the keyframe: custom
   properties are resolved but not interpolated between, so a var() here animates as a
   step rather than a fade. */
@keyframes cc-fresh-in{from{background:#fdeee5}to{background:transparent}}
.cc-item.cc-fresh{animation:cc-fresh-in 2.4s ease-out 1}
.cc-item.cc-unseen{background:var(--orange-50)}
.cc-item.cc-unseen .cc-item-top b::after{content:"";display:inline-block;width:7px;height:7px;
  border-radius:50%;background:var(--orange-600);margin-left:var(--space-6);vertical-align:middle}
/* Swapping a pane takes one round trip. Dimming it is enough to say "that click landed" —
   a spinner over half a second reads as slower than no spinner at all. */
.cc-pane.cc-loading{opacity:.55}

.cc-pane{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  display:flex;flex-direction:column;min-height:420px;overflow:hidden}
.cc-head{display:flex;align-items:center;gap:var(--space-10);padding:var(--space-10) var(--space-14);
  border-bottom:1px solid var(--line)}
.cc-back{display:none;font-size:var(--text-20);text-decoration:none;color:var(--slate-2);
  width:34px;height:34px;align-items:center;justify-content:center;border-radius:9px;
  border:1px solid var(--line);flex:none}
.cc-who{display:flex;align-items:center;gap:var(--space-10);text-decoration:none;color:var(--ink)}
.cc-who-name{display:block;line-height:1.25}
.cc-who-name small{display:block;color:var(--muted);font-size:var(--text-11)}
.cc-thread{flex:1;overflow-y:auto;padding:var(--space-14);max-height:calc(100vh - 380px);
  min-height:240px;background:var(--bg)}
.cc-note{font-size:var(--text-11);color:var(--muted);background:var(--card);
  border:1px dashed var(--line);border-radius:10px;padding:var(--space-8) var(--space-10);
  margin-bottom:var(--space-12);line-height:1.45}
.cc-day{text-align:center;font-size:var(--text-11);color:var(--muted);font-weight:700;
  text-transform:uppercase;letter-spacing:.05em;margin:var(--space-16) 0 var(--space-8)}
.cc-row{display:flex;margin:var(--space-6) 0}
.cc-row.out{justify-content:flex-end}
.cbub{max-width:80%;min-width:0;border-radius:14px;padding:var(--space-8) var(--space-12);
  font-size:var(--text-13);line-height:1.45;overflow-wrap:anywhere}
.cbub.sms.out{background:var(--orange-600);color:#fff;border-bottom-right-radius:5px}
.cbub.email.out{background:var(--slate-2);color:#fff;border-bottom-right-radius:5px}
.cbub.sms.in{background:var(--card);color:var(--ink);border:1px solid var(--line);
  border-left:4px solid var(--orange-600);border-bottom-left-radius:5px}
.cbub.email.in{background:var(--card);color:var(--ink);border:1px solid var(--line);
  border-left:4px solid var(--slate-2);border-bottom-left-radius:5px}
.cc-meta{font-size:var(--text-10);opacity:.75;margin-top:var(--space-4)}
/* WHO SENT IT, on outbound bubbles only. Its own row rather than a child of .cc-meta,
   because .cc-meta is 75% opacity and a faded headshot on an orange bubble is a smudge —
   the circle needs to be legible while the time beside it stays quiet. */
.cc-foot{display:flex;align-items:center;gap:var(--space-6);margin-top:var(--space-4)}
.cc-foot .cc-meta{margin-top:0;min-width:0}
.cc-by{flex:none;width:20px;height:20px;border-radius:50%;overflow:hidden;
  display:inline-flex;align-items:center;justify-content:center;line-height:1;
  font-size:8px;font-weight:800;letter-spacing:.01em;
  background:rgba(255,255,255,.28);color:#fff}
.cc-by img{width:100%;height:100%;object-fit:cover;display:block}
/* "Sent from the office, before we recorded who" — deliberately blank-looking. It must not
   be mistaken for TEL (which would claim the app sent something a person did) and it has no
   name to show, so it is a dot. */
.cc-by-unknown{font-size:var(--text-12);opacity:.8}
/* A message that never left sits on a pale grey bubble, where white-on-white would vanish. */
.cbub.undeliv .cc-by{background:var(--grey);color:#fff}
/* A message that never left must not read like one that did. Qualified with the channel
   classes so it BEATS .cbub.sms.out on specificity rather than relying on source order —
   a purpose-built rule losing a tie to a generic one has cost a day here before. */
.cbub.undeliv,.cbub.undeliv.sms.out,.cbub.undeliv.email.out,
.cbub.undeliv.sms.in,.cbub.undeliv.email.in{
  background:var(--line-2);color:var(--muted);border:1px dashed var(--grey)}
.cc-undeliv{font-size:var(--text-11);font-weight:700;color:var(--bad);margin-top:var(--space-4)}
/* Read receipts: whose face sits against the last message they've seen. Aligned to the same
   side as the message it belongs to, so the eye reads "this person got to HERE" rather than
   having to work out which bubble a floating row refers to. Pulled up tight under the
   bubble (negative margin) because it belongs TO that message, not between two of them. */
.cc-seen{display:flex;gap:var(--space-4);margin:-2px 0 var(--space-8)}
.cc-seen.out{justify-content:flex-end}
.cc-eye{width:18px;height:18px;border-radius:50%;overflow:hidden;flex:none;
  display:inline-flex;align-items:center;justify-content:center;line-height:1;
  font-size:8px;font-weight:800;background:var(--slate-2);color:#fff;
  border:1.5px solid var(--bg)}
.cc-eye img{width:100%;height:100%;object-fit:cover;display:block}
.cc-handle{display:flex;align-items:center;gap:var(--space-8);margin:var(--space-4) 0 var(--space-10)}
.cc-handle .btn{font-size:var(--text-11);padding:var(--space-3) var(--space-9)}
.cc-empty,.cc-none{padding:var(--space-22);text-align:center;color:var(--muted);font-size:var(--text-13)}

/* Emails arrive folded: subject + one line. <details> does this with no JavaScript, so it
   keeps working inside a fragment the ajax rail swapped in (where an injected <script>
   would never have run). */
.cmail summary{cursor:pointer;list-style:none;display:block;font-size:var(--text-13)}
.cmail summary::-webkit-details-marker{display:none}
.cmail-tag{display:inline-block;font-size:var(--text-10);font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;margin-right:var(--space-6);opacity:.85}
.cmail-peek{display:block;opacity:.8;font-size:var(--text-12);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.cmail[open] .cmail-peek{display:none}
.cmail-body{margin-top:var(--space-8);padding-top:var(--space-8);font-size:var(--text-12);
  border-top:1px solid rgba(255,255,255,.35)}
.cbub.email.in .cmail-body{border-top-color:var(--line)}

.cc-compose{border-top:1px solid var(--line);padding:var(--space-10) var(--space-14);background:var(--card)}
.cc-toggle{display:flex;align-items:center;gap:var(--space-6);flex-wrap:wrap}
.cc-opt{display:inline-flex;align-items:center;gap:var(--space-5);font-size:var(--text-12);
  font-weight:600;padding:var(--space-4) var(--space-10);border:1px solid var(--line);
  border-radius:999px;cursor:pointer;background:var(--bg)}
.cc-opt.off{opacity:.45;cursor:not-allowed}
.cc-opt:has(input:checked){background:var(--orange-600);color:#fff;border-color:var(--orange-600)}
.cc-opt-email:has(input:checked){background:var(--slate-2);border-color:var(--slate-2)}
.cc-dest{font-size:var(--text-11);color:var(--muted);margin-left:auto}
.cc-cant{font-size:var(--text-12);color:var(--bad);margin-top:var(--space-6);line-height:1.45}
.cc-cant.info{color:var(--muted)}
/* The subject box exists only for email. :has() on the checked radio does it in CSS, which
   means no per-page script and no state to keep in sync with the server's default. */
.cc-subject{display:none;margin-top:var(--space-8)}
.cc-compose:has(input[value="email"]:checked) .cc-subject{display:block}
.cc-subject input{width:100%;font-size:var(--text-13);padding:var(--space-8) var(--space-10);
  border:1px solid var(--line);border-radius:10px;font-family:var(--sans)}
.cc-write{display:flex;gap:var(--space-8);align-items:flex-end;margin-top:var(--space-8)}
.cc-write textarea{flex:1;min-width:0;font-family:var(--sans);font-size:var(--text-13);
  padding:var(--space-10);border:1px solid var(--line);border-radius:12px;resize:vertical}
/* The character count, texts only — shown/hidden by the same :has() trick as .cc-subject, so
   the Email toggle removes it with no script involved. Muted and small: going over the limit
   is ALLOWED (it just splits), so this is information, not a warning, and it never turns red.
   .split is only a weight change — enough to notice that the answer changed, not enough to
   read as "you have done something wrong". */
.cc-count{display:none;text-align:right;font-size:var(--text-11);color:var(--muted);
  margin-top:var(--space-6);font-variant-numeric:tabular-nums}
.cc-compose:has(input[value="sms"]:checked) .cc-count{display:block}
.cc-count.split{color:var(--slate-2);font-weight:600}
/* The sound toggle. Starts hidden and comms.js unhides it: with no JavaScript there is no
   poll, so there is no sound, so a control that turns it off would be a lie. Text as well as
   an icon — this is the one control someone in an open-plan office has to find in a hurry
   and be certain about, and a bare glyph is a guess. */
.cc-tools{display:flex;justify-content:flex-end;padding:var(--space-6) var(--space-10) 0}
.cc-sound{font-family:var(--sans);font-size:var(--text-11);font-weight:600;cursor:pointer;
  border:1px solid var(--line);border-radius:999px;background:var(--bg);color:var(--muted);
  padding:var(--space-3) var(--space-9)}
.cc-sound:hover{border-color:var(--grey);color:var(--ink)}
.cc-sound[aria-pressed="true"]{background:var(--orange-50);border-color:var(--orange-600);
  color:var(--orange-700)}

@media(max-width:900px){
  .cc{grid-template-columns:minmax(0,1fr)}
  .cc-list{max-height:none}
  .cc.cc-open .cc-list{display:none}
  .cc:not(.cc-open) .cc-pane{display:none}
  .cc-back{display:flex}
  .cc-thread{max-height:none;min-height:50vh}
  .cbub{max-width:88%}
}

/* ── The clients half of the messaging centre ─────────────────────────────────
   Greg, 2026-07-30: "the same chat box/inbox feature needs to apply to clients as well. on
   an individual level and at a whole organisation level." Everything below is ADDITIVE to
   the rules above — the team half's markup gained no classes, so it renders identically. */

/* TEAM / CLIENTS. Two real links (two URLs you can bookmark), styled as one control. Sits
   above the search box because it changes what the search box searches. */
.cc-tabs{display:flex;gap:var(--space-4);padding:var(--space-10) var(--space-10) 0}
.cc-tab{flex:1;text-align:center;font-size:var(--text-12);font-weight:700;text-decoration:none;
  padding:var(--space-6) var(--space-10);border:1px solid var(--line);border-radius:999px;
  color:var(--muted);background:var(--bg)}
.cc-tab:hover{border-color:var(--grey);color:var(--ink)}
.cc-tab.on{background:var(--orange-600);border-color:var(--orange-600);color:#fff}

/* An organisation has no face. Square-ish rather than a circle, so a client is never mistaken
   at a glance for a person in the same list — the shape carries the difference, not just the
   fact that one has initials where the other has a photograph. */
.headshot.hs-org{border-radius:10px;background:var(--slate-2)}
.headshot.hs-xs{width:22px;height:22px;font-size:10px;margin:0;flex:none}

/* THE PEOPLE AT AN ORGANISATION — the join between the two levels, and the answer to the
   read-only org thread. Scrolls sideways rather than wrapping to three rows: on a client with
   a dozen contacts the thread is what should own the vertical space. */
.cc-chips{display:flex;align-items:center;gap:var(--space-6);overflow-x:auto;
  padding:var(--space-8) var(--space-14);border-bottom:1px solid var(--line);
  background:var(--bg)}
.cc-chips-lab{font-size:var(--text-11);font-weight:700;color:var(--muted);flex:none;
  text-transform:uppercase;letter-spacing:.04em}
.cc-chip{display:inline-flex;align-items:center;gap:var(--space-5);flex:none;
  text-decoration:none;font-size:var(--text-12);font-weight:600;color:var(--ink);
  background:var(--card);border:1px solid var(--line);border-radius:999px;
  padding:var(--space-3) var(--space-9) var(--space-3) var(--space-4)}
.cc-chip:hover{border-color:var(--orange-600);color:var(--orange-700)}
.cc-chip-name{white-space:nowrap}

/* WHICH PERSON AT THE CLIENT said it — org-level thread only, where the whole point is that
   each answer names who it was said to. Small and above the message, so it reads as a label
   on the bubble rather than part of what was written. */
.cc-party{font-size:var(--text-10);font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;opacity:.75;margin-bottom:var(--space-4)}

/* ONE-CHANNEL COMPOSER. A client contact can only be emailed, so there is no radio to hang
   the :has() rules above off — the subject box and the counter are decided in the template and
   simply shown. Qualified with .cc-compose so it beats the display:none defaults on
   specificity rather than on document order (memory css-later-rule-wins-a-specificity-tie). */
.cc-compose.cc-one .cc-subject{display:block}
.cc-compose.cc-one .cc-count{display:block}
.cc-toggle.cc-solo{gap:var(--space-8)}

/* ── The floating chat bubble ──────────────────────────────────────────────────
   Greg, 2026-07-30: "a little message bubble/window in the bottom right corner the way
   facebook messenger appears."

   ⚠️ THE BOTTOM-RIGHT CORNER ALREADY HAD A TENANT. #scrolltop is a 42px circle at
   right:22px/bottom:22px, and the Annotate control owns bottom-LEFT. The launcher takes the
   corner and pushes #scrolltop up above it with a sibling selector — which is why
   {% comms_widget %} is written BEFORE that button in base.html. Two round controls in the
   same 42px of screen is a mis-tap, not a layout preference. */
.ccw{position:fixed;right:22px;bottom:22px;z-index:60}
.ccw ~ #scrolltop{bottom:78px}

/* Annotation #250 (Greg, on /kit/): "can this pill just be the symbol, not the word
   'messages'". A round icon button, not a pill — it sits over content on every page in the
   app, and the smallest thing that still reads as "messages" is the right size for that. The
   word survives as screen-reader-only text below: a button labelled only by an <svg>
   announces itself as "button". */
.ccw-fab{display:inline-flex;align-items:center;justify-content:center;cursor:pointer;
  width:46px;height:46px;color:#fff;
  background:var(--ink);border:none;border-radius:50%;padding:0;
  box-shadow:var(--shadow-lg);position:relative}
.ccw-lbl{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.ccw-fab:hover{background:var(--orange-600)}
.ccw-fab.has-new{background:var(--orange-600)}
.ccw[data-open="1"] .ccw-fab{background:var(--orange-600)}
/* The count, on the launcher as well as in the tab title: the bubble's whole job is to be
   noticeable from a page that is about something else. Hidden at zero — see comms.js. */
.ccw-badge{position:absolute;top:-3px;right:-3px;min-width:19px;height:19px;border-radius:999px;
  background:var(--bad);color:#fff;font-size:var(--text-10);font-weight:800;line-height:19px;
  text-align:center;padding:0 4px;box-shadow:0 0 0 2px var(--card)}

.ccw-panel{position:absolute;right:0;bottom:56px;width:min(94vw,720px);
  background:var(--card);border:1px solid var(--line);border-radius:16px;
  box-shadow:var(--shadow-lg);overflow:hidden;display:flex;flex-direction:column}
/* ⚠️ `display:flex` ABOVE BEATS THE `hidden` ATTRIBUTE, and this rule is what puts it back.
   The browser's own `[hidden]{display:none}` is a plain element rule, so any class rule that
   sets `display` outranks it — the panel shipped permanently open, sitting over the bottom
   half of every page in the app and swallowing clicks on whatever was underneath.
   Playwright missed it because the assertion asked whether the `hidden` ATTRIBUTE was present
   (it was, and always had been) instead of whether the element was actually visible. Any
   `hidden` element this stylesheet gives a `display` to needs this line. */
.ccw-panel[hidden]{display:none}
.ccw-bar{display:flex;align-items:center;gap:var(--space-10);padding:var(--space-10) var(--space-14);
  border-bottom:1px solid var(--line);background:var(--bg);font-size:var(--text-13)}
.ccw-full{margin-left:auto;font-size:var(--text-11);font-weight:700;text-decoration:none;
  color:var(--muted)}
.ccw-full:hover{color:var(--orange-700)}
.ccw-x{background:none;border:none;cursor:pointer;font-size:var(--text-20);line-height:1;
  color:var(--muted);padding:0 var(--space-4)}
.ccw-x:hover{color:var(--ink)}

/* The panel hosts the SAME .cc grid as /messages/, just shorter and with its own scroll. Only
   the sizes are overridden — the layout, the rows, the bubbles and the composer are the page's
   own rules, which is what keeps the widget from becoming a second design.

   ⚠️ EVERY RULE HERE IS ABOUT HEIGHT, AND THE FIRST VERSION GOT IT WRONG IN A WAY THAT ONLY A
   SCREENSHOT CAUGHT. On /messages/ the pane is as tall as the viewport allows
   (`.cc-thread{max-height:calc(100vh - 380px)}`) and `.cc{align-items:start}` lets each column
   take its content height. Dropped into a 460px panel that produces a pane ~570px tall whose
   bottom — the composer — is simply clipped off by the panel's `overflow:hidden`. The page
   rendered, nothing errored, no overflow was reported, and there was just no message box.

   The fix is `align-items:stretch` + `height:100%` down the chain, and `min-height:0` so the
   thread is allowed to be SHORTER than its content and scroll. A flex child defaults to
   `min-height:auto`, which means "never shrink below your content" — that default is what was
   pushing the composer out. */
.ccw-cc{height:min(70vh,460px);border:none;border-radius:0;align-items:stretch;gap:0}
.ccw-cc .cc-list{max-height:none;height:100%;min-height:0;border:none;border-radius:0;
  border-right:1px solid var(--line)}
.ccw-cc .cc-pane{height:100%;min-height:0;border:none;border-radius:0}
.ccw-cc .cc-thread{max-height:none;min-height:0}
.ccw-cc .cc-search{padding:var(--space-8) var(--space-10)}
/* One pane at a time in a 366px-wide panel, same as the phone breakpoint does for the page. */
.ccw-cc .cc-chips{padding:var(--space-6) var(--space-10)}

/* On a phone the panel is nearly the whole width, so anchoring it to the launcher leaves 2px
   of margin on one side and 22px on the other. Pinned to both edges instead — it is a sheet at
   this size, not a popover. */
@media(max-width:520px){
  .ccw-panel{position:fixed;left:10px;right:10px;bottom:80px;width:auto}
}

/* Nothing floating belongs on paper — the same rule #scrolltop already follows. */
@media print{.ccw{display:none}}

/* ── Casting grid: the status in words, on demand ──────────────────────────────
   Roadmap item "casting cell labels visible without hovering". Every cell renders BOTH a
   symbol (.cs-ab) and the words (.cs-lb); this decides which is shown, and the toggle above
   the grid flips it per person.

   Default stays the symbol. The grid is a column per date and a row per person — it only fits
   on screen at symbol width, so words are a deliberate choice somebody makes for one sheet,
   not a change imposed on everyone scanning colour across forty dates. */
.cs-cell .cs-lb{display:none}
.cs-grid.cs-labels .cs-cell .cs-ab{display:none}
/* ⚠️ A FIXED WIDTH ON THE INNER BLOCK, not just a min-width on the cell. A table cell sizes
   itself to its content, so `white-space:normal` alone let a long label ("Held — University of
   NSW - AGSM") stretch its column to ~270px and pushed most of the dates off screen — in the
   one mode somebody turned on to read more, not less. Fixing the width here makes the text
   wrap instead of the column grow. Wrapping to two or three lines is the right trade: labels
   like "Work offer sent" and "Not available" differ at the END, so an ellipsis would leave
   exactly the wrong half. */
.cs-grid.cs-labels .cs-cell .cs-lb{display:block;font-size:var(--text-11);font-weight:700;
  line-height:1.2;white-space:normal;overflow-wrap:break-word;width:104px;margin:0 auto;
  padding:0 2px}
.cs-grid.cs-labels .cs-cell{min-width:112px;height:auto;min-height:42px;
  padding:var(--space-5) 0}
/* The ✉ / ★ cells are bumped up to 17px for the symbol; that size on a sentence is shouting. */
.cs-grid.cs-labels .cs-cell.cs-offer{font-size:12.5px}
.cs-labels-toggle[aria-pressed="true"]{background:var(--orange-50);border-color:var(--orange-600);
  color:var(--orange-700)}
/* A whole sentence, not a chip. `.cs-legend span{display:inline-flex}` above makes every child
   a flex item, which stacks this sentence's <b> into its own column once the row is tight. */
.cs-legend .cs-legend-note{display:block;flex:1 1 100%;margin-left:0;line-height:1.5}

/* Quick select — a mode, so it has to LOOK like one. Greg, 2026-07-31: working out a cast was
   "too many steps" because every click opened the editor panel. While this is on, a plain
   click is the tick, so the grid gets a pointer that says "I am a switch" rather than "I open
   something", and the toggle stays lit. */
.cs-quick-toggle[aria-pressed="true"]{background:var(--orange-50);border-color:var(--orange-600);
  color:var(--orange-700)}
.cs-grid.cs-quick .cs-cell{cursor:copy}
.cs-grid.cs-quick .cs-cell:hover{box-shadow:inset 0 0 0 2px var(--orange-600)}
.cs-quick-hint{flex:1 1 100%;margin-left:0;line-height:1.5}

/* 1st choice / 2nd choice — the number in the corner of a picked cell. Greg, 2026-07-31:
   "selecting someone as option #1, then if you select someone else, the page already knows
   that they're option 2 (and shows #2)."

   Top-LEFT, because the auto-cast marker already owns the top-right (.cs-cell.auto::after) and
   two dots in one corner is a mystery rather than two facts. Small, high-contrast and
   tabular: it is scanned down a column, so the digits have to line up. */
.cs-pick{position:absolute;top:2px;left:3px;min-width:14px;height:14px;padding:0 3px;
  border-radius:7px;background:var(--ink);color:#fff;font-size:9.5px;font-weight:800;
  line-height:14px;text-align:center;font-variant-numeric:tabular-nums;
  box-shadow:0 0 0 1.5px rgba(255,255,255,.85)}
.cs-grid.cs-labels .cs-pick{top:3px;left:4px}
