/* ============================================================
   AceMakers course platform - UI system.
   Restyled to a Supabase/Resend dashboard aesthetic: neutral surfaces,
   1px borders define edges, flat (near-invisible shadows), tight spacing.
   The brand teal/green is kept but used ONLY as an accent - primary
   buttons, the active nav row, links, and focus rings. Everything else
   is neutral. Every component is written against the tokens, so a
   re-theme stays a token swap.
   Dark mode = dark NEUTRAL surfaces with the same teal accent.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

/* DARK - dark neutral surfaces, teal accent (brand mint). */
:root {
  --background: #131313;
  --card: #1b1b1b;
  --surface: #1f1f1f;        /* subtle panel fill (columns, metrics, rows) */
  --surface-hover: #242424;  /* hover / active neutral fill */
  /* Hover veil for the calendar bars: painted OVER whatever fill a bar already
     has (neutral gray, or a pale category tint), so every event highlights the
     same amount. brightness() could not do that - it barely moves a 10%-alpha
     tint, and it washed the gray bars out to the cell's own white. */
  --cal-hover-veil: rgba(255,255,255,.10);
  --primary: #70ffe2;        /* brand accent - interactive/active/links only */
  --primary-fg: #0c1f1b;     /* text/icon on a solid primary button */
  --primary-30: rgba(112,255,226,.30);
  --primary-20: rgba(112,255,226,.20);
  --primary-12: rgba(112,255,226,.12);
  --primary-08: rgba(112,255,226,.08);
  --primary-04: rgba(112,255,226,.05);
  --foreground: #ededed;     /* near-white text */
  --muted-foreground: #a3a3a3;
  --muted-2: #6f6f6f;
  --line: #2b2b2b;           /* neutral 1px border */
  --line-strong: #363636;
  --ring: rgba(112,255,226,.15);
  --red: #ff8a8a;  --amber: #ffce5a;  --blue: #5ad7e8;  --gold: #f5b301;  --purple: #a78bfa;
  /* Low-opacity fills for the calendar category chips + all-day banners. */
  --red-bg: rgba(255,138,138,.14); --amber-bg: rgba(255,206,90,.14);
  --blue-bg: rgba(90,215,232,.14); --purple-bg: rgba(167,139,250,.14);
  --navy: #13282a; --dark-ink: #0c1f1b;
  --radius: 6px;  --card-radius: 8px;
  --shadow: none;
  --shadow-pop: 0 10px 30px rgba(0,0,0,.5);
  /* Shell geometry (theme-independent, so they live only here). --topbar-h is the
     full-width top bar; --sb-full / --sb-rail are the two sidebar widths. */
  --topbar-h: 56px;  --sb-full: 240px;  --sb-rail: 60px;
  --card-bg: var(--card);
  --input-bg: #1c1c1c;
  --th-bg: #1a1a1a;
  --td-border: #262626;
  --bar-track: #2b2b2b;
  --dnd-slot: #363636;       /* the flat gray drop slot (Asana placeholder) */
  --gold-bg: rgba(245,179,1,.08); --gold-border: rgba(245,179,1,.28); --gold-fg: #ffd98a;
  /* Swatches: a patch of the OTHER theme's surface, so one page can show both.
     Used by the logo uploader, where the whole question is whether a mark reads
     on light and on dark. Each theme block holds the values it is not currently
     painting, so a colour still only ever appears in a token declaration. */
  --swatch-dark-bg: var(--background);  --swatch-dark-line: var(--line-strong);
  --swatch-dark-fg: var(--muted-foreground);
  --swatch-light-bg: #f7f7f6;  --swatch-light-line: #d9d9d6;  --swatch-light-fg: #6b6b6b;
}

/* LIGHT - near-white background, white cards, neutral gray borders.
   Accent changes VALUE (deep teal), never hue. */
[data-theme="light"] {
  --background: #f7f7f6;
  --card: #ffffff;
  --surface: #fafafa;
  --surface-hover: #f2f2f1;
  --cal-hover-veil: rgba(0,0,0,.07);
  --primary: #0a7d68;
  --primary-fg: #ffffff;
  --primary-30: rgba(10,125,104,.28);
  --primary-20: rgba(10,125,104,.18);
  --primary-12: rgba(10,125,104,.12);
  --primary-08: rgba(10,125,104,.08);
  --primary-04: rgba(10,125,104,.05);
  --foreground: #1a1a1a;
  --muted-foreground: #6b6b6b;
  --muted-2: #9a9a9a;
  --line: #e6e6e4;
  --line-strong: #d9d9d6;
  --ring: rgba(10,125,104,.11);
  --red: #d12f2f;  --amber: #a9760a;  --blue: #0d7f9e;  --gold: #9c7400;  --purple: #6d28d9;
  --red-bg: rgba(209,47,47,.10); --amber-bg: rgba(169,118,10,.12);
  --blue-bg: rgba(13,127,158,.12); --purple-bg: rgba(109,40,217,.10);
  --dark-ink: #ffffff;
  --shadow: none;
  --shadow-pop: 0 8px 28px rgba(16,24,24,.12);
  --card-bg: #ffffff;
  --input-bg: #ffffff;
  --th-bg: #fafafa;
  --td-border: #eeeeec;
  --bar-track: #ececea;
  --dnd-slot: #e8e8e8;       /* the flat gray drop slot (Asana placeholder) */
  --gold-bg: rgba(169,118,10,.10); --gold-border: rgba(169,118,10,.28); --gold-fg: #6b4e00;
  --swatch-dark-bg: #131313;  --swatch-dark-line: #363636;  --swatch-dark-fg: #a3a3a3;
  --swatch-light-bg: var(--background);  --swatch-light-line: var(--line-strong);
  --swatch-light-fg: var(--muted-foreground);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: Inter, system-ui, -apple-system, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

/* Flat design: no ambient glow, no crosshatch. Borders define edges. */

/* ============================================================
   App shell (Asana / Supabase pattern)
   - A full-width TOP BAR: fixed brand far left, then the per-page toolbar, search
     far right. It never moves when the sidebar changes.
   - Below it, the SIDEBAR (fixed, icon rail <-> labelled column) and the CONTENT.

   Two custom properties drive the widths:
     --sb-cur     the sidebar's RENDERED (visual) width - what animates.
     --sb-reserve the width the content is offset by. It equals --sb-cur in the
                  expanded/collapsed modes, but in HOVER mode it stays pinned to
                  the rail, so a hover-expansion floats OVER the content as an
                  overlay instead of reflowing the page.
   data-sidebar (expanded|collapsed) is the rendered form; data-sb-mode
   (expanded|collapsed|hover) is the persisted choice.
   ============================================================ */
html { --sb-reserve: var(--sb-cur); }
[data-sidebar="expanded"]  { --sb-cur: var(--sb-full); }
[data-sidebar="collapsed"] { --sb-cur: var(--sb-rail); }
/* Hover mode: the page is always reserved a rail; the expansion overlays it. */
[data-sb-mode="hover"] { --sb-reserve: var(--sb-rail); }

/* `flow-root` is load-bearing, not decoration. .content sits below the fixed
   topbar via `margin-top: var(--topbar-h)`, and on a plain block that margin
   COLLAPSES straight out through this element - taking the shell down with it.
   The shell then started at 56px, still stood 100vh tall, and finished 56px past
   the bottom of the window, so every page in the app scrolled by exactly the
   height of its topbar with nothing down there to see. It is also what sliced the
   top off the wizard: the page, not the stage, was scrolled. flow-root gives the
   shell its own block formatting context, so the margin stays inside where it
   was meant to push the content, not the shell. */
.app-shell { min-height: 100vh; display: flow-root; }

/* ---------- full-width top bar ----------
   LEFT = fixed brand slot, then the hamburger (phones) + page title / context.
   RIGHT = the page's view controls, then the static search button. */
.topbar {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  display: flex; align-items: center; gap: 12px;
  height: var(--topbar-h);
  padding-right: 20px;
  background: var(--background);
  border-bottom: 1px solid var(--line);
}
.topbar .sp { flex: 1 1 auto; min-width: 8px; }
.topbar .btn-sec, .topbar .product, .topbar .who { white-space: nowrap; }

/* The brand: a fixed slot the width of the expanded sidebar, so the wordmark
   sits far left and holds its place no matter what the sidebar does. */
.brand {
  display: flex; align-items: center; flex: none;
  width: var(--sb-full); height: 100%;
  padding-left: 20px; box-sizing: border-box;
  text-decoration: none;
}
.brand .logo-full { height: 18px; }
.brand-light, .brand-dark, .brand-custom { display: none; }
html[data-theme="light"] .logo-full.brand-light { display: block; }
html:not([data-theme="light"]) .logo-full.brand-dark { display: block; }

/* A user's own logo takes the slot outright, and switches on theme exactly like
   the AceMakers pair above. It is sized by a BOX rather than a fixed height:
   their mark may be a wide wordmark or a square icon, and either has to sit in
   the same slot without pushing the top bar around. */
.brand[data-brand="custom"] .logo-full.brand-light,
.brand[data-brand="custom"] .logo-full.brand-dark { display: none; }
html[data-theme="light"] .brand[data-brand="custom"] .logo-full.brand-custom-light,
html:not([data-theme="light"]) .brand[data-brand="custom"] .logo-full.brand-custom-dark {
  display: block; height: auto;
  max-height: 26px; max-width: calc(var(--sb-full) - 40px);
  width: auto; object-fit: contain;
}

/* The hamburger lives in the top bar; only the phone drawer regime shows it. */
.nav-burger {
  display: none; flex: none;
  width: 34px; height: 34px; padding: 0; border-radius: var(--radius);
  align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); background: var(--card); color: var(--muted-foreground);
  cursor: pointer; transition: background .12s, color .12s;
}
.nav-burger:hover { background: var(--surface-hover); color: var(--foreground); }
.nav-burger .ico { width: 18px; height: 18px; }

/* ---------- app body: the sidebar + the content column, under the top bar ---------- */
.app-body { position: relative; }

.sidebar {
  position: fixed; top: var(--topbar-h); left: 0; bottom: 0; z-index: 40;
  width: var(--sb-cur);
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 12px;
  background: var(--card);
  border-right: 1px solid var(--line);
  transition: width .18s ease-out, box-shadow .18s ease;
}
/* Hover mode, currently expanded (mouse over the rail): float the full column
   over the content with a soft shadow - the page underneath never reflows.

   ABOVE THE CHAPTER DRAWER (92). This is the rule that governs the sidebar while
   it is floating, and at 55 it floated over the page but under a maximised
   chapter - so pointing at the menu made it slide out behind the chapter and
   disappear. Raising the base .sidebar rule does not help: this selector is two
   attributes and a class, and it wins. */
[data-sb-mode="hover"][data-sidebar="expanded"] .sidebar {
  z-index: 95; box-shadow: var(--shadow-pop);
}

/* the nav - a vertical list of icon+label rows */
.nav { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; flex: 1; min-height: 0; }
.nav::-webkit-scrollbar { width: 0; }

/* ---------- sidebar foot: four fixed rows, identical in rail and expanded ----------
   The rule that stops hover-expand from misfiring (Supabase follows it): expanding
   may only ADD a label to the RIGHT of an already-visible icon. Every control keeps
   its exact row and vertical position in BOTH states - nothing reorders, nothing
   appears or disappears as a row, no height changes - so the element under a
   stationary cursor can never swap mid-hover. The rows mirror .nav-item geometry so
   the whole sidebar shares ONE icon column. */
.sidebar-foot {
  display: flex; flex-direction: column; gap: 2px;
  padding-top: 10px; margin-top: 6px; border-top: 1px solid var(--line);
}
/* ONE shared row - the single NavRow component behind BOTH sidebar groups: the
   nav items (Course, Tools, Calendar, ...) AND the foot rows (profile, theme,
   sidebar, sign out). Sharing one geometry is exactly what stops the two groups
   drifting: the same 40px height in the rail and expanded, one 16px icon column
   that never moves, and a label that only fades in to the RIGHT of that icon. */
.navrow {
  display: flex; align-items: center; gap: 10px; width: 100%;
  /* flex:none - .nav is a flex column, and without it a PACKED sidebar quietly
     compresses its direct-child rows below 40px while rows inside a wrapper
     (#nav-tools, .sidebar-foot) kept full height - three row heights on one
     screen. Instead ALL rows share --navrow-h, which fitSidebar() (app.js)
     lowers uniformly until everything fits - a full sidebar gets denser, it
     never scrolls. 40px is the roomy default a member sees. */
  flex: none; height: var(--navrow-h, 40px); padding: 0 9px; border-radius: var(--radius);
  border: none; background: transparent; color: var(--muted-foreground);
  font: 500 13px/1.2 Inter; text-align: left; cursor: pointer;
  transition: background .12s, color .12s;
}
.navrow:hover { background: var(--surface-hover); color: var(--foreground); }
/* A fixed 16px icon slot sitting at the SAME x in every row, so the cursor stays
   on the same icon as the sidebar width animates. */
.navrow-ico {
  flex: none; width: 16px; height: 16px;
  display: grid; place-items: center;
}
.navrow-ico > svg, .navrow-ico > .ico { width: 16px; height: 16px; }
/* Labels fade in place to the right; clipped (not display:none) in the rail so
   they can animate and never mount/unmount a row. */
.navrow .lbl {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  opacity: 1; transition: opacity .12s ease .1s;
}
/* The foot group adds only the open-popover highlight; all geometry is .navrow. */
.foot-row[aria-expanded="true"] { background: var(--surface-hover); color: var(--foreground); }

/* content column: offset by the top bar and by the reserved sidebar width. */
.content {
  margin-top: var(--topbar-h);
  margin-left: var(--sb-reserve);
  min-width: 0; min-height: calc(100vh - var(--topbar-h));
  display: flex; flex-direction: column;
  transition: margin-left .18s ease-out;
}

/* The overlay scrim behind the phone drawer. Painted only in that regime. */
.nav-scrim { display: none; }

/* ---------- sidebar-control popover (the three-mode picker) ---------- */
.sb-menu {
  position: fixed; z-index: 400; width: 236px; padding: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  box-shadow: var(--shadow-pop);
}
.sb-menu .sb-menu-title {
  padding: 6px 10px 8px; font: 600 11px/1 Inter; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted-foreground);
}
.sb-menu .sb-opt {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 8px 10px; border-radius: var(--radius);
  background: transparent; border: none; color: var(--foreground);
  font: 500 13px/1.3 Inter; cursor: pointer;
}
.sb-menu .sb-opt:hover { background: var(--surface-hover); }
.sb-menu .sb-opt .sb-opt-check { width: 16px; height: 16px; flex: none; margin-top: 1px; color: var(--primary); opacity: 0; }
.sb-menu .sb-opt.on .sb-opt-check { opacity: 1; }
.sb-menu .sb-opt .sb-opt-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sb-menu .sb-opt .sb-opt-sub { font: 500 11.5px/1.35 Inter; color: var(--muted-foreground); }

/* ---------- top-bar toolbar slots (filled per page via setTopbar) ----------
   LEFT (#tb-left)  = page title (15px semibold) + an optional context control.
   RIGHT (#tb-right) = that page's view controls, then the static search button. */
/* Nudged 2px below the bar's true centre: the brand image carries the mountain
   mark ABOVE the wordmark, so the wordmark sits low-of-centre. Dropping the whole
   left cluster (title + any context control, kept in sync with each other) onto
   the wordmark's line stops the title reading as too high next to the logo. */
.tb-left  { display: flex; align-items: center; gap: 12px; min-width: 0; position: relative; top: 2px; }
.tb-right { display: flex; align-items: center; gap: 14px; min-width: 0; }
.tb-title {
  margin: 0; font: 600 15px/1.35 Inter; letter-spacing: 0; color: var(--foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tb-meta { font: 500 12px/1.4 Inter; color: var(--muted-foreground); white-space: nowrap; }

/* A compact page-context control that lives beside the title (board client
   picker, cohort switcher). Smaller than a form select - it is chrome. */
.tb-select {
  width: auto; max-width: 220px; height: 30px;
  padding: 0 26px 0 10px; font: 500 12.5px/1 Inter;
}
/* The quiet "?" that folds a page's explainer into a hover tooltip next to the title. */
.tb-info {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex: none; padding: 0; border-radius: 50%;
  border: none; background: transparent; color: var(--muted-2);
  transition: color .12s, background .12s;
}
.tb-info:hover { color: var(--foreground); background: var(--surface-hover); }
.tb-info .ico { width: 16px; height: 16px; }

/* Month stepper (‹ September 2026 ›) - used by the calendar toolbar. */
.tb-monthnav { display: inline-flex; align-items: center; gap: 6px; }
.tb-monthnav .cal-month { font-size: 14px; white-space: nowrap; min-width: 118px; text-align: center; }
.tb-monthnav .cal-nav {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: var(--card); color: var(--foreground);
}
.tb-monthnav .cal-nav:hover { background: var(--surface-hover); }
.tb-monthnav .cal-nav .ico { width: 15px; height: 15px; }

/* Calendar toolbar RIGHT: the cohort countdown + the category legend. The
   legend ALWAYS folds into a "Legend" popover so the bar stays on one line and
   the grid never has to scroll. Below 1200px the countdown hides too. */
.tb-range { font: 500 12px/1.4 Inter; color: var(--muted-foreground); white-space: nowrap; }
.cal-legend-btn { display: inline-flex; }
.cal-legend-pop { display: grid; gap: 8px; }
.cal-legend-pop .cal-key { font-size: 12px; }

/* ---------- dragging a call to another day (admin) ----------
   Same language as the board cards: grab-cursor bars, a straight ghost with
   the elevation shadow (shared .dnd-ghost), a quiet wash on the day you are
   over, and the bar you lifted staying put as a dimmed placeholder. */
.cal-grid.can-drag .cal-ev,
.cal-grid.can-drag .cal-band { cursor: grab; touch-action: none; }
.cal-ev.cal-dragging,
.cal-band.cal-dragging { opacity: .35; }
.cal-cell.drop-active { box-shadow: inset 0 0 0 999px var(--primary-08), inset 0 0 0 2px var(--primary-30); }

/* The event panel's Advanced fold - quiet until opened. */
.ep-adv { margin: 2px 0 13px; border-top: 1px solid var(--line); padding-top: 10px; }
.ep-adv summary {
  cursor: pointer; font: 600 11px/1 Inter; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted-foreground); margin-bottom: 10px; list-style: none;
}
.ep-adv summary::before { content: '\25B8  '; }
.ep-adv[open] summary::before { content: '\25BE  '; }

/* ---------- collateral submission (#collateral) ---------- */
.coll-wrap { max-width: 680px; margin: 0 auto; display: grid; gap: 16px; }
.coll-card .card-title { margin-bottom: 6px; }
.coll-blurb { margin: 0 0 14px; font: 400 13px/1.5 Inter; color: var(--muted-foreground); }
/* BIG on purpose - the field is the page's whole job. */
.coll-links { min-height: 140px; font-size: 14px; }
.coll-files { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.coll-files:empty { display: none; }
.coll-filechip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 4px 6px 4px 9px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); font: 500 12.5px/1 Inter; color: var(--foreground);
}
.coll-filechip .ico { width: 13px; height: 13px; flex: none; color: var(--muted-foreground); }
.coll-filechip .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.coll-send { width: 100%; height: 40px; font-size: 14px; margin-top: 4px; }
.coll-sub { padding: 12px 0; border-top: 1px solid var(--line); }
.coll-sub:first-of-type { border-top: none; }
.coll-sub-when, .coll-sub-who { font: 500 12px/1.4 Inter; color: var(--muted-foreground); margin-bottom: 5px; }
.coll-sub-who b { color: var(--foreground); font-weight: 600; margin-right: 6px; }
.coll-sub-links { font: 400 13px/1.7 Inter; overflow-wrap: break-word; }
.coll-sub-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }

/* ---------- timezone picker (the calendar top bar) ----------
   The BAR wears the abbreviation and nothing else - "PST". A full label in a
   fixed-width select truncated to "PST - San Francisco (yours", so the label
   moved into the panel where it has room. */
.tzbtn { gap: 4px; padding-left: 10px; padding-right: 6px; }
.tzbtn .tzb-abbr { font-weight: 600; letter-spacing: .01em; }
.tzbtn .ico { width: 14px; height: 14px; opacity: .6; }

.pop:has(.tz-pop) { width: auto; padding: 6px; }
.tz-pop { width: 328px; }   /* fits "CET  Central European Time  8:12 PM" */
/* The search box: a magnifier and a borderless input, so the panel reads as one
   surface rather than a form dropped into a popover. */
.tz-search {
  display: flex; align-items: center; gap: 7px; padding: 6px 8px 8px;
  border-bottom: 1px solid var(--line); margin-bottom: 4px;
}
.tz-search .ico { width: 15px; height: 15px; color: var(--muted-2); flex: none; }
.tz-search input {
  flex: 1; min-width: 0; height: 22px; padding: 0; border: none; background: transparent;
  color: var(--foreground); font: 500 13px/1 Inter; outline: none;
}
/* The form-wide focus ring would draw a teal box around a field that has no
   border of its own - the line under the whole row IS the affordance here. */
.tz-search input:focus { border: none; box-shadow: none; }
.tz-list { max-height: 320px; overflow-y: auto; }
.tz-opt {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 7px 8px; border: none; border-radius: var(--radius);
  background: transparent; color: var(--foreground); font: 500 13px/1.2 Inter; cursor: pointer;
}
/* Hover and the keyboard highlight are the same state, drawn the same way. */
.tz-opt:hover, .tz-opt.act { background: var(--surface-hover); }
/* The abbreviation is the identity, so it gets a fixed column - the names line
   up under each other instead of stepping in and out with "HST" vs "AKST". */
/* CENTRED in its column: the names already line up with each other, and a
   left-aligned badge made "PT" and "WITA" look like two ragged columns. */
.tz-opt .tz-ab {
  flex: none; width: 56px; font: 600 10.5px/1 Inter; letter-spacing: .02em;
  color: var(--muted-foreground); text-transform: uppercase; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tz-opt.on .tz-ab { color: var(--primary); }
.tz-opt .tz-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tz-opt .tz-you { color: var(--muted-foreground); font-weight: 500; }
/* The local clock - what makes an offset mean something at a glance. */
.tz-opt .tz-tm { flex: none; font: 500 11.5px/1 Inter; color: var(--muted-2); }
.tz-opt .tz-ck { flex: none; width: 14px; height: 14px; color: var(--primary); }
.tz-opt .tz-ck .ico { width: 14px; height: 14px; }
.tz-empty { padding: 14px 8px; font: 500 12.5px/1.4 Inter; color: var(--muted-foreground); text-align: center; }
/* The popover carries BOTH keys - the categories, then the sprint weeks - so the
   second group needs a heading to say what its colours mean. */
.cal-legend-head {
  font: 600 10px/1 Inter; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted-foreground); margin-top: 4px;
  padding-top: 9px; border-top: 1px solid var(--line);
}
@media (max-width: 1200px) {
  .tb-range { display: none; }
}

/* PMO board's track legend, in the toolbar RIGHT: keep it on one row, and shed
   it before it can crowd the search button on a narrow window (the cards still
   carry their track colour, so the legend is a nicety, not load-bearing). */
.tb-right .track-legend { margin-left: 0; flex-wrap: nowrap; gap: 4px 12px; }
@media (max-width: 1100px) { .tb-right .track-legend { display: none; } }

/* profile row - a plain NavRow like every other. Its icon is the same 16px person
   line glyph the Admin item uses, sitting in the shared .navrow-ico slot, so it
   lines up with the rest instead of a larger circle that read as offset. */
.who-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The popover the collapsed avatar opens: name/email header, then Sign out. */
.who-menu {
  position: fixed; z-index: 400; width: 214px; padding: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  box-shadow: var(--shadow-pop);
}
.who-menu .wm-head {
  display: block; width: 100%; text-align: left; padding: 8px 10px; border-radius: var(--radius);
  background: transparent; border: none; cursor: pointer;
}
.who-menu .wm-head:hover { background: var(--surface-hover); }
.who-menu .wm-name { font: 600 13px/1.35 Inter; color: var(--foreground);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who-menu .wm-email { font: 500 11.5px/1.35 Inter; color: var(--muted-foreground);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who-menu .wm-sep { height: 1px; background: var(--line); margin: 6px 4px; }
.who-menu .wm-item {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  padding: 8px 10px; border-radius: var(--radius);
  background: transparent; border: none; color: var(--foreground);
  font: 500 13px/1 Inter; cursor: pointer;
}
.who-menu .wm-item:hover { background: var(--surface-hover); }
.who-menu .wm-item .ico { width: 15px; height: 15px; flex: none; color: var(--muted-foreground); }

/* ============================================================
   Collapsed rail - the 60px icon-only state. Scoped to >=768px so the phone
   drawer (which reuses the full labelled layout) is never affected.
   ============================================================ */
@media (min-width: 768px) {
  [data-sidebar="collapsed"] .sidebar { padding-left: 8px; padding-right: 8px; }

  /* Every NavRow (nav items AND foot rows) collapses the same way: keep the icon
     in place, fold the label to a clipped zero-width so it can fade back in on
     expand. Let the tooltip escape the rail (the column is short enough to not
     need a scrollbar). ONE rule for both groups - they cannot diverge.
     Left-align with a computed left pad (rail 8 + 13 = full 12 + 9) instead of
     justify-content:center, so the icon's centre stays at x=29 - the SAME column
     as the expanded rows, with no sideways slide on expand. */
  [data-sidebar="collapsed"] .nav { overflow: visible; }
  [data-sidebar="collapsed"] .navrow { padding: 0 0 0 13px; }
  [data-sidebar="collapsed"] .navrow .lbl { flex: 0 0 0; width: 0; opacity: 0; transition-delay: 0s; }
  [data-sidebar="collapsed"] .nav-item { position: relative; }   /* anchors the hover tooltip */
  [data-sidebar="collapsed"] .nav-item .dot { display: none; }
  /* In the rail there's no room for the "Admin" word, but the heading must keep
     its FULL box so the rows below it don't jump up when collapsed: visibility
     hidden reserves the same height (margin + padding + text line) as expanded,
     it just hides the glyph. Same reason the divider keeps its 10px vertical
     margin here - any mismatch shifts every admin icon and the foot group. */
  [data-sidebar="collapsed"] .nav-section { visibility: hidden; }
  [data-sidebar="collapsed"] .nav-divider { margin: 10px 10px; }

  /* Right-side dark tooltip, ~300ms in on hover. Dark in BOTH themes on purpose. */
  [data-sidebar="collapsed"] .nav-item::after {
    content: attr(data-label);
    position: absolute; left: calc(100% + 12px); top: 50%; transform: translateY(-50%);
    padding: 5px 9px; border-radius: 6px; white-space: nowrap;
    background: #1c1c1c; color: #f4f4f4; font: 500 12px/1 Inter;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .22);
    opacity: 0; visibility: hidden; pointer-events: none; z-index: 400;
    transition: opacity .12s ease, visibility .12s ease;
  }
  [data-sidebar="collapsed"] .nav-item:hover::after,
  [data-sidebar="collapsed"] .nav-item:focus-visible::after {
    opacity: 1; visibility: visible; transition-delay: .3s;
  }

}

/* nav rows - geometry (row height, icon slot, label fade) all comes from .navrow;
   this block only carries what is unique to a nav item: the icon colour, the
   active state, and the count dot. */
.nav-item .ico { width: 16px; height: 16px; flex: none; }
/* Active gets its own tint - it used to share --surface-hover with plain
   hover, so "which page am I on" and "where is my cursor" looked identical. */
.nav-item.on { background: var(--primary-12); color: var(--primary); }
.nav-item.on .ico { color: var(--primary); }
.nav-item .dot {
  min-width: 18px; padding: 0 5px; border-radius: 4px;
  background: var(--surface-hover); color: var(--muted-foreground);
  font: 600 11px/18px Inter; text-align: center;
}
.nav-item.on .dot { background: var(--primary-12); color: var(--primary); }

/* Product group heading in the sidebar - only shown when a member has more than
   one product to tell apart. 11px, muted, NOT uppercase (per the design guide's
   quiet-labels rule). The first heading sits flush; later ones get breathing
   room above so the groups read as distinct clusters. */
.nav-section {
  padding: 4px 9px 2px; margin-top: 12px;
  color: var(--muted-foreground);
  font: 600 11px/1.4 Inter; letter-spacing: .01em;
}
.nav-section:first-child { margin-top: 0; }
/* The divider above the Admin group. A hairline, plus matching space. */
.nav-divider {
  height: 1px; margin: 10px 4px; background: var(--line);
}

/* ---------- Tools dock: drag rows in / out of the Tools section (admin) ----------
   #nav-tools boxes the section's rows so a drag has ONE drop target covering the
   whole group, gaps included. Same flex column as .nav, so geometry is unchanged. */
#nav-tools { display: flex; flex-direction: column; gap: 2px; }
/* Where the drop will land: a hairline in the accent, not a gray slot - the row
   is 34px and a full slot would make the list jump while you aim. */
.nav-drop-line { height: 2px; border-radius: 1px; margin: 0 10px; background: var(--primary); }
/* The row being dragged: still in place (HTML5 DnD keeps it), but clearly the
   one in your hand. */
.navrow.pin-dragging { opacity: .35; }

/* The dock poof - a ring of neutral puffs where an unpinned row was let go.
   Each span flies to its own (--dx, --dy) and fades; the host is a zero-size
   fixed anchor, so nothing reflows. */
.poof { position: fixed; z-index: 120; width: 0; height: 0; pointer-events: none; }
.poof span {
  position: absolute; left: -5px; top: -5px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--muted-2); opacity: 0;
  animation: poof-puff .5s ease-out forwards;
}
@keyframes poof-puff {
  from { transform: translate(0, 0) scale(.6); opacity: .9; }
  to   { transform: translate(var(--dx), var(--dy)) scale(1.8); opacity: 0; }
}

/* The admin's inline edit pencil. Quiet until you hover its card. */
.edit-pin {
  margin-left: auto; background: transparent; border: none; border-radius: var(--radius);
  padding: 3px 5px; opacity: 0; color: var(--muted-foreground);
  display: inline-flex; align-items: center;
  transition: opacity .12s, background .12s, color .12s;
}
.edit-pin .ico { width: 13px; height: 13px; vertical-align: 0; }
.kcard:hover .edit-pin, .lrow:hover .edit-pin, .edit-pin:focus-visible { opacity: .7; }
.edit-pin:hover { opacity: 1; background: var(--surface-hover); color: var(--primary); }

/* ---------- profile ---------- */
.avatar-lg {
  width: 64px; height: 64px; border-radius: 50%; flex: none;
  display: grid; place-items: center; overflow: hidden;
  background: var(--surface-hover); color: var(--primary);
  border: 1px solid var(--line); font: 600 24px/1 Inter;
}
.avatar-lg img { width: 100%; height: 100%; object-fit: cover; }

/* The avatar doubles as the photo drop target, so the thing you replace is the
   thing you are looking at. Quiet until you reach for it - a permanent dashed
   ring on a face would read as an error state. */
.avatar-drop { cursor: pointer; transition: border-color .12s, box-shadow .12s; }
.avatar-drop:hover, .avatar-drop:focus-visible, .avatar-drop.dragover {
  border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-08); outline: none;
}

/* ---------- Profile > Your logo ----------
   Two drop targets, each painted in the surface of the theme it is FOR, so the
   card answers the only question that matters about a logo: does it read there.
   Each doubles as its own preview - what you see is the mark that will land in
   the top bar, on that background. */
.logo-slots { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 780px) { .logo-slots { grid-template-columns: 1fr; } }

.logo-slot .fl { display: block; margin-bottom: 7px; }
.logo-slot .help { margin: 7px 0 0; }

.logo-drop {
  display: grid; place-items: center;
  min-height: 96px; padding: 16px 20px;
  border: 1px dashed var(--swatch-light-line); border-radius: var(--card-radius);
  background: var(--swatch-light-bg); color: var(--swatch-light-fg);
  cursor: pointer; text-align: center;
  transition: border-color .12s, box-shadow .12s;
}
.logo-drop.on-dark {
  border-color: var(--swatch-dark-line);
  background: var(--swatch-dark-bg); color: var(--swatch-dark-fg);
}
.logo-drop:hover, .logo-drop.dragover { border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }
.logo-drop img { max-height: 44px; max-width: 100%; width: auto; object-fit: contain; }
.logo-drop .ico { width: 20px; height: 20px; }
.logo-drop .t { font-size: 12.5px; margin-top: 6px; }

/* The bug-report modal's screenshot slot: the logo-drop pattern on the
   theme's own surface. Click, drop or paste; the slot doubles as the preview,
   so what you see is what the admins get. */
/* The reporter's own earlier reports under the bug form: status pill (the
   board column), what they wrote, when. */
.bug-mine { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); }
.bug-mine .fl { margin-bottom: 8px; }
.bug-mine-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: baseline;
  padding: 7px 0; border-top: 1px solid var(--line); font-size: 12.5px;
}
.bug-mine-row:first-of-type { border-top: 0; }
.bug-mine-row .t { color: var(--foreground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bug-mine-row .d { color: var(--muted-foreground); font-size: 11.5px; }
.bug-drop {
  display: grid; place-items: center;
  min-height: 96px; padding: 14px 18px;
  border: 1px dashed var(--line-strong); border-radius: var(--card-radius);
  background: var(--surface); color: var(--muted-foreground);
  cursor: pointer; text-align: center;
  transition: border-color .12s, box-shadow .12s;
}
.bug-drop:hover, .bug-drop:focus-visible, .bug-drop.dragover {
  border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring);
}
.bug-drop .bug-drop-hint { display: grid; place-items: center; gap: 7px; }
.bug-drop .ico { width: 20px; height: 20px; }
.bug-drop .t { font-size: 12.5px; }
.bug-drop img { max-height: 180px; max-width: 100%; width: auto; border-radius: 6px; }

/* The slot itself, at the size the top bar renders it. */
.logo-preview {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 9px 16px; border: 1px solid var(--line); border-radius: 9999px;
  background: var(--background);
}
.logo-preview .lbl {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted-2); white-space: nowrap;
}
.logo-preview img { max-height: 18px; max-width: 150px; width: auto; object-fit: contain; }

.kv {
  display: flex; justify-content: space-between; gap: 14px;
  padding: 8px 0; border-bottom: 1px solid var(--td-border); font-size: 13px;
}
.kv:last-child { border-bottom: none; }
.kv span { color: var(--muted-foreground); }

/* Page container: content spans the full content area (viewport minus sidebar)
   with the standard page padding - never a centred, capped island. A page that
   needs a narrow READABLE column caps its own CONTENT (.page-desc at 78ch, the
   wizard body at 620px), not the page itself. .wide / .full are kept as no-op
   aliases so the renderers that still set them (admin, boards) keep working. */
.main { margin: 0; width: 100%; padding: 24px 24px 72px; }
.main.wide, .main.full { max-width: none; }

/* ---------- card ---------- */
.card {
  background: var(--card-bg);
  border: 1px solid var(--line);
  border-radius: var(--card-radius);
  padding: 18px 18px;
  margin-bottom: 14px;
  box-shadow: var(--shadow);
}
.card-title {
  font-size: 15px; font-weight: 600; letter-spacing: 0;
  color: var(--foreground); margin: 0 0 14px; display: flex; align-items: center; gap: 8px;
}
/* ============================================================
   Global page pattern - ONE layout for every route.

   Content sits DIRECTLY on the page background. A `.card` is reserved for
   ATOMIC items (a kanban card, a chapter tile, a tool tile) - it never wraps
   a whole page or a whole section. A page is:

     .page-head   -> .page-title (+ right-aligned .meta) and an optional
                     one-line .page-desc, then content directly below.
     .page-section -> whitespace + a semibold heading to separate sections,
                     OR a .page-divider hairline. Never a nested box.

   Every route shares this, so page padding, title size and section rhythm
   match app-wide (Admin and the Operations Board already looked like this). */
.page-head { margin: 0 0 22px; }
.page-title {
  display: flex; align-items: center; gap: 10px; margin: 0;
  font: 600 16px/1.35 Inter; letter-spacing: 0; color: var(--foreground);
}
.page-title .meta {
  margin-left: auto; display: inline-flex; align-items: center; gap: 10px;
  font: 500 13px/1.4 Inter; color: var(--muted-foreground);
}
.page-desc {
  margin: 7px 0 0; max-width: 78ch;
  font-size: 13px; line-height: 1.6; color: var(--muted-foreground);
}
/* A slim progress row that hangs under a page title (course header). */
.page-progress { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.page-progress .progress-bar-wrap { flex: 1; height: 5px; }

/* Separate sections on one page: 32px of air, then a quiet heading. */
.page-section { margin-top: 32px; }
.page-section:first-child { margin-top: 0; }
.page-section-title {
  display: flex; align-items: center; gap: 8px; margin: 0 0 14px;
  font: 600 14px/1.3 Inter; letter-spacing: 0; color: var(--foreground);
}
.page-section-title .meta {
  margin-left: auto; font: 500 12.5px/1.4 Inter; color: var(--muted-foreground);
}
.page-section-title .sec-kick { margin: 0; }
/* Full-width hairline, an alternative to a heading between sections. */
.page-divider { height: 1px; background: var(--line); border: 0; margin: 24px 0; }

/* A card-less disclosure (details) that reads as a link, not a box - used for
   "how it works" explainers that shouldn't sit in a giant empty card. */
.page-accordion { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.page-accordion > summary {
  display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
  padding: 13px 2px; font: 600 13px/1.4 Inter; color: var(--foreground);
}
.page-accordion > summary::-webkit-details-marker { display: none; }
.page-accordion > summary::before {
  content: '▸'; color: var(--muted-foreground); font-size: 11px; transition: transform .12s;
}
.page-accordion[open] > summary::before { transform: rotate(90deg); color: var(--primary); }
.page-accordion > summary:hover { color: var(--primary); }
.page-accordion .page-accordion-body { padding: 2px 2px 16px; }

.card-title .meta {
  margin-left: auto; font-weight: 500;
  font-size: 13px; color: var(--muted-foreground);
}

/* ---------- buttons ---------- */
button { font-family: Inter, system-ui, sans-serif; cursor: pointer; }

.btn-primary {
  width: 100%; height: 32px; padding: 0 14px; font: 600 13px/1 Inter; letter-spacing: 0;
  border: 1px solid transparent; border-radius: var(--radius);
  background: var(--primary); color: var(--primary-fg);
  box-shadow: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: filter .12s, background .12s;
}
.btn-primary:hover { filter: brightness(1.06); }
.btn-primary:active { filter: brightness(.96); }
.btn-primary:disabled { opacity: .45; cursor: not-allowed; filter: none; }

.btn-sec {
  height: 32px; padding: 0 14px; font: 500 13px/1 Inter; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: var(--card); color: var(--foreground);
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: background .12s, border-color .12s;
}
.btn-sec:hover { background: var(--surface-hover); border-color: var(--line-strong); }
.btn-sec:disabled { opacity: .5; cursor: not-allowed; }

.btn-green {
  height: 32px; padding: 0 14px; font: 600 13px/1 Inter; border: 1px solid transparent; border-radius: var(--radius);
  background: var(--primary); color: var(--primary-fg);
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
}
.btn-green:hover { filter: brightness(1.06); }

.btn-danger {
  height: 30px; padding: 0 12px; font: 500 13px/1 Inter; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: var(--card); color: var(--red);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.btn-danger:hover { background: rgba(209,47,47,.08); border-color: var(--red); }
.btn-sm { height: 28px; padding: 0 10px; font-size: 12.5px; }
.btn-auto { width: auto; }

/* ---------- inputs ----------
   A TEXT FIELD IS NOT A PILL. Pills are for buttons, chips and toggles - things
   you press. A field you type a sentence into is a box, and a 9999px radius on
   one only pushes the text away from its own left edge and makes a wide select
   look like a lozenge. Card radius, which is the shape the panels around it use.

   The FOCUS RING is one soft ring, not two. It used to take an accent border AND
   3px of --ring (a 22-35% accent) on top of it, which on a small field reads as a
   heavy double outline around whatever you are typing rather than a quiet "you
   are here". Accent border plus a low-opacity halo says the same thing without
   shouting, and that halo is what was actually wrong.

   TYPES: every text-ish input is listed, not just the four that were here. `tel`,
   `url`, `search`, `time` and untyped inputs were all missing, so those fields
   fell through to the browser's own control - a stubby ~170px box, which is why
   some forms had one short field sitting beside full-width ones. */
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=tel], input[type=url], input[type=search],
input[type=time], input[type=datetime-local], input:not([type]),
select, textarea {
  width: 100%; box-sizing: border-box; font-family: Inter, sans-serif; font-size: 13px;
  padding: 8px 11px; border-radius: var(--card-radius);
  border: 1px solid var(--line-strong); background: var(--input-bg); color: var(--foreground);
  transition: border-color .12s, box-shadow .12s;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--ring);
}
::placeholder { color: var(--muted-2); }
textarea { resize: vertical; min-height: 76px; line-height: 1.6; }
label.fl { display: block; font-size: 13px; font-weight: 500; color: var(--foreground); margin-bottom: 6px; }
.field { margin-bottom: 13px; }
.help { font-size: 12.5px; color: var(--muted-foreground); line-height: 1.5; margin-top: 5px; }

/* ---------- segmented control (was tab pills) - used inside pages ---------- */
.tabs {
  display: flex; gap: 2px; padding: 3px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line); width: fit-content;
  flex-wrap: nowrap; overflow-x: auto; max-width: 100%;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  padding: 6px 12px; font: 500 13px/1 Inter; border-radius: 4px;
  border: none; background: transparent; color: var(--muted-foreground);
  display: flex; align-items: center; gap: 6px; white-space: nowrap; flex: none;
  transition: background .12s, color .12s;
}
.tab:hover { color: var(--foreground); }
.tab.on { background: var(--card); color: var(--foreground); box-shadow: var(--shadow); }
.tab .dot {
  min-width: 16px; padding: 0 5px; border-radius: 4px;
  background: var(--primary-12); color: var(--primary);
  font: 600 10px/16px Inter; text-align: center;
}

/* ---------- chips ---------- */
.chip {
  padding: 4px 11px; font: 500 12.5px/1 Inter; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: var(--card);
  color: var(--muted-foreground);
}
.chip.on { background: var(--primary-12); color: var(--primary); border-color: var(--primary-30); }
.chip .n { opacity: .7; }

/* ---------- status badges ---------- */
.badge {
  display: inline-block; padding: 2px 8px; border-radius: 5px;
  font: 500 12px/1.5 Inter; text-transform: none; letter-spacing: 0;
}
.b-success { background: var(--primary-12); color: var(--primary); }
.b-danger  { background: rgba(209,47,47,.12); color: var(--red); }
.b-warning { background: rgba(169,118,10,.15); color: var(--amber); }
.b-neutral { background: var(--surface-hover); color: var(--muted-foreground); }
.b-info    { background: rgba(13,127,158,.12); color: var(--blue); }
/* The staging mirror's marker (state.staging, app.js). Blue = information, the
   one status colour that fits "this is not the real thing"; fixed so it rides
   over every view and never moves with a scroll. */
.staging-pill {
  position: fixed; right: 16px; bottom: 16px; z-index: 9000;
  padding: 5px 12px; border-radius: 9999px;
  background: rgba(13,127,158,.14); color: var(--blue);
  border: 1px solid rgba(13,127,158,.35);
  font: 600 11.5px/1.4 Inter, system-ui, sans-serif; letter-spacing: .04em; text-transform: uppercase;
  pointer-events: none;
}

/* ---------- metric row ---------- */
.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; }
.metric {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 14px; text-align: left;
}
.metric .n { font: 600 24px/1.2 Inter; }
.metric .l {
  font: 500 12px/1.4 Inter; text-transform: none; letter-spacing: 0;
  color: var(--muted-foreground); margin-top: 3px;
}
.n-success { color: var(--primary); } .n-danger { color: var(--red); }
.n-warning { color: var(--amber); }   .n-info { color: var(--blue); }

/* ---------- table ---------- */
.table-wrap { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 700px; }
table.data th {
  position: sticky; top: 0; padding: 8px 10px; text-align: left;
  font: 500 12px/1.4 Inter; text-transform: none; letter-spacing: 0;
  color: var(--muted-foreground); background: var(--th-bg);
  border-bottom: 1px solid var(--line);
}
table.data td {
  padding: 9px 10px; vertical-align: top; border-bottom: 1px solid var(--td-border);
  overflow-wrap: anywhere;
}
table.data tr:hover td { background: var(--surface); }

/* ---------- progress ---------- */
.progress-bar-wrap { height: 6px; border-radius: 4px; background: var(--bar-track); overflow: hidden; }
.progress-bar {
  height: 100%; border-radius: 4px;
  background: var(--primary);
  box-shadow: none;
  transition: width .4s;
}

/* ---------- modal ---------- */
.modal-overlay {
  /* Above the calendar's side panel (z 120): the modal asks a QUESTION about
     what the panel is doing, so it must sit on top of it and its scrim must
     block the panel - a question you can click around is a trap. */
  position: fixed; inset: 0; z-index: 140; padding: 20px;
  background: rgba(0,0,0,.5); backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center;
}
.modal {
  max-width: 560px; width: 100%; max-height: 88vh; overflow-y: auto;
  padding: 20px 20px; border-radius: var(--card-radius);
  border: 1px solid var(--line); background: var(--card);
  box-shadow: var(--shadow-pop);
}
.modal.lg { max-width: 900px; }
/* DRAWER - the card editor. Not a question floating mid-screen but a work
   surface beside the board: pinned to the right edge, full height, sliding in
   the way the admin detail sheet does. The scrim and Escape/backdrop exits are
   the ordinary modal's - only the shape changes. */
.modal-overlay.drawer { padding: 0; align-items: stretch; justify-content: flex-end; }
.modal.drawer {
  max-width: 480px; width: 100%; height: 100%; max-height: none;
  border-radius: 0; border: none; border-left: 1px solid var(--line);
  display: flex; flex-direction: column;
  animation: drawer-slide-in .22s ease-out;
}
@keyframes drawer-slide-in { from { transform: translateX(48px); opacity: .3; } to { transform: none; opacity: 1; } }
/* The notes field is the drawer's body: it takes every pixel the fields above
   and below leave, instead of a fixed few lines. */
.modal.drawer .field-grow { flex: 1; display: flex; flex-direction: column; min-height: 140px; }
.modal.drawer .field-grow textarea { flex: 1; resize: none; min-height: 0; }
/* FULL - the chapter editor. It is not a dialog asking one question, it is a
   writing surface: a body with videos and images in it, read full-width by
   everyone taking the course. So it gets the screen, and the content column
   inside it keeps a readable measure rather than stretching to 2000px. */
/* It sits in the APP's frame, not on top of it: below the topbar, beside the
   sidebar, edge to edge - the same rectangle a maximised chapter uses. No
   scrim, because the chrome behind it is not "behind" it at all; it is still
   the app you are in, and dimming a sidebar you can still click reads as
   broken. The sidebar width follows the same [data-sidebar] attribute the
   shell uses, so collapsing the rail moves this with it.

   --sb-reserve is THE offset .content is laid out by, so borrowing it (rather
   than picking a width) means this cannot disagree with the page underneath -
   including hover mode, where the rail reserves less than it shows. */
.modal-overlay.full {
  padding: 0; background: none; backdrop-filter: none;
  /* The fallback is not decoration. If --sb-reserve ever resolves to nothing,
     `left` is invalid at computed-value time and falls back to `auto` - and
     with the base rule's right:0 that parks the whole editor in a sliver down
     the right-hand edge. A rail's width is the safe wrong answer. */
  top: var(--topbar-h); left: var(--sb-reserve, var(--sb-rail)); display: block;
  transition: left .18s ease-out;
  /* UNDER the sidebar (40), above the page. The editor takes the content area,
     and the sidebar is still the app's - hovering the rail has to expand it
     OVER the editor, the way it does over any other page. At the dialog layer
     (100) the expanded rail opened behind the editor and could not be seen. */
  z-index: 30;
}

.modal.full {
  max-width: none; width: 100%; height: 100%; max-height: none;
  border-radius: 0; padding: 0; border: none; box-shadow: none;
  display: flex; flex-direction: column; overflow: hidden;
}
.modal.full > * { flex: none; }
.modal.full .modal-head {
  position: sticky; top: 0; z-index: 2; margin: 0;
  padding: 14px 20px; border-bottom: 1px solid var(--line); background: var(--card);
}
/* ONE scroll, and it is this one.

   The head and the footer stay put (Save is never off the bottom of a long
   chapter) and everything between them scrolls here - full width, so the
   scrollbar sits at the right edge of the window like every other page rather
   than partway across, and the fields use the space instead of leaving a
   margin down both sides. */
.modal.full .modal-scroll {
  flex: 1 1 auto; overflow-y: auto; padding: 0 24px 24px;
  display: flex; flex-direction: column;
}
/* The breathing room at the top is a MARGIN on the first field, not padding on
   the scroller. Padding there pushed the sticky toolbar down by its own height
   when it pinned, leaving a strip of content sliding past ABOVE the bar. */
.modal.full .modal-scroll > :first-child { margin-top: 18px; }
/* The rich-text body is not its own scrolling window in here - it grows, and
   the one scroll above carries it. A box that scrolls inside a box that
   scrolls inside a page that scrolls is three ways to lose your place. */
.modal.full .rte-body { max-height: none; overflow: visible; }
.modal.full .modal-foot {
  position: sticky; bottom: 0; z-index: 2; margin: 0;
  padding: 12px 20px; border-top: 1px solid var(--line); background: var(--card);
}
/* And the page behind does not scroll while it is open - it was scrolling
   nothing, which is worse than not scrolling at all. */
body.modal-full-open { overflow: hidden; }

/* PANED - the same editor, as a side panel instead of the whole page, so the
   board stays visible beside it. The reader's maximise control does this for a
   chapter; this is that control for editing one.

   The right edge stays pinned and only the left edge moves, so the toggle reads
   as the panel growing and shrinking rather than jumping across the screen -
   the same geometry .detail-drawer uses. */
.modal-overlay.full.paned {
  left: auto; width: min(760px, 94vw);
  border-left: 1px solid var(--line);
  box-shadow: -12px 0 40px rgba(0, 0, 0, .18);
  background: var(--card);
}
/* A narrow panel does not need the wide gutters. */
.modal-overlay.full.paned .modal-scroll { padding-left: 18px; padding-right: 18px; }
/* And the board behind it stays usable, so the page must scroll again. */
body.modal-full-open:has(.modal-overlay.full.paned) { overflow: auto; }

/* The chapter you were READING goes away while you edit it. You reach the
   editor from the reader, so the drawer was still open underneath - and once
   the editor dropped below the sidebar in the stacking order it was below the
   drawer too (92), which put the reader back on top of the thing editing it.
   Hidden rather than closed, so cancelling puts you back where you were. */
body.modal-full-open #detail-drawer { display: none; }

/* The formatting bar follows you down a long chapter. It is the one control you
   reach for repeatedly while writing, and it was scrolling away at the top.

   `.rte-bar` was ALREADY position:sticky and it still did not stick, because
   `.rte` clips itself with overflow:hidden to round its corners - and an
   ancestor with overflow:hidden is a scroll container, so the bar was sticking
   inside a box that never scrolls. Which looks precisely like not sticking.

   Unclipping the wrapper hands stickiness back to .modal-scroll, the one thing
   here that actually scrolls. The corners are the price, and the bar's own
   bottom border draws the line the clip used to. */
.modal.full .rte { overflow: visible; }
.modal.full .rte-bar {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface); border-bottom: 1px solid var(--line);
  border-radius: var(--radius) var(--radius) 0 0;
}
@media (max-width: 720px) {
  .modal-overlay { padding: 0; }
  .modal.full { border-radius: 0; }
}
.modal-head { display: flex; align-items: center; margin-bottom: 14px; }
.modal-head h3 { font: 600 16px/1.3 Inter; margin: 0; flex: 1; }
.modal-head .x {
  background: none; border: none; color: var(--muted-foreground);
  display: inline-flex; align-items: center; padding: 4px; border-radius: var(--radius);
}
.modal-head .x .ico { width: 17px; height: 17px; vertical-align: 0; }
.modal-head .x:hover { background: var(--surface-hover); color: var(--foreground); }

/* ============================================================
   Admin - dense data tables (Supabase / Resend style).

   Rows are one line, 46px, 13px. Secondary info (email, VAT, IP, timestamps)
   lives INLINE next to the name or in the right-side detail panel, never on a
   second row. All controls that used to sit in the row moved to the "..." menu
   and the panel, so the grid stays scannable.
   ============================================================ */
.adm { display: block; }

/* --- inline metrics header: "label value" pairs, <=32px tall --- */
.adm-stats {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  min-height: 32px; margin-bottom: 14px; font-size: 13px; color: var(--muted-foreground);
}
.adm-stat { display: inline-flex; align-items: baseline; gap: 6px; }
.adm-stat b { color: var(--foreground); font-weight: 600; }
.adm-stats .sep { color: var(--line-strong); }

/* --- toolbar: search on the left, actions on the right --- */
.adm-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.adm-toolbar .spacer { flex: 1; }

.adm-search {
  display: inline-flex; align-items: center; gap: 7px;
  height: 32px; padding: 0 10px; border-radius: var(--radius);
  border: 1px solid var(--line-strong); background: var(--input-bg);
  color: var(--muted-2); min-width: 230px;
}
.adm-search:focus-within { border-color: var(--primary); }
.adm-search .ico { width: 14px; height: 14px; }
.adm-search input {
  border: none; background: transparent; padding: 0; height: 100%;
  font: 400 13px/1 Inter; color: var(--foreground); flex: 1; outline: none;
}

/* --- section title + hint tooltip (replaces the beige banners) --- */
.adm-title {
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
  font: 600 15px/1.3 Inter; color: var(--foreground);
}
.adm-sub { font-size: 12px; color: var(--muted-foreground); margin: 0 0 14px; line-height: 1.5; }
.hint {
  width: 15px; height: 15px; border-radius: 50%; flex: none;
  border: 1px solid var(--line-strong); background: transparent; color: var(--muted-2);
  font: 600 10px/13px Inter; text-align: center; cursor: help; position: relative; padding: 0;
}
.hint:hover, .hint:focus-visible { color: var(--foreground); border-color: var(--line-strong); }
.hint:hover::after, .hint:focus-visible::after {
  content: attr(data-tip); position: absolute; left: 0; top: calc(100% + 7px);
  width: max-content; max-width: 340px;
  background: var(--foreground); color: var(--background);
  padding: 8px 11px; border-radius: 6px; font: 400 12px/1.55 Inter; text-align: left;
  z-index: 60; box-shadow: var(--shadow-pop); white-space: normal;
}

/* --- the dense table itself --- */
.dtable { width: 100%; border-collapse: collapse; font-size: 13px; }
.dtable th {
  text-align: left; padding: 7px 12px; font: 500 12px/1.4 Inter;
  color: var(--muted-foreground); border-bottom: 1px solid var(--line); white-space: nowrap;
}
.dtable th.right, .dtable td.right { text-align: right; }
.dtable td {
  padding: 0 12px; height: 46px; vertical-align: middle;
  border-bottom: 1px solid var(--td-border); color: var(--foreground);
}
.dtable tbody tr { transition: background .1s; }
.dtable tbody tr.clickable { cursor: pointer; }
.dtable tbody tr:hover { background: var(--surface); }
.dtable td.num { font-variant-numeric: tabular-nums; }
.dtable td.actions { width: 44px; padding-left: 0; padding-right: 8px; }
.dtable .empty-cell { color: var(--muted-foreground); height: 60px; }

/* --- row selection (checkbox column + bulk-action bar) --- */
.dtable th.checkcell, .dtable td.checkcell { width: 34px; padding-right: 0; text-align: center; }
/* Section lines inside a table (the Users list: Users / Test accounts / Admins
   / Permanent test accounts) - a quiet label over a rule, not a header row. */
.dtable tr.dt-section td {
  padding: 16px 10px 6px; border-bottom: 1px solid var(--line-strong);
  font: 600 11px/1 Inter; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted-foreground); background: transparent;
}
.dtable tr.dt-section:first-child td { padding-top: 4px; }
.dtable tr.dt-section td .dt-muted { font-weight: 500; margin-left: 4px; }
/* A team's people cluster under this header inside a Users section - name,
   seats, and the dots menu with the seat cap. */
.dtable tr.dt-team td {
  padding: 12px 10px 5px; font: 600 12.5px/1 Inter; color: var(--foreground);
  border-bottom: 1px solid var(--line); background: transparent;
}
.dtable tr.dt-team td .dt-muted { font: 500 11.5px/1 Inter; margin-left: 8px; }
.dtable tr.dt-team.solo td { color: var(--muted-foreground); font-weight: 500; }
.dt-team-ico { display: inline-flex; vertical-align: -2px; margin-right: 7px; color: var(--muted-foreground); }
.dt-team-ico .ico { width: 13px; height: 13px; }
.dtable tr.dt-team td .dots-btn { float: right; }
.dtable .checkcell input { width: 15px; height: 15px; margin: 0; cursor: pointer; accent-color: var(--primary); vertical-align: middle; }
.bulk-bar { display: inline-flex; align-items: center; gap: 10px; }
.bulk-bar .bulk-count { font: 500 13px/1 Inter; color: var(--muted-foreground); white-space: nowrap; }

.dt-name { font-weight: 600; color: var(--foreground); }
.dt-email { color: var(--muted-foreground); font-weight: 400; margin-left: 6px; }
/* Admin users: emails blur by default so a screenshare cannot leak the client
   list; the toolbar eye lifts it for 60 seconds. Blur keeps the layout, and
   no selection while hidden means a stray copy grabs nothing. */
.dt-email, .au-mail { transition: filter .25s ease; }
.au-blur .dt-email, .au-blur .au-mail { filter: blur(4.5px); user-select: none; }
/* The users toolbar follows you down the list - the eye and Add person are
   reached for from anywhere in it. Sticks under the fixed topbar; the page
   background behind it, so rows do not show through while it floats. */
.au-toolbar {
  position: sticky; top: var(--topbar-h); z-index: 5;
  background: var(--background); padding: 10px 0; margin-bottom: 2px;
}
.dt-muted { color: var(--muted-foreground); }

/* --- small tags: subtle tinted, max 2 then a "+N" chip --- */
.tags { display: inline-flex; gap: 5px; align-items: center; white-space: nowrap; }
.tag {
  display: inline-block; padding: 2px 7px; border-radius: 4px;
  font: 500 11px/1.5 Inter; background: var(--surface-hover); color: var(--muted-foreground);
  white-space: nowrap;
}
.tag.green { background: var(--primary-12); color: var(--primary); }
.tag.blue  { background: rgba(13,127,158,.12); color: var(--blue); }
.tag.gold  { background: var(--gold-bg); color: var(--gold-fg); }
.tag.more  { background: transparent; color: var(--muted-2); border: 1px solid var(--line); }
.tag.red   { background: rgba(190,50,50,.12); color: var(--red); }
/* A template an admin switched off: still readable, clearly asleep. */
.mail-card.off .mail-frame { opacity: .45; }

/* --- Admin -> Emails: the template catalogue + editor --- */
.adm-subtitle { font: 600 12px/1.3 Inter; color: var(--muted-foreground);
  text-transform: uppercase; letter-spacing: .5px; }
.mail-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; }
.mail-card { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  background: var(--card); display: flex; flex-direction: column; }
/* Stacked, not side-by-side: the trigger sentence leads, the buttons sit
   under it (Matt - the old layout wasted the width and hid the one thing an
   admin needs: WHEN this email fires). */
.mail-card-head { display: block; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.mail-when { font: 600 13px/1.45 Inter; color: var(--foreground); }
.mail-subj { font: 600 13px/1.35 Inter; color: var(--foreground); }
.mail-card-head .actions { display: flex; gap: 6px; flex-wrap: wrap; }
.mail-frame { width: 100%; height: 260px; border: 0; background: var(--background);
  display: block; }
.mail-frame.tall { height: 100%; min-height: 420px; border: 1px solid var(--line);
  border-radius: var(--radius); }
.mail-editor { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: stretch; }
.mail-editor-preview { display: flex; flex-direction: column; gap: 6px; }
@media (max-width: 720px) { .mail-editor { grid-template-columns: 1fr; }
  .mail-frame.tall { min-height: 300px; } }

/* --- status: a coloured dot + the word, not a pill --- */
.stat-dot { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; color: var(--foreground); }
.stat-dot .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.dot.green { background: var(--primary); }
.dot.amber { background: var(--amber); }
.dot.red { background: var(--red); }
.dot.blue { background: var(--blue); }
.dot.neutral { background: var(--muted-2); }

/* --- icon button (the "..." trigger and toolbar overflow) --- */
.icon-btn {
  width: 28px; height: 28px; border-radius: var(--radius); padding: 0;
  border: 1px solid transparent; background: transparent; color: var(--muted-2);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.icon-btn:hover { background: var(--surface-hover); color: var(--foreground); border-color: var(--line); }
.icon-btn .ico { width: 16px; height: 16px; vertical-align: 0; }
.icon-btn.box { width: 32px; height: 32px; border-color: var(--line-strong); background: var(--card); color: var(--muted-foreground); }
.icon-btn.box:hover { background: var(--surface-hover); color: var(--foreground); }

/* --- floating row-action menu --- */
.row-menu {
  position: fixed; z-index: 120; min-width: 178px; padding: 5px;
  background: var(--card); border: 1px solid var(--line-strong);
  border-radius: var(--card-radius); box-shadow: var(--shadow-pop);
  display: flex; flex-direction: column; gap: 1px;
}
.row-menu-item {
  text-align: left; padding: 7px 10px; border-radius: var(--radius);
  font: 500 13px/1.3 Inter; color: var(--foreground); background: transparent; border: none; cursor: pointer;
}
.row-menu-item:hover { background: var(--surface-hover); }
.row-menu-item.danger { color: var(--red); }
.row-menu-item.danger:hover { background: rgba(209,47,47,.08); }

/* --- right-side detail panel --- */
.adm-panel-scrim {
  position: fixed; inset: 0; z-index: 110; background: rgba(0,0,0,.28);
  opacity: 0; transition: opacity .24s;
}
.adm-panel-scrim.show { opacity: 1; }
.adm-panel {
  position: fixed; top: 0; right: 0; z-index: 111; height: 100vh;
  width: min(460px, 96vw); background: var(--card); border-left: 1px solid var(--line);
  box-shadow: -12px 0 40px rgba(0,0,0,.18);
  transform: translateX(100%); transition: transform .28s cubic-bezier(.32,.72,0,1);
  display: flex; flex-direction: column;
}
[data-theme="light"] .adm-panel { box-shadow: -12px 0 40px rgba(16,24,24,.12); }
.adm-panel.show { transform: translateX(0); }
/* Opened from the calendar's edit-event card (host picker -> Add person): park
   LEFT of that card, above its backdrop, so the event stays in view. On narrow
   screens there is no room beside it - cover it instead. */
.adm-panel-scrim.by-cal { z-index: 130; }
.adm-panel.by-cal { z-index: 131; right: min(420px, 94vw); border-right: 1px solid var(--line); }
@media (max-width: 920px) { .adm-panel.by-cal { right: 0; } }
.adm-panel-head {
  display: flex; align-items: center; gap: 10px; padding: 15px 18px;
  border-bottom: 1px solid var(--line); flex: none;
}
.adm-panel-head h3 { font: 600 15px/1.3 Inter; margin: 0; flex: 1; overflow-wrap: anywhere; }
.adm-panel-head .sub { font: 400 12px/1.4 Inter; color: var(--muted-foreground); margin-top: 2px; }
.adm-panel-head .x {
  background: none; border: none; color: var(--muted-foreground); flex: none;
  display: inline-flex; padding: 5px; border-radius: var(--radius); cursor: pointer; align-self: flex-start;
}
.adm-panel-head .x:hover { background: var(--surface-hover); color: var(--foreground); }
.adm-panel-body { flex: 1; overflow-y: auto; padding: 18px; }

.pnl-kv {
  display: flex; justify-content: space-between; gap: 14px; padding: 9px 0;
  border-bottom: 1px solid var(--td-border); font-size: 13px;
}
.pnl-kv .k { color: var(--muted-foreground); flex: none; }
.pnl-kv .v { color: var(--foreground); text-align: right; overflow-wrap: anywhere; }
.pnl-kv .v.mono { font-family: ui-monospace, monospace; font-size: 12px; }
.pnl-sec { margin-top: 20px; }
.pnl-sec:first-child { margin-top: 0; }
.pnl-sec > h4 {
  font: 600 12px/1.4 Inter; color: var(--muted-foreground); margin: 0 0 10px; text-transform: none; letter-spacing: 0;
}

/* A recorded call (Fathom) on the deal panel: the row IS the share link. */
.crm-rec { padding: 8px 0; border-bottom: 1px solid var(--line); }
.crm-rec:last-child { border-bottom: 0; }
.crm-rec-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.crm-rec-link { display: inline-flex; align-items: center; gap: 7px; font: 600 13px/1.4 Inter; color: var(--primary); min-width: 0; }
.crm-rec-link svg { width: 14px; height: 14px; flex: none; }
.crm-rec-sum { margin-top: 4px; }
.crm-rec-sum summary { cursor: pointer; font: 600 11.5px/1.4 Inter; color: var(--muted-foreground); }
.crm-rec-sum-body { margin-top: 6px; max-height: 220px; overflow-y: auto; font: 400 12.5px/1.55 Inter;
  color: var(--muted-foreground); white-space: pre-wrap; overflow-wrap: anywhere; }

@media (max-width: 560px) {
  .adm-panel { width: 100vw; }
}

/* ============================================================
   Keyboard-key chip - the ONE shared component behind every keyboard hint
   (⌘K in the search pill, esc on a detail panel, ↵ / Esc in the palette).
   A kbd-style key: 11px text, muted gray, 1px border with a slightly thicker
   BOTTOM edge for the physical-key look, 4px corners.
   ============================================================ */
.kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; padding: 2px 6px; border-radius: 4px;
  font: 500 11px/1.35 Inter; color: var(--muted-foreground);
  background: var(--surface); border: 1px solid var(--line); border-bottom-width: 2px;
}
.kbd .ico { width: 12px; height: 12px; }

/* A detail-panel close control: a quiet ✕ icon button with the shared "esc" key
   chip beside it. Used on the chapter drawer and the slide-in admin/calendar
   panels, so every panel closes the same way and advertises the same shortcut. */
.close-ctl { display: inline-flex; align-items: center; gap: 6px; flex: none; }
.close-ctl .close-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none; padding: 0; border-radius: var(--radius);
  border: none; background: transparent; color: var(--muted-foreground); cursor: pointer;
  transition: background .12s, color .12s;
}
.close-ctl .close-x:hover { background: var(--surface-hover); color: var(--foreground); }
.close-ctl .close-x .ico { width: 17px; height: 17px; }

/* ============================================================
   Command palette (Cmd/Ctrl-K) - public/search.js.
   ============================================================ */
.cmd-open {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  padding: 6px 10px; border-radius: var(--radius);
  background: var(--card); border: 1px solid var(--line-strong);
  color: var(--muted-foreground); font-size: 13px; font-weight: 500;
  transition: background .12s, border-color .12s;
}
.cmd-open:hover { border-color: var(--line-strong); background: var(--surface-hover); }
.cmd-open .ico { width: 15px; height: 15px; }
/* ⌘K hint - the shared .kbd look; no bespoke styling of its own. */
@media (max-width: 720px) { .cmd-open-label, .cmd-open-kbd { display: none; } .cmd-open { padding: 7px; } }

.cmd-overlay {
  position: fixed; inset: 0; z-index: 200; padding: 12vh 20px 20px;
  background: rgba(0,0,0,.5); backdrop-filter: blur(2px);
  display: flex; align-items: flex-start; justify-content: center;
  animation: cmd-fade .12s ease-out;
}
@keyframes cmd-fade { from { opacity: 0; } to { opacity: 1; } }

.cmd-panel {
  width: 100%; max-width: 620px; max-height: 74vh; display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  box-shadow: var(--shadow-pop); overflow: hidden;
  animation: cmd-rise .14s ease-out;
}
@keyframes cmd-rise { from { transform: translateY(-8px); opacity: .6; } to { transform: none; opacity: 1; } }

.cmd-search { display: flex; align-items: center; gap: 11px; padding: 13px 16px; border-bottom: 1px solid var(--line); }
.cmd-search-ic { display: inline-flex; color: var(--muted-foreground); }
.cmd-search-ic .ico { width: 18px; height: 18px; }
.cmd-search input {
  flex: 1; border: none; background: transparent; color: var(--foreground);
  font: 400 15px/1.4 Inter; padding: 0; outline: none; box-shadow: none;
}
.cmd-search input:focus { box-shadow: none; }
/* .cmd-esc is just a .kbd chip - shared styling, no bespoke rules. */

.cmd-list { overflow-y: auto; padding: 6px 6px 8px; }
.cmd-note { padding: 24px 18px; text-align: center; font-size: 13px; color: var(--muted-foreground); }

/* Group header - Raycast-style small muted label above each type's rows. */
.cmd-group {
  font: 500 11px/1 Inter; letter-spacing: .01em; color: var(--muted-2);
  padding: 12px 12px 5px;
}
.cmd-list > .cmd-group:first-child { padding-top: 4px; }

.cmd-row {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  min-height: 44px; padding: 4px 10px; border-radius: 6px; border: none;
  background: transparent; color: var(--foreground); cursor: pointer;
}
/* Selected row: clear full-row neutral fill, no border blob (Alfred-style). */
.cmd-row.on { background: var(--surface-hover); }
/* Fixed 32px slot so every title starts at the same x. Line icon, muted, no box. */
.cmd-ic {
  flex: none; width: 32px; height: 32px; display: grid; place-items: center;
  color: var(--muted-foreground);
}
.cmd-ic .ico { width: 16px; height: 16px; }
.cmd-main {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  justify-content: center; gap: 1px;
}
.cmd-title {
  font: 400 14px/1.35 Inter; color: var(--foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cmd-sub {
  font: 400 12px/1.3 Inter; color: var(--muted-foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Matched characters: bold, same colour - no background highlight. */
.cmd-hl { font-weight: 600; color: inherit; }
/* The only affordance on the selected row: a quiet enter hint at the right. */
.cmd-enter {
  flex: none; opacity: 0; color: var(--muted-foreground);
  display: inline-flex; align-items: center; padding-right: 4px;
}
.cmd-enter .ico { width: 14px; height: 14px; }
.cmd-row.on .cmd-enter { opacity: 1; }
/* Marks the mode an Actions row already has in force - same accent tick as the
   sidebar's own mode popover (.sb-opt-check), so the two read as one control. */
.cmd-check { flex: none; display: inline-flex; align-items: center; color: var(--primary); }
.cmd-check .ico { width: 14px; height: 14px; }
.cmd-more .cmd-title { color: var(--muted-foreground); font-weight: 500; }

.cmd-foot {
  display: flex; gap: 16px; padding: 9px 16px; border-top: 1px solid var(--line);
  font-size: 11px; color: var(--muted-2);
}
/* Footer hints are .kbd chips, one notch smaller and with a little trailing gap. */
.cmd-foot .kbd { font-size: 10px; min-width: 15px; padding: 2px 5px; margin-right: 3px; }
.cmd-foot .cmd-kbd-ic { padding: 2px 4px; }
@media (max-width: 560px) { .cmd-foot { display: none; } }

/* ---------- misc ---------- */
.empty { text-align: center; padding: 36px; }
.empty .ic { font-size: 30px; opacity: .7; }
.empty .t { font: 600 14px/1.5 Inter; margin-top: 8px; }
.empty .d { font-size: 12.5px; color: var(--muted-foreground); margin-top: 4px; }

.spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--primary);
  animation: spin .8s linear infinite; margin: 0 auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

.callout {
  background: var(--gold-bg); border: 1px solid var(--gold-border); color: var(--gold-fg);
  border-radius: var(--radius); padding: 12px; font-size: 12.5px; line-height: 1.6;
}
/* Status variant: something needs attention (e.g. calls with no join link). Gold
   is for insight, so a warning gets the amber status tokens instead. */
.callout.warn {
  background: var(--amber-bg); border-color: rgba(169,118,10,.34); color: var(--amber);
}
/* Section intro on the first screen of a wizard section - the doc's per-section
   framing. Gold = insight, so it reads as knowledge, not a status. */
.wiz-intro { margin: 0 0 16px; display: flex; gap: 8px; align-items: flex-start; }
.wiz-intro svg { flex: none; margin-top: 2px; }

/* Calendar Subscribe: the little popover, and the same links on the profile page. */
/* .pop is a fixed 230px, which leaves 206px of content once its 12px padding is
   taken - narrower than the 240px these links ask for. So the panel and every
   button in it hung 35px past the card's own right edge, over the calendar
   behind. The popover sizes to this content instead; .sub-pop's own max-width
   still caps how wide that can get. */
.pop:has(.sub-pop) { width: auto; }
.sub-pop { min-width: 240px; max-width: 300px; }
/* Full-width stacked buttons - a flex-wrap row let two long labels (Apple
   Calendar + Outlook) share a line and spill past the popover edge. */
.sub-links { display: grid; grid-template-columns: 1fr; gap: 6px; }
.sub-links > * { width: 100%; box-sizing: border-box; text-align: center; }
.error-banner {
  background: rgba(209,47,47,.08); border: 1px solid rgba(209,47,47,.28); color: var(--red);
  border-radius: var(--radius); padding: 10px 13px; font-size: 13px; font-weight: 500; margin-bottom: 14px;
}

/* Same shape, accent instead of red, for a CONFIRMATION. Red is status only,
   so "a new code is on its way" and "password updated" must not borrow the
   failure treatment - both were doing exactly that. Accent at low opacity,
   so it stays a token swap in both themes. */
.ok-banner {
  background: var(--primary-08); border: 1px solid var(--primary-30); color: var(--primary);
  border-radius: var(--radius); padding: 10px 13px; font-size: 13px; font-weight: 500; margin-bottom: 14px;
}

/* ---------- icons ---------- */
.ico { width: 15px; height: 15px; display: inline-block; vertical-align: -2px; flex: none; }
.ico-sm { width: 13px; height: 13px; }
.ico-lg { width: 20px; height: 20px; }
.empty .ico { width: 28px; height: 28px; opacity: .5; vertical-align: 0; }

/* The theme toggle is a .foot-row (layout comes from there); only the sun/moon
   swap is its own - show the icon for the mode you'd switch TO. */
.theme-toggle .ico-sun  { display: block; }
.theme-toggle .ico-moon { display: none; }
[data-theme="light"] .theme-toggle .ico-sun  { display: none; }
[data-theme="light"] .theme-toggle .ico-moon { display: block; }

.row { display: flex; gap: 10px; align-items: center; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
/* Tool tiles flow to fill the full page width - as many per row as fit, each at
   least 340px, so more tools wrap naturally instead of a fixed two-up column. */
.tools-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 14px; }

/* ---------- All Tools page: the two dock sections ----------
   Each section is a drop zone: dropping a card on "In your menu" pins it,
   dropping on "Other tools" unpins it. The dashed frame only shows while a
   compatible drag is over the zone, so at rest it reads as plain sections. */
.tools-zone {
  border: 1.5px dashed transparent; border-radius: var(--radius);
  padding: 10px; margin: 0 -10px;
}
.tools-zone + .tools-zone { margin-top: 16px; }
.tools-zone.drop-ready { border-color: var(--primary-30); background: var(--primary-12); }
.tools-sec-head { display: flex; align-items: baseline; gap: 10px; margin: 0 0 12px; }
.tools-sec-head .t {
  font: 600 12px/1.5 Inter; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted-foreground);
}
.tools-sec-head .s { font-size: 12.5px; color: var(--muted-foreground); }
.tools-empty {
  border: 1.5px dashed var(--line-strong); border-radius: var(--radius);
  padding: 24px; text-align: center; font-size: 13px; color: var(--muted-foreground);
}

/* ---------- All Tools first-visit explainer ----------
   A dismissible card with a small looping animation: a mini card lifts out of
   the grid, slides into the sidebar's empty slot, the slot lights up. Pure
   CSS; reduced-motion users get the static picture instead. */
.tools-intro { display: flex; align-items: center; gap: 18px; padding: 16px 18px; margin-bottom: 18px; }
.tools-intro .tp-txt { flex: 1; min-width: 0; }
.tools-intro .tp-txt b { font: 600 14px/1.4 Inter; }
.tools-intro .btn-sec { flex: none; }
.tp-demo { position: relative; flex: none; width: 150px; height: 84px; }
.tp-side, .tp-main {
  position: absolute; top: 0; bottom: 0; border-radius: 8px;
  background: var(--surface-hover); padding: 8px;
  display: flex; flex-direction: column; gap: 8px;
}
.tp-side { left: 0; width: 52px; }
.tp-main { left: 62px; right: 0; }
.tp-side > span, .tp-main > span {
  display: block; height: 14px; border-radius: 5px;
  background: var(--line-strong); opacity: .55;
}
.tp-side > .tp-slot {
  background: transparent; border: 1.5px dashed var(--line-strong); opacity: 1;
  animation: tp-slot 3.6s ease-in-out infinite;
}
.tp-main > .tp-ghost { opacity: .2; }
.tp-card {
  position: absolute; left: 0; top: 0; width: 36px; height: 14px; border-radius: 5px;
  background: var(--primary-12); border: 1.5px solid var(--primary-30);
  animation: tp-drag 3.6s ease-in-out infinite;
}
@keyframes tp-drag {
  0%, 14%   { transform: translate(70px, 52px) scale(1); opacity: 1; box-shadow: none; }
  24%       { transform: translate(66px, 38px) scale(1.12); box-shadow: var(--shadow-md, 0 4px 10px rgba(0,0,0,.12)); }
  56%       { transform: translate(12px, 38px) scale(1.12); box-shadow: var(--shadow-md, 0 4px 10px rgba(0,0,0,.12)); }
  68%, 86%  { transform: translate(8px, 52px) scale(1); opacity: 1; box-shadow: none; }
  94%, 100% { transform: translate(8px, 52px) scale(1); opacity: 0; box-shadow: none; }
}
@keyframes tp-slot {
  0%, 60%   { border-color: var(--line-strong); background: transparent; }
  68%, 86%  { border-color: var(--primary-30); background: var(--primary-12); }
  94%, 100% { border-color: var(--line-strong); background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .tp-card { animation: none; transform: translate(8px, 52px); }
  .tp-side > .tp-slot { animation: none; border-color: var(--primary-30); background: var(--primary-12); }
}
.muted { color: var(--muted-foreground); }
.hidden { display: none !important; }

/* ============================================================
   Course views
   ============================================================ */

/* --- GTM Framework diagram --- */
.gtm {
  display: grid; gap: 12px;
  grid-template-areas: "pos pos pos" "lead sales cs" "revops revops revops";
  grid-template-columns: 1fr 1fr 1fr;
}
.gtm-box {
  position: relative; border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 18px 16px 16px; background: var(--surface); cursor: pointer; text-align: center;
  transition: border-color .12s, background .12s;
  overflow: hidden;
}
.gtm-box:hover { border-color: var(--line-strong); background: var(--surface-hover); }
.gtm-box[data-pillar="positioning"] { grid-area: pos; }
.gtm-box[data-pillar="leadgen"] { grid-area: lead; }
.gtm-box[data-pillar="sales"] { grid-area: sales; }
.gtm-box[data-pillar="cs"] { grid-area: cs; }
.gtm-box[data-pillar="revops"] { grid-area: revops; }
.gtm-box .num {
  position: absolute; top: 12px; left: 12px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--surface-hover); color: var(--muted-foreground);
  font: 600 12px/22px Inter; text-align: center;
}
.gtm-box.done .num { background: var(--primary); color: var(--primary-fg); }
.gtm-box .t { font: 600 15px/1.35 Inter; margin: 6px 0 8px; }
.gtm-box .c { font-size: 12.5px; color: var(--muted-foreground); margin-bottom: 10px; }
.gtm-box .fill {
  position: absolute; left: 0; bottom: 0; height: 3px;
  background: var(--primary); box-shadow: none; transition: width .4s;
}
.gtm-box.done { background: var(--primary-08); border-color: var(--primary-30); }

/* --- Kanban --- */
.kanban { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 12px; align-items: flex-start; }

/* ---------- board scrolling ----------
   The board has always been overflow-x:auto, but nothing SAID so: the platform
   overlay scrollbar is invisible at rest, so a board wider than the viewport
   looked like a board with columns missing. Give it a real track. */
.kanban {
  scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent;
  overscroll-behavior-x: contain;   /* don't hand a sideways swipe to the browser's back gesture */
}
.kanban::-webkit-scrollbar { height: 10px; }
.kanban::-webkit-scrollbar-track { background: transparent; }
.kanban::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 9999px; }
.kanban::-webkit-scrollbar-thumb:hover { background: var(--muted-2); }

.kcol {
  flex: 0 0 280px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--card-radius); padding: 12px;
}
.kanban.fit .kcol { flex: 1 1 0; min-width: 168px; padding: 10px; }
.kcol.dragover { border-color: var(--primary); background: var(--primary-08); }
/* Pointer-DnD: the column under the cursor gets a barely-there wash (a faint
   grey overlay that darkens light columns / lightens dark ones), so the drop
   zone is obvious at column level without shouting. */
.kcol.drop-active, .ops-board .kcol.drop-active {
  box-shadow: inset 0 0 0 999px rgba(128, 128, 128, .045);
}
.kcol h4 {
  font: 600 13px/1.4 Inter; text-transform: none; letter-spacing: 0;
  color: var(--muted-foreground); margin: 0 0 10px; display: flex; gap: 8px; align-items: center;
}
.kcol h4 .n {
  margin-left: auto; font-weight: 500; font-size: 12px;
  color: var(--muted-2); letter-spacing: 0;
}
.kcard {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 11px 12px; margin-bottom: 8px; cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}
.kcard[data-drag] { cursor: grab; touch-action: none; }
.kcard:not(.dragging):hover { border-color: var(--line-strong); }

/* --- pointer drag/drop (see makeSortable in board.js) --- */
/* The card's own slot while it's in flight: a plain flat GRAY BLOCK, Asana-style,
   marking where the card will land. It IS the card (so it keeps the card's exact
   size), but its content is hidden and it carries no border, no shadow, and no
   track-colour tint - just a neutral slot. Its position is FLIP-animated in
   board.js, so it slides between valid positions. */
/* `.kcard.is-chapter.dragging` is NOT redundant. `.kcard.is-chapter` further down
   paints the track tint at the same specificity as `.kcard.dragging` and is
   defined later, so it wins the tie - and the slot keeps its colour and its
   stripe instead of going neutral. What you get then is a blank COLOURED box
   where the card was, which reads as the card having vanished. Same reason the
   ghost rule below spells out `.kcard.dnd-ghost`. */
.kcard.dragging,
.ops-card.dragging,
.kcard.is-chapter.dragging {
  background: var(--dnd-slot);
  border-color: transparent;
  box-shadow: none; cursor: grabbing;
}
.kcard.dragging > * { visibility: hidden; }   /* no text */
.kcard.dragging::before,
.kcard.is-chapter.dragging::before { display: none; }   /* no track-colour stripe */
/* The floating clone that follows the cursor - tilt + shadow, click-through.
   Spelled out rather than left as a bare `.dnd-ghost`, because ANY later rule
   that gives a card `position: relative` at equal-or-greater weight drops the
   clone out of fixed positioning, and a clone that is not fixed does not follow
   the pointer at all - it parks itself in the body flow and looks, to the person
   dragging, exactly as though the card vanished.
   `.pmo-card` is one such rule and was already covered. `.kcard.is-chapter` is
   another, and was not: it TIES with `.kcard.dnd-ghost` and is defined later, so
   it won - which is why dragging a chapter card on the ops board lost the card,
   while dragging a plain one worked. */
.kcard.dnd-ghost,
.kcard.is-chapter.dnd-ghost,
.dnd-ghost {
  position: fixed; left: 0; top: 0; margin: 0; z-index: 1000;
  pointer-events: none; cursor: grabbing;
  box-shadow: var(--shadow-pop); opacity: .97; will-change: transform;
}
body.dnd-dragging { cursor: grabbing; user-select: none; }
.kcard .head { display: flex; align-items: flex-start; gap: 9px; margin-bottom: 8px; }
.kcard .t { font: 500 13px/1.45 Inter; }
.kcard.done .t { color: var(--muted-foreground); }
.kcard .kcheck {
  width: 18px; height: 18px; border-radius: 50%; flex: none; margin-top: 1px;
  border: 1.5px solid var(--line-strong); display: grid; place-items: center;
  font-size: 10px; color: var(--primary); background: transparent; cursor: pointer;
}
.kcard .kcheck:hover { border-color: var(--primary); }
.kcard .kcheck.on { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); }
.kcard .foot { display: flex; align-items: center; gap: 8px; }
.kcard .tasks { font-size: 11.5px; color: var(--muted-foreground); margin-left: auto; }

/* pillar tag - identity, kept neutral */
.pill-tag {
  display: inline-block; padding: 2px 8px; border-radius: 5px;
  font: 500 11px/1.6 Inter; background: var(--surface-hover);
  color: var(--muted-foreground); border: 1px solid var(--line);
}

/* Track tag - the coloured category chip. Colours come from `.track-<key>`,
   which is GENERATED from src track-colors.js so it can't drift. Asana-style:
   soft tint, no border, 11px, 4px radius. */
.ctag {
  display: inline-flex; align-items: center; padding: 2px 7px; border-radius: 4px;
  font: 600 11px/1.5 Inter; white-space: nowrap; border: none;
  background: var(--surface-hover); color: var(--muted-foreground);   /* fallback before a track class lands */
}

/* The 3px left accent stripe, shared by every card/section that carries a track
   class. An INSET box-shadow, not a border, so the card's own border-color
   hover can't recolour it. `--track-accent` is set by the generated rule. */
/* The PMO board (.kanban.fit) no longer wears the stripe - its track lives in
   the dot+label above the title - so this is scoped to the course kanban and
   the List sections, which still carry it. */
.kanban:not(.fit) .kcard[class*="track-"], .sec-box[class*="track-"] {
  box-shadow: inset 3px 0 0 var(--track-accent, transparent);
}
.kanban:not(.fit) .kcard[class*="track-"] { padding-left: 13px; }

/* Track dot - a bare colour disc that stands in for the tag text. Colour rides
   on `--track-accent` (set by the generated `.track-<key>` class on the same
   element), so it can never drift from the borders and tags. Used in the PMO
   legend and the List section header. */
.tdot {
  display: inline-block; flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: var(--track-accent, var(--muted-foreground));
}

/* The PMO board legend - the one place the board names its tracks in text.
   Right-aligned on the card-title line, quiet, wraps if the row runs out. */
.track-legend {
  margin-left: auto; display: flex; flex-wrap: wrap; align-items: center;
  gap: 4px 13px; justify-content: flex-end;
}
.track-legend-item {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font: 500 11.5px/1 Inter; color: var(--muted-foreground);
}

/* --- List view --- */
.lsec { margin-bottom: 22px; }
.lsec h3 {
  font: 600 13px/1.4 Inter; text-transform: none; letter-spacing: 0;
  color: var(--muted-foreground); margin: 0 0 10px;
}
.lrow {
  display: flex; align-items: center; gap: 12px; padding: 11px 13px;
  border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 7px;
  background: var(--card); cursor: pointer;
  transition: border-color .12s, background .12s;
}
.lrow:hover { border-color: var(--line-strong); background: var(--surface); }
.lrow .tick {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  border: 1.5px solid var(--line-strong); display: grid; place-items: center;
  color: var(--primary); background: transparent; padding: 0;
}
.lrow .tick .ico { width: 12px; height: 12px; vertical-align: 0; }
.lrow.done .tick { background: var(--primary); color: var(--primary-fg); border-color: var(--primary); }
.lrow .t { font-weight: 500; flex: 1; }
.lrow.done .t { color: var(--muted-foreground); }

/* --- module reader --- */
/* ============================================================
   FIELD DIALOG - the little form the editor asks its questions with.

   Above the chapter editor (which sits at 30) and above the reader's drawer, so
   it is never the thing hidden behind something else. It survives leaving the
   tab, which a native prompt() does not - the whole reason it exists.
   ============================================================ */
.fd-overlay {
  position: fixed; inset: 0; z-index: 150;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(0,0,0,.42); backdrop-filter: blur(2px);
}
.fd {
  width: 100%; max-width: 460px; padding: 20px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--card-radius); box-shadow: var(--shadow-pop);
}
.fd h3 { font: 600 16px/1.3 Inter; margin: 0 0 14px; }
.fd .field { margin-bottom: 12px; }

/* ============================================================
   RESOURCE - a titled link, added on purpose from the editor's Resource
   button. One line: "Resource: <title>". No host line and no service logo;
   the title is what somebody chose to call it, and that is the whole point.

   Blue with the "i", the same language the Resource callout speaks. The icon
   is a CSS mask (lucide info), so it is not markup that can be half-deleted
   inside a contenteditable and it never lands in the clipboard.
   ============================================================ */
.res-chip {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 14px; padding: 11px 14px 11px 40px; position: relative;
  border: 1px solid color-mix(in srgb, var(--blue) 30%, transparent);
  border-radius: var(--card-radius);
  background: var(--blue-bg);
  font: 400 14px/1.4 Inter; text-decoration: none;
  transition: border-color .12s, box-shadow .12s;
}
/* Reading ink, not link blue, and not bold. `.mod-body a` paints every link
   with the accent and outranks a bare .res-chip, which is why the whole row
   came out teal - this beats it in both places the chip is shown. The tint,
   the border and the "i" already say it is a link; the words are just words. */
.mod-body .res-chip, .rte-body .res-chip { color: var(--foreground); font-weight: 400; }
/* The word "Resource:" is the bold part; the title it labels is not. */
.res-chip strong { font-weight: 700; }
.res-chip::before {
  content: ""; position: absolute; left: 14px; top: 50%; margin-top: -8px;
  width: 16px; height: 16px; background: var(--blue);
  -webkit-mask: var(--res-ico) center / contain no-repeat;
  mask: var(--res-ico) center / contain no-repeat;
  --res-ico: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>');
}
.res-chip:hover {
  border-color: color-mix(in srgb, var(--blue) 55%, transparent);
  box-shadow: 0 2px 8px rgba(16,24,24,.08);
}
/* The chapter's Action steps card, wearing the accent the way the old Action
   steps callout did - tinted, bordered, bullseye - so the section reads as the
   thing to DO without a second copy of it living in the body. */
.card.act-card {
  background: color-mix(in srgb, var(--primary) 8%, var(--card));
  border-color: color-mix(in srgb, var(--primary) 30%, transparent);
}
.card-title.act-title { display: flex; align-items: center; gap: 8px; }
.card-title.act-title .ico { color: var(--primary); }
/* Hovering a step deepens the card's own tint. The generic checklist hover
   paints var(--surface), which flashes WHITE against this tinted card. */
.act-card .checklist li:hover {
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  border-color: color-mix(in srgb, var(--primary) 35%, transparent);
}

/* ============================================================
   CALLOUTS - a boxed aside in a chapter: Pro tip, Goal, Resource, Warning.

   Four kinds, and the colour of each is the design system's meaning, not a
   decoration picked to look nice:

     Pro tip   GOLD  - insight and knowledge, which is exactly what gold is for
     Action    ACCENT at low opacity - the work itself, the thing to go and do
     Resource  BLUE  - informational
     Warning   RED   - a real warning, the only decorative-looking use of red
                       that the guide allows, because it is a status

   The icon is a CSS mask rather than markup, so it cannot be deleted, edited or
   half-selected inside a contenteditable, and it never lands in the clipboard
   when somebody copies the text. It inherits `color`, like every other icon
   here. The paths are Lucide, the same ones public/icons.js serves as `bulb`,
   `target`, `info` and `alert` - kept in step by hand, which is the cost of not
   putting an element in the content.
   ============================================================ */
.cb {
  position: relative;
  margin: 0 0 14px; padding: 12px 14px 12px 42px;
  border: 1px solid var(--line); border-radius: var(--card-radius);
  font-size: 14px; line-height: 1.7;
}
.cb > p:last-child { margin-bottom: 0; }
.cb::before {
  content: ""; position: absolute; left: 14px; top: 14px;
  width: 17px; height: 17px;
  background: currentColor;
  -webkit-mask: var(--cb-ico) center / contain no-repeat;
  mask: var(--cb-ico) center / contain no-repeat;
}
/* lucide bulb */
.cb-tip {
  background: var(--gold-bg); border-color: var(--gold-border); color: var(--gold-fg);
  --cb-ico: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg>');
}
/* lucide target */
.cb-goal {
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  border-color: color-mix(in srgb, var(--primary) 30%, transparent);
  color: var(--primary);
  --cb-ico: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>');
}
/* lucide info */
.cb-info {
  background: var(--blue-bg); border-color: color-mix(in srgb, var(--blue) 30%, transparent);
  color: var(--blue);
  --cb-ico: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>');
}
/* lucide alert-triangle */
.cb-warn {
  background: var(--red-bg); border-color: color-mix(in srgb, var(--red) 32%, transparent);
  color: var(--red);
  --cb-ico: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>');
}
/* The BODY text stays readable ink; only the label and the icon carry the hue,
   so a paragraph of warning is not a paragraph of red. */
.cb p { color: var(--foreground); }
.cb strong:first-child { color: inherit; }

/* ============================================================
   CODE BLOCK - a prompt or a snippet, meant to be copied verbatim.

   One rule set, applied in the reader AND inside the editor, so what an admin
   writes is what a member reads. Whitespace is preserved, long lines scroll
   INSIDE the block (never widening the page), and the numbers down the left
   are CSS counters on the line spans the reader wraps each line in - so they
   are decoration, and copying the block copies the code, not the numbers.
   ============================================================ */
pre.code-block {
  position: relative;                 /* the Copy button anchors to THIS box */
  margin: 0 0 14px; padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* WRAP, never scroll sideways. A prompt is prose in a monospace box: a
   horizontal scrollbar hides the ends of the longest lines, which are the ones
   most worth reading. pre-wrap keeps the author's own line breaks and
   indentation; anywhere stops one long URL widening the block.

   On the BLOCK as well as its <code>: an imported chapter stores a bare <pre>
   with no <code> inside it, and styling only the child left those scrolling. */
pre.code-block, pre.code-block code {
  white-space: pre-wrap; overflow-wrap: anywhere;
  font: 400 12.5px/1.7 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--foreground);
}
pre.code-block code { display: block; }
.rte-body pre.code-block { cursor: text; }

/* The block plus its Copy button. Relative so the button can sit in the corner
   of the block rather than above it, where it would push the content down. */
.code-wrap { position: relative; }
.code-copy {
  position: absolute; top: 8px; right: 8px; z-index: 1;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 9999px;
  border: 1px solid var(--line); background: var(--card);
  color: var(--muted-foreground); font: 500 12px/1 Inter; cursor: pointer;
  opacity: 0; transition: opacity .12s, color .12s, border-color .12s;
}
/* Shown on hover, and always once focused by the keyboard - a control that
   only exists on hover is a control somebody tabbing cannot find. */
.code-wrap:hover .code-copy, .code-copy:focus-visible { opacity: 1; }
.code-copy:hover { color: var(--foreground); border-color: var(--muted-2); }
.code-copy.ok { opacity: 1; color: var(--primary); border-color: var(--primary); }
@media (hover: none) { .code-copy { opacity: 1; } }

.mod-body { font-size: 14px; line-height: 1.75; }
.mod-body h3 { font: 600 15px/1.4 Inter; text-transform: none; letter-spacing: 0;
  color: var(--foreground); margin: 22px 0 8px; }
.mod-body p { margin: 0 0 12px; }
/* A raw address is one unbreakable word as far as the browser is concerned, so
   a long Google Docs URL pushed the whole chapter off the side of the screen.
   It wraps now. `anywhere` rather than `break-word` because these break at
   slashes and dashes, not at spaces there are none of. */
.mod-body a { color: var(--primary); overflow-wrap: anywhere; }
.mod-body p, .mod-body li { overflow-wrap: anywhere; }
/* A resource is a title, not an address, so it wraps like ordinary words. */
.mod-body .res-chip { overflow-wrap: normal; }
/* zoom-in: clicking opens the full file in a new tab (see app.js), and the
   cursor is what says so - a diagram scaled to the column hides its detail. */
.mod-body img { max-width: 100%; border-radius: var(--card-radius); cursor: zoom-in; }
.mod-body .import-todo {
  background: var(--gold-bg); border: 1px solid var(--gold-border); color: var(--gold-fg);
  border-radius: var(--radius); padding: 10px 12px; font-size: 12.5px;
}
.mod-body .import-todo a { color: var(--gold-fg); text-decoration: underline; }
.video-wrap {
  position: relative; padding-bottom: 56.25%; height: 0; border-radius: var(--card-radius); overflow: hidden;
  border: 1px solid var(--line); margin-bottom: 16px; background: #000;
}
.video-wrap iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.checklist { list-style: none; padding: 0; margin: 0; }
.checklist li {
  display: flex; gap: 11px; align-items: flex-start; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 7px;
  background: var(--card); cursor: pointer; font-size: 13px; line-height: 1.55;
}
.checklist li:hover { border-color: var(--line-strong); }
.checklist li .box {
  width: 18px; height: 18px; border-radius: 5px; flex: none; margin-top: 1px;
  border: 1.5px solid var(--line-strong); display: grid; place-items: center;
  color: var(--primary-fg);
}
.checklist li .box .ico { width: 12px; height: 12px; vertical-align: 0; }
.checklist li.done .box { background: var(--primary); border-color: var(--primary); }
.checklist li.done span { color: var(--muted-foreground); text-decoration: line-through; }

/* --- achievements --- */
.badges { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
.badge-card {
  border: 1px solid var(--line); border-radius: var(--card-radius); padding: 16px; text-align: center;
  background: var(--surface); opacity: .5; filter: grayscale(1);
}
.badge-card.earned {
  opacity: 1; filter: none; border-color: var(--primary-30); background: var(--primary-08);
}
.badge-card .ic { font-size: 28px; }
.badge-card .t { font: 600 13px/1.4 Inter; margin: 7px 0 4px; }
.badge-card .d { font-size: 11.5px; color: var(--muted-foreground); line-height: 1.5; }
.badge-card .when { font-size: 10.5px; color: var(--primary); font-weight: 600; margin-top: 7px; }

/* toast */
.toast {
  position: fixed; right: 22px; bottom: 22px; z-index: 200;
  display: flex; gap: 12px; align-items: center;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 13px 16px; box-shadow: var(--shadow-pop); animation: pop .3s ease-out;
}
.toast .ic { font-size: 24px; }
.toast .t { font: 600 13px/1.3 Inter; }
.toast .d { font-size: 11.5px; color: var(--muted-foreground); }
/* Update cards (admins only) - stacked in the corner, one per shipped change,
   each staying put until its X or its Check it out! is pressed. The stack owns
   the fixed corner spot; the cards inside flow normally. */
#toast-stack {
  position: fixed; right: 22px; bottom: 22px; z-index: 200;
  display: flex; flex-direction: column; gap: 10px; align-items: flex-end;
}
#toast-stack .toast { position: static; }
.update-toast { max-width: 300px; align-items: flex-start; padding: 10px 12px; gap: 9px; }
.update-toast .ic { display: inline-flex; color: var(--primary); margin-top: 1px; }
.update-toast .ic .ico { width: 15px; height: 15px; }
.update-toast .up-body { flex: 1; min-width: 0; }
.update-toast .t { font-size: 12.5px; }
.update-toast .d { font-size: 11px; line-height: 1.45; }
.update-toast .d.where { color: var(--primary); font-weight: 600; margin-top: 3px; }
.update-toast .up-go { white-space: nowrap; margin-top: 7px; padding: 4px 12px; font-size: 11.5px; }
.update-toast .icon-btn { width: 22px; height: 22px; flex: 0 0 auto; }
@keyframes pop { from { transform: translateY(14px); opacity: 0; } to { transform: none; opacity: 1; } }

/* --- tools --- */
.tool-sec { border-top: 1px solid var(--line); padding-top: 18px; margin-top: 18px; }
.tool-sec:first-child { border-top: none; padding-top: 0; margin-top: 0; }
.tool-sec > h3 { font: 600 15px/1.4 Inter; margin: 0 0 5px; }
.tool-sec > .blurb { font-size: 12.5px; color: var(--muted-foreground); line-height: 1.6; margin-bottom: 14px; }
.banned { background: rgba(209,47,47,.12); border-bottom: 1.5px solid var(--red); }
.saved-note { font-size: 12px; color: var(--primary); font-weight: 600; }
.trow { display: grid; gap: 8px; margin-bottom: 8px; align-items: start; }
.trow .del {
  background: none; border: none; color: var(--muted-foreground); font-size: 16px; padding: 8px 4px;
}
.trow .del:hover { color: var(--red); }

/* --- tools: wizard --- */
/* De-boxed: the wizard sits directly on the page background (no outer card).
   The gap stands in for the padding the removed card used to give the content
   column, so the body keeps a gutter from the rail rather than hugging it. */
/* The wizard is a LEFT-anchored two-column layout: the rail sits flush against
   the sidebar (the page's own 24px padding is the only gutter), never a centred
   island. So the tools page opts out of the centred 1080px column. */
/* FIT layout, same pattern as the calendar page: the wizard owns the viewport,
   the page itself never scrolls, and the Back/Continue bar is pinned under a
   single scroll region (.wiz-stage) instead of riding the content. Released
   again under 900px, where an on-screen keyboard needs normal page scroll. */
.content:has(.main.wiz-page) { height: calc(100dvh - var(--topbar-h)); overflow: hidden; }
.main.wiz-page {
  flex: 1 1 auto; min-height: 0; max-width: none; margin: 0;
  padding: 20px 24px 0; overflow: hidden;
  display: flex; flex-direction: column;
}
.wiz { display: flex; gap: 44px; align-items: flex-start; flex: 1 1 auto; min-height: 0; }

/* left step rail - flush to the content column's left edge, its own scroller
   (the page no longer scrolls, so there is nothing left to stick to) */
.wiz-rail {
  width: 230px; flex: none; align-self: stretch; min-height: 0; overflow-y: auto;
}
.wiz-steplist { display: flex; flex-direction: column; gap: 2px; }
.wiz-node {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 8px 10px; border: none; background: none; border-radius: var(--radius);
  color: var(--muted-foreground); font: 500 13px/1.35 Inter; cursor: pointer;
  transition: background .12s, color .12s;
}
.wiz-node:hover { background: var(--surface-hover); color: var(--foreground); }
.wiz-node-t { min-width: 0; }
.wiz-step .wiz-node-t { flex: 1; }
.wiz-mark {
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font: 600 11px/1 Inter; border: 1px solid var(--line-strong);
  background: var(--card); color: var(--muted-foreground);
}
.wiz-mark .ico { width: 13px; height: 13px; }
.wiz-mark.done { background: var(--primary-12); border-color: var(--primary-30); color: var(--primary); }
.wiz-mark.current { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); }
.wiz-node.is-current { color: var(--primary); font-weight: 700; background: var(--primary-08); }
.wiz-node.is-done .wiz-node-t { color: var(--foreground); }
/* "n/total" position pill on the active step */
.wiz-pos {
  flex: none; font: 600 11px/1 Inter; color: var(--muted-foreground);
  background: var(--surface); border-radius: 9999px; padding: 3px 8px;
}
.wiz-node.is-current .wiz-pos { background: var(--primary-12); color: var(--primary); }
/* sub-items: the fields of the current step */
.wiz-sub { padding: 6px 10px; font-size: 12.5px; }
/* A named sub-group (Region(s)) is one clickable row; while you are on one of
   its screens it is "open" and its Introduction / Selection indent beneath it. */
.wiz-sub.is-open { color: var(--foreground); font-weight: 600; }
.wiz-sub.is-grouped { padding-left: 22px; }
.wiz-subdot {
  width: 7px; height: 7px; flex: none; border-radius: 50%;
  background: var(--line-strong); margin-left: 7px;
}
.wiz-sub.is-current { color: var(--primary); font-weight: 600; }
.wiz-sub.is-current .wiz-subdot { background: var(--primary); box-shadow: 0 0 0 3px var(--primary-12); }
.wiz-sub.is-done .wiz-subdot { background: var(--primary); }
.wiz-sub.is-done .wiz-node-t { color: var(--foreground); }
.wiz-subs {
  display: flex; flex-direction: column; gap: 1px;
  margin: 2px 0 4px 21px; padding-left: 10px; border-left: 1px solid var(--line);
}
/* compact rail (dropdown + slim bar) - only shown under 900px */
.wiz-rail-compact { display: none; flex-direction: column; gap: 10px; }
.wiz-rail-compact .progress-bar-wrap { height: 4px; }

/* right content column - LEFT-aligned to the rail. Three stacked parts: the
   progress row (fixed), the stage (the only scroller) and the pinned nav. */
/* `align-self: stretch` is load-bearing, exactly as it is on .wiz-rail above.
   .wiz sets `align-items: flex-start`, so its children are NOT stretched to its
   height - they size to their content. The rail opts back in so its own scroller
   has a height to scroll within; .wiz-main never did, so .wiz-stage below it had
   no constrained height either, grew to fit its content, and the overflow was
   clipped by the `overflow: hidden` further up instead of scrolling. Reachable in
   Chrome, which resolves the height differently, and unreachable in Safari. */
.wiz-main {
  flex: 1; min-width: 0; align-self: stretch;
  display: flex; flex-direction: column; min-height: 0;
}
.wiz-progress {
  flex: none; margin: 0 0 20px; display: flex; align-items: center; gap: 12px;
}
.wiz-progress .progress-bar-wrap { flex: 1; height: 4px; }
.wiz-count { font: 500 11.5px/1 Inter; color: var(--muted-foreground); white-space: nowrap; }

/* The stage: field column + context aside. The field column keeps a READABLE
   measure; the width left over goes to the aside rather than to a dead gutter. */
.wiz-stage {
  /* overflow-x is NOT free to be `visible` here: per spec, a box with one axis
     scrollable computes the other to `auto`. Every panel enters on
     `panelInFwd`, which starts at translateX(14px), so for the length of that
     animation the stage really is 14px wider than itself - it becomes sideways
     scrollable, keeps whatever scroll it picks up, and the panel is left clipped
     down its left edge once the animation has finished. `clip` cannot scroll at
     all, so there is nothing to keep. `hidden` first, for anything that has not
     got `clip` yet. */
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; overflow-x: clip;
  padding-bottom: 8px;
  /* One column: the 340px aside track is gone with the aside itself - it was
     leaving a dead strip down the right of every question. */
  display: grid; grid-template-columns: minmax(0, 1fr);
  /* align-items keeps each item at the top of ITS row; align-content keeps the
     ROWS at the top of the grid. Without the second, a stage taller than its
     content stretched the auto rows to fill it - on a short screen that put 369px
     of nothing between the fields and the summary, and pushed the summary far
     enough down that it had to be scrolled to on a window with room to spare. */
  align-items: start; align-content: start;
}
/* The field column stacks ABOVE the aside. .wiz-summary animates in with
   `panelInFwd ... both`, and an animation that FILLS keeps applying its transform
   after it finishes - which keeps a stacking context alive on the summary for as
   long as it is on screen. Being a later grid item, it therefore painted over
   anything absolutely positioned in the column above it, however high that thing
   set its own z-index: a suggestion dropdown near the foot of a short screen was
   half-hidden behind "The picture so far". z-index cannot climb out of a stacking
   context, so the column it belongs to has to win instead. */
/* No cap: any measure narrower than the stage read as wasted right-side
   space and cost scrolling (tried 760px, then 900px - both rejected). */
.wiz-body { max-width: none; margin: 0; position: relative; z-index: 1; }

/* A wide screen (map, persona grid, tiles, tables, Review) takes the whole
   stage; the aside drops to a row underneath and keeps only the summary bar. */
.wiz-stage.is-wide { grid-template-columns: minmax(0, 1fr); }
.wiz-stage.is-wide .wiz-body { max-width: none; }
.wiz-stage.is-wide .wiz-aside { position: static; }
.wiz-stage.is-wide .wiz-aside-step, .wiz-stage.is-wide .wiz-aside-tips { display: none; }
/* Two presentations, one source: when the aside shows the tips, the heading's
   "?" chip would be a duplicate, so it stands down. */
.wiz-stage:not(.is-wide) .wiz-tipsq { display: none; }

/* aside blocks - tokens only, no new colour: a hairline frame on the surface */
.wiz-aside-where {
  font: 600 11px/1 Inter; letter-spacing: .04em; text-transform: uppercase;
  color: var(--primary); margin-bottom: 6px;
}
.wiz-aside-title { font: 600 14px/1.4 Inter; color: var(--foreground); }
.wiz-aside-tips {
  border: 1px solid var(--line); border-radius: var(--card-radius);
  background: var(--surface); padding: 13px 15px;
}
.wiz-aside-h {
  display: flex; align-items: center; gap: 7px; font: 600 12px/1.3 Inter;
  color: var(--primary); margin-bottom: 8px;
}
.wiz-aside-tips ul {
  margin: 0; padding-left: 18px; line-height: 1.7; font-size: 12.5px; color: var(--muted-foreground);
}
.wiz-aside-tips li { margin-bottom: 5px; }
.wiz-aside-tips li:last-child { margin-bottom: 0; }
/* In the aside column the summary reads as a stacked card; in wide mode it
   stays the one-line bar it has always been, under the fields. */
.wiz-stage:not(.is-wide) .wiz-summary {
  margin-top: 0; flex-direction: column; align-items: flex-start; gap: 8px;
}

.wiz-head { margin-bottom: 18px; }
.wiz-stepno { font: 600 11px/1 Inter; letter-spacing: .04em; text-transform: uppercase; color: var(--primary); margin-bottom: 7px; }
.wiz-title-row { display: flex; align-items: center; gap: 9px; margin-bottom: 6px; }
.wiz-title { font: 600 20px/1.3 Inter; margin: 0; }
.wiz-purpose { font-size: 13px; color: var(--muted-foreground); line-height: 1.6; margin: 0; }

.wiz-human { margin-bottom: 16px; font-size: 12px; padding: 10px 12px; }

/* tips as a small "?" next to the heading, revealing a popover */
.wiz-tipsq { position: relative; flex: none; }
.wiz-tipsq > summary {
  cursor: pointer; list-style: none; width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--line-strong); background: var(--card); color: var(--muted-foreground);
  font: 700 12px/1 Inter; display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s, color .12s, border-color .12s;
}
.wiz-tipsq > summary::-webkit-details-marker { display: none; }
.wiz-tipsq > summary:hover { color: var(--primary); border-color: var(--primary-30); }
.wiz-tipsq[open] > summary { background: var(--primary); color: var(--primary-fg); border-color: var(--primary); }
.wiz-tipsq-pop {
  position: absolute; z-index: 60; top: 28px; left: 0; width: 330px; max-width: 78vw;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  box-shadow: var(--shadow-pop); padding: 12px 14px;
}
.wiz-tipsq-h { display: flex; align-items: center; gap: 7px; font: 600 12px/1.3 Inter; color: var(--primary); margin-bottom: 8px; }
.wiz-tipsq-pop ul { margin: 0; padding-left: 18px; line-height: 1.7; font-size: 12.5px; color: var(--muted-foreground); }
.wiz-tipsq-pop li { margin-bottom: 5px; }

/* one field per screen */
.wiz-body .field { margin-bottom: 18px; }
.wiz-body label.fl { font-size: 14px; font-weight: 500; }
.wiz-body .field .help { font-size: 12px; }
/* Bare screen: hide the single field's own label + help (the heading carries
   them). Direct children only, so a widget's nested subfield labels survive.
   The live banned-buzzword warning is NOT help text, so it stays visible. */
.wiz-fields.is-bare > .field > label.fl { display: none; }
.wiz-fields.is-bare > .field > .help:not(.banned-note) { display: none; }
.wiz-fields.is-bare > .field { margin-bottom: 0; }
/* A multi-field screen on a wide stage (the matched competitor / USP tables)
   sits side by side rather than stacking two half-empty builders. */
.wiz-stage.is-wide .wiz-fields:not(.is-bare) {
  display: grid; gap: 24px 34px; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
}
/* Two builders side by side are two SECTIONS of one screen, so their names get
   the section-label treatment the aside already uses - uppercase, accent, over a
   hairline - rather than looking like one more field label among the column
   headings directly beneath them. */
.wiz-stage.is-wide .wiz-fields:not(.is-bare) > .field > label.fl {
  font: 600 11px/1.3 Inter; letter-spacing: .04em; text-transform: uppercase;
  color: var(--primary); margin: 0 0 12px; padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
/* And their column headings line up across the two. One side's heading wraps to
   two lines and the other's does not, which walked every row beneath it out of
   step with its neighbour; bottom-aligning them in a two-line box keeps the
   first input on both sides at the same height. */
.wiz-stage.is-wide .wiz-fields:not(.is-bare) [data-table] > .trow:first-child {
  align-items: end; min-height: 3.2em;   /* the ROW, so both tables match even
     when one side's heading runs to three lines and the other's to one */
}
.wiz-stage.is-wide .wiz-fields:not(.is-bare) [data-table] > .trow:first-child label.fl {
  display: flex; align-items: flex-end; margin: 0;
}

/* continue / back - pinned below the stage, so it never scrolls away */
.wiz-nav {
  flex: none; display: flex; align-items: center; margin-top: 0;
  padding: 16px 0; border-top: 1px solid var(--line); background: var(--background);
}

/* personas as a 2-col card grid */
.persona-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: start; }
.persona {
  border: 1px solid var(--line); border-radius: var(--card-radius); padding: 13px 14px;
  background: var(--surface); transition: border-color .12s;
}
.persona[open] { border-color: var(--line-strong); background: var(--card); grid-column: 1 / -1; }
.persona.is-filled { border-color: var(--primary-30); }
.persona > summary { cursor: pointer; }
.persona > summary::-webkit-details-marker { display: none; }
.persona-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.persona h4 { font: 600 13.5px/1.4 Inter; margin: 0; color: var(--foreground); }
.persona .role { font-size: 11.5px; color: var(--muted-foreground); margin-top: 5px; line-height: 1.5; }
.persona-empty {
  font: 500 11px/1 Inter; color: var(--muted-2); padding: 2px 7px;
  border: 1px solid var(--line); border-radius: 9999px;
}
.persona-body { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }

/* review "document" - columns of sections, so the whole positioning is readable
   without scrolling a single narrow ribbon. Every section keeps its hairline
   rule (no first-child exception: in a grid the "first" one is per column). */
/* Columns that PACK. As a grid, every row was as tall as its tallest section, so
   a short section left a hole beneath it until the next row began - which is
   where the empty space on the right came from. Multi-column flows each section
   into the shortest column instead, and break-inside keeps one whole. */
.rv-doc { columns: 420px 3; column-gap: 34px; }
.rv-sec { break-inside: avoid; }
.rv-sec { border-top: 1px solid var(--line); padding: 18px 0; }
.rv-open { column-span: all; }
.rv-sec-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.rv-sec-head h3 { font: 600 15px/1.4 Inter; margin: 0; }
.rv-none { font-size: 12.5px; color: var(--muted-2); font-style: italic; }
.rv-f { margin-bottom: 13px; }
.rv-l { font: 500 12px/1.4 Inter; color: var(--muted-foreground); margin-bottom: 3px; }
.rv-v { font-size: 13.5px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
.rv-list { margin: 2px 0 0; padding-left: 18px; font-size: 13.5px; line-height: 1.7; }
.rv-rows { display: flex; flex-direction: column; gap: 8px; }
.rv-row {
  border: 1px solid var(--line); border-radius: var(--radius); padding: 10px 12px;
  background: var(--surface); display: flex; flex-direction: column; gap: 7px;
}
.rv-cell { font-size: 13px; line-height: 1.55; overflow-wrap: anywhere; }
.rv-ck { display: block; font: 500 11px/1.4 Inter; color: var(--muted-foreground); margin-bottom: 1px; }
.rv-persona {
  border: 1px solid var(--line); border-radius: var(--radius); padding: 10px 12px;
  background: var(--surface); margin-top: 8px; display: flex; flex-direction: column; gap: 7px;
}
.rv-persona h4 { font: 600 13px/1.4 Inter; margin: 0 0 2px; }
.rv-open { border-top: 1px solid var(--line); padding-top: 18px; margin-top: 8px; }
.rv-open h3 { font: 600 14px/1.4 Inter; margin: 0 0 10px; display: flex; align-items: center; gap: 6px; }
.rv-open h3 .muted { font-weight: 500; }
.rv-open ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.rv-open li { font-size: 12.5px; }
.rv-jump {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--primary); font: 600 12.5px/1.4 Inter; text-decoration: underline;
}
.rv-alldone h3 { color: var(--primary); }
.rv-alldone h3 .ico { width: 15px; height: 15px; }

/* ---------- responsive ----------
   Below 768px the sidebar leaves the flow and becomes an overlay drawer: it
   slides in from the left over a scrim, toggled by the top-bar hamburger. It
   keeps its FULL labelled layout here (it is not a rail), so the collapsed-rail
   rules above - all gated to >=768px - never touch it. */
@media (max-width: 767px) {
  .nav-burger { display: inline-flex; }

  /* The brand slot shrinks to its content so the hamburger + page title fit. */
  .brand { width: auto; padding-left: 16px; }

  /* The sidebar becomes an overlay drawer: full labelled layout (the rail rules
     are all gated to >=768px), slid off-screen until the hamburger opens it. Hover
     mode never fires here - the drawer is all-or-nothing. Content takes the full
     width; the drawer floats over it. */
  .content { margin-left: 0; }
  .sidebar {
    top: var(--topbar-h); width: 264px; z-index: 120;
    transform: translateX(-100%);
    transition: transform .22s ease;
    box-shadow: none;
  }
  body.nav-open .sidebar { transform: translateX(0); box-shadow: 6px 0 30px rgba(16, 24, 24, .22); }

  /* Scrim only exists while the drawer is open - this override beats both the
     element's [hidden] attribute and the base .nav-scrim rule. */
  body.nav-open .nav-scrim {
    display: block; position: fixed; inset: var(--topbar-h) 0 0 0; z-index: 110;
    background: rgba(16, 24, 24, .38);
  }
}
@media (max-width: 760px) {
  .topbar { padding: 8px 14px; }
  .main { padding: 20px 14px 56px; }
  .card { padding: 15px 15px; }
  .grid2, .grid3 { grid-template-columns: 1fr; }
  .metrics { grid-template-columns: repeat(2, 1fr); }
  .gtm { grid-template-areas: "pos" "lead" "sales" "cs" "revops"; grid-template-columns: 1fr; }
  .tabs { width: 100%; }
  table.data { min-width: 700px; }
  .persona-grid { grid-template-columns: 1fr; }
}
/* Under 1280px there is no room for a second column: the aside drops below the
   fields and keeps only the summary, and the tips go back into the "?" chip. */
@media (max-width: 1279px) {
  .wiz-stage { grid-template-columns: minmax(0, 1fr); }
  .wiz-stage .wiz-body { max-width: none; }
  .wiz-stage .wiz-aside { position: static; }
  .wiz-stage .wiz-aside-step, .wiz-stage .wiz-aside-tips { display: none; }
  /* both selectors match the :not(.is-wide) rules above, so they win on order */
  .wiz-stage:not(.is-wide) .wiz-tipsq { display: block; }
  .wiz-stage:not(.is-wide) .wiz-summary {
    margin-top: 22px; flex-direction: row; align-items: center; gap: 8px 10px;
  }
}

/* Under 900px the rail collapses to a top progress bar + a step dropdown, and
   the viewport lock is released - a phone with an on-screen keyboard needs the
   page to scroll normally (the calendar page does exactly the same). */
@media (max-width: 900px) {
  .content:has(.main.wiz-page) { height: auto; overflow: visible; }
  .main.wiz-page { overflow: visible; padding: 20px 24px 56px; }
  .wiz { flex-direction: column; gap: 14px; }
  .wiz-rail {
    width: 100%; align-self: auto; overflow: visible;
    padding-bottom: 14px; margin-bottom: 2px; border-bottom: 1px solid var(--line);
  }
  .wiz-steplist { display: none; }
  .wiz-rail-compact { display: flex; }
  .wiz-rail-select {
    width: 100%; padding: 9px 12px; border: 1px solid var(--line-strong);
    border-radius: var(--radius); background: var(--card); color: var(--foreground);
    font: 500 13px/1.3 Inter; cursor: pointer;
  }
  .wiz-stage { overflow: visible; }
  .wiz-nav { padding-bottom: 0; }
}

/* ============================================================
   Hackathon - calendar + scorecard
   ============================================================ */
.cal-head { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.cal-key { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--muted-foreground); }
.cal-key i { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-dow {
  font: 500 11px/1 Inter; text-transform: none; letter-spacing: 0;
  color: var(--muted-foreground); text-align: center; padding: 6px 0;
}
.cal-cell {
  position: relative; min-height: 90px; padding: 6px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
  /* A grid item's automatic minimum size is its CONTENT, so an event title long
     enough (they carry the week's topic now) would push its column - and the whole
     seven-column grid - wider than the page. The bars already ellipsise; this is
     what lets them. */
  min-width: 0;
}
.cal-cell.today { border-color: var(--primary); background: var(--primary-08); }
.cal-cell .d { font: 500 11px/1 Inter; color: var(--muted-foreground); margin-bottom: 5px; }
.cal-cell.today .d { color: var(--primary); }
.cal-cell:hover .cal-add { opacity: 1; }
/* On the FIT calendar the bars fill the cell, so the add button tucks up into
   the day-number row rather than sitting on top of the first event's time. */
.cal-grid.fit .cal-add { top: 1px; right: 2px; width: 17px; height: 17px; }
.cal-add {
  position: absolute; top: 4px; right: 4px; opacity: 0;
  width: 20px; height: 20px; border-radius: 5px; line-height: 1;
  border: 1px solid var(--line-strong); background: var(--card); color: var(--primary);
  transition: opacity .12s;
}

/* A scheduled CALL reads as an appointment card: its category colour runs down
   the left edge with a wash of the same colour behind it, and the title sits
   semibold. Self-paced banners keep NO wash and NO edge (see .cal-band below) -
   colour weight now means "be there", quiet means "in your own time". That one
   contrast is the calendar's whole hierarchy, so don't hand a banner a wash or
   strip a call of its edge without meaning to change what it says. */
.cal-ev {
  display: flex; align-items: center; gap: 4px; width: 100%; text-align: left;
  border: none; border-left: 3px solid var(--line-strong); border-radius: 5px;
  padding: 3px 5px; margin-bottom: 3px;
  font: 500 10.5px/1.35 Inter; color: var(--foreground);
  background: var(--surface-hover); cursor: pointer;
}
.cal-ev.k-coach,
.cal-ev.k-office { border-left-color: var(--blue); background: var(--blue-bg); }
.cal-ev.k-judge  { border-left-color: var(--amber); background: var(--amber-bg); }
.cal-ev .cdot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.cal-ev .t {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0;
  font-weight: 600;
}

/* Titles carry the week's topic now ("Deep Focus Implementation - Lead Gen"), which
   is more than one line of a narrow cell. The CARDED grid has spare height, so let
   them run to two lines instead of ellipsising into uselessness. The FIT grid keeps
   one line on purpose: its rows are a fixed slice of the viewport, and fitCalCells()
   already trades overflow for a "+N more" chip. */
.cal-grid:not(.fit) .cal-ev,
.cal-grid:not(.fit) .cal-band { align-items: flex-start; }
/* Both bar and banner WRAP here, because both can carry a time now: without it
   the time's `flex: 0 0 100%` claimed the whole row and squeezed the sparring gym's
   title out of existence on the admin grid. */
.cal-grid:not(.fit) .cal-ev,
.cal-grid:not(.fit) .cal-band { flex-wrap: wrap; }
.cal-grid:not(.fit) .cal-ev .cdot,
.cal-grid:not(.fit) .cal-band .cdot { margin-top: 4px; }
/* The time drops to its own line so the title gets the cell's full width, indented
   to sit under the title rather than under the dot. */
.cal-grid:not(.fit) .cal-ev .tm,
.cal-grid:not(.fit) .cal-band .tm { flex: 0 0 100%; margin: 1px 0 0 11px; }
.cal-grid:not(.fit) .cal-ev .t,
.cal-grid:not(.fit) .cal-band .t {
  white-space: normal; overflow: hidden; text-overflow: clip;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
/* The time on a bar - and on the sparring gym's banner, the one banner that has one. */
.cal-ev .tm,
.cal-band .tm { font-weight: 600; opacity: .7; font-size: 9.5px; white-space: nowrap; flex: none; }
.cal-ev .rec { flex: none; width: 11px; height: 11px; opacity: .55; }

/* ============================================================
   Calendar - FIT layout (the participant #calendar page).
   The whole page is the grid: week rows that divide ALL remaining viewport
   height equally. The month nav, date range and legend live in the top bar
   (see .tb-monthnav / .tb-range / .cal-legend-btn), so there is no in-page
   header row here - no card, no page scroll.
   ============================================================ */
/* The page owns the viewport exactly, so the SHELL must stop adding its own.
   .content's top margin (the top bar's height) COLLAPSES out through .app-body,
   .app-shell and body - none of them has padding, a border or a BFC - so the
   shell box already starts one bar-height down the page. Leaving their
   min-height: 100vh in place would then stack a full viewport UNDER that
   collapsed margin and the page would scroll by exactly the bar's height. Every
   other page is taller than the viewport anyway, which is why it only ever shows
   up here. min-height: 0 on .content also stops the base vh minimum from beating
   the dvh height where browser chrome makes 100vh > 100dvh. */
html:has(.main.cal-view), body:has(.main.cal-view) { overflow: hidden; }
body:has(.main.cal-view), .app-shell:has(.main.cal-view) { min-height: 0; }
.content:has(.main.cal-view) {
  height: calc(100dvh - var(--topbar-h)); min-height: 0; overflow: hidden;
}
.main.cal-view {
  flex: 1 1 auto; min-height: 0; max-width: none; margin: 0;
  padding: 16px 20px; overflow: hidden;
  display: flex; flex-direction: column;
}
.cal-fit { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

/* collapsed grid: 1px lines shown through the gaps, one outer border, no radius per cell.
   First row auto (weekday labels), then one 1fr row per week so they share the height. */
.cal-grid.fit {
  flex: 1 1 auto; min-height: 0; gap: 1px;
  grid-template-rows: auto repeat(var(--weeks, 5), minmax(0, 1fr));
  background: var(--line); border: 1px solid var(--line);
  border-radius: var(--card-radius); overflow: hidden;
}
.cal-grid.fit .cal-dow { background: var(--card-bg); padding: 6px 0 5px; }
.cal-grid.fit .cal-cell {
  min-height: 0; border: none; border-radius: 0; padding: 3px 4px;
  background: var(--card-bg); display: flex; flex-direction: column; overflow: hidden;
}
.cal-grid.fit .cal-cell.today { background: var(--primary-08); }
.cal-grid.fit .cal-cell .d {
  font: 500 12px/1 Inter; color: var(--muted-foreground); margin: 1px 0 3px 1px; flex: none;
}
.cal-grid.fit .cal-cell.today .d {
  align-self: flex-start; display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 4px; border-radius: 999px;
  background: var(--primary); color: var(--primary-fg); font-weight: 600; margin-bottom: 3px;
}
/* Every event bar is ONE SLOT of the cell's event area, whatever that day holds,
   and they stack from the top. A slot is a THIRD by default - the sprint's own
   Wednesday rhythm, so a lone Monday topic takes the top third rather than the
   whole box - and the grid raises --cal-slots when a month on screen has a
   busier day than that (the all-cohorts view). fitCalCells() still trims what
   is past the count into a "+N more" chip. The 2px stack gap lives on the
   column's `gap` rather than a per-bar margin, so the bars land flush. */
.cal-grid.fit .cal-evs {
  flex: 1 1 auto; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column; gap: 2px;
  /* Every bar is exactly a THIRD of the day's event area - three of them and
     the two 2px gaps fill it exactly. A day with one event therefore gets one
     third-height bar at the top, not one bar stretched over the whole cell:
     Wednesday's three-up rhythm becomes the rhythm of every day, so the month
     reads as a grid of bars instead of blocks of wildly different heights. */
  --cal-slot: calc((100% - (var(--cal-slots, 3) - 1) * 2px) / var(--cal-slots, 3));
}
.cal-grid.fit .cal-ev {
  gap: 5px; flex: 0 0 var(--cal-slot); min-height: 18px; padding: 0 5px; margin-bottom: 0; border-radius: 4px;
  font-size: 11px; line-height: 18px;
  /* no background here - the per-kind tints on .cal-ev.k-* must win in this grid too */
}
.cal-grid.fit .cal-ev .cdot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
/* Titles use the height their bar actually has instead of ellipsising into one
   line: fitCalCells() sets --cal-lines to how many 15px lines the bar's final
   flexed height fits, so a lone event shows its whole title and a crowded day's
   compact bars stay at one line. Clamp still ellipsises what truly cannot fit. */
.cal-grid.fit .cal-ev .t,
.cal-grid.fit .cal-band .t {
  white-space: normal; text-overflow: clip; line-height: 15px;
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--cal-lines, 1); overflow: hidden;
}
.cal-grid.fit .cal-more {
  font: 500 10.5px/1 Inter; color: var(--muted-foreground);
  padding: 2px 5px; cursor: default;
  flex: none;   /* the chip keeps its own height; only the bars share the box */
}

/* The six categories - identity colour, drawn from the app palette.
   topic = teal, coach = blue, implementation = purple, judge = amber, deadline = red.
   Open Office Hours is coaching too, so it keeps the coach blue and separates
   itself by being HOLLOW - same family, still one glance apart in the legend.
   The ring is drawn inside, so the dot keeps the size every other dot has. */
i.k-topic          { background: var(--primary); }
i.k-coach          { background: var(--blue); }
i.k-office         { background: transparent; box-shadow: inset 0 0 0 2px var(--blue); }
i.k-implementation { background: var(--purple); }
i.k-judge          { background: var(--amber); }
/* The sparring gym is feedback like the judge call, so it keeps the judge amber and
   separates itself by being HOLLOW - the same family trick office plays on coach. */
i.k-peer           { background: transparent; box-shadow: inset 0 0 0 2px var(--amber); }
i.k-deadline       { background: var(--red); }

/* All-day banners (self-paced categories): QUIET on purpose - dot and muted
   text on the bare cell, no wash, no edge. The coloured washes they used to
   carry made the self-paced filler louder than the actual calls, which is
   exactly backwards (Matt, Aug 2026: "which of these are real calls?"). The
   transparent left border keeps their text column aligned with the calls'. */
.cal-band {
  display: flex; align-items: center; gap: 5px; width: 100%; text-align: left;
  border: none; border-left: 3px solid transparent; border-radius: 4px;
  padding: 1px 6px; margin-bottom: 2px;
  font: 500 10.5px/1.6 Inter; color: var(--muted-foreground); cursor: pointer; overflow: hidden;
  background: transparent;   /* it's a <button> - without this the UA paints it grey */
}
.cal-band .cdot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.cal-band .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
/* The two exceptions, each on purpose: the sparring gym is TIMED - you and your
   group actually meet - so it dresses like an appointment (amber family, hollow
   dot). A deadline is a warning, and red stays a status colour. */
.cal-band.k-peer {
  border-left-color: var(--amber); background: var(--amber-bg); color: var(--foreground);
}
.cal-band.k-peer .t { font-weight: 600; }
.cal-band.k-deadline { background: var(--red-bg); color: var(--red); }
.cal-grid.fit .cal-band { flex: 0 0 var(--cal-slot); min-height: 16px; line-height: 16px; padding: 0 6px; margin-bottom: 0; }

/* "#8" - which cohort a bar belongs to, in the all-cohorts view only. */
.cal-tag {
  flex: none; font: 700 9px/1 Inter; letter-spacing: .02em; padding: 2px 4px;
  border-radius: 3px; background: var(--surface-hover); color: var(--muted-foreground);
}
.cal-grid.fit .cal-tag { padding: 1px 3px; font-size: 8.5px; }

/* The all-cohorts view is the SAME calendar, not a smaller one: --cal-slots
   (set on the grid) simply counts a busy day's bars instead of always three, so
   the bars keep their size, type and spacing and only lose a little height. */

/* One hover for EVERY event bar - timed calls and all-day banners alike.
   An inset shadow, not a background, because the fills above are `background`
   shorthands that would blow away a hover background-image; the veil sits on top
   of the fill and under the text, so the bar keeps its category colour and just
   gets a shade deeper. */
.cal-ev, .cal-band { transition: box-shadow .12s ease; }
.cal-ev:hover, .cal-band:hover {
  box-shadow: inset 0 0 0 999px var(--cal-hover-veil);
}

/* ---------- Sprint weeks: the WEEK's identity, under the events ----------
   A cohort week wears the hackathon course's own track colour (positioning amber,
   lead gen orange, sales purple, revops blue, CS green, the judging week gray) so
   the calendar and the course agree at a glance. The classes and the --track-*
   values come from track-colors.js - this is a consumer of that palette, never a
   second copy of it.

   Painted as a background IMAGE rather than a background colour: --track-tint is a
   translucent rgba in dark mode, so it has to sit ON TOP of the cell's own surface,
   not replace it. It also keeps the wash clear of the `background` shorthands the
   base and fit cells already use, so neither rule has to know about the other. */
.cal-cell.cal-wk,
.cal-grid.fit .cal-cell.cal-wk {
  background-image: linear-gradient(var(--track-tint), var(--track-tint));
}
/* Today outranks its week - the accent is a stronger signal than the week wash. */
.cal-cell.cal-wk.today,
.cal-grid.fit .cal-cell.cal-wk.today { background-image: none; }

/* A day from the month either side is a DAY: same fill, same edges, same weight.
   It carries no styling of its own at all - nothing is greyed out, because a
   cohort whose first day is the 31st of the month before had the day it opens
   drawn as an afterthought. */

/* The day number and, on a Monday, the week it opens. The spacing the day number
   used to carry moves out to the row, so the two sit on one line. */
.cal-dayhead { display: flex; align-items: center; gap: 6px; min-width: 0; flex: none; }
/* The weekday initials ride along in every day head but only SHOW on the phone
   agenda, where there is no weekday header row above the column. */
.cal-wd { display: none; }
.cal-cell .cal-dayhead { margin-bottom: 5px; }
.cal-cell .cal-dayhead .d { margin: 0; flex: none; }
.cal-grid.fit .cal-cell .cal-dayhead { margin: 1px 0 3px 1px; }
.cal-grid.fit .cal-cell .cal-dayhead .d { margin: 0; align-self: auto; }
/* The week name gives up its space before the day number does, and truncates
   rather than widening the column. */
.cal-wk-tag {
  min-width: 0; flex: 0 1 auto;
  font: 600 9.5px/1 Inter; letter-spacing: .02em; text-transform: uppercase;
  color: var(--track-label); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* "Hackathon starts Sep 1 ->" - the jump hint next to the month nav, shown when
   the current month is empty and the cohort is still ahead. */
.cal-hint {
  display: inline-flex; align-items: center; gap: 3px; white-space: nowrap;
  font: 500 12px/1 Inter; color: var(--primary);
  background: var(--primary-08); border: 1px solid var(--primary-30);
  padding: 5px 10px; border-radius: 999px; cursor: pointer;
}
.cal-hint:hover { background: var(--primary-12); }
.cal-hint .ico { width: 13px; height: 13px; }

/* Event details popover - opens anchored to the clicked event (uses .pop). */
.cal-pop { outline: none; display: flex; flex-direction: column; gap: 7px; }
/* No category pill above the title - the title already names the session, and
   the card was drowning in restated facts (Matt's feedback). */
.cal-pop-title { font: 600 14px/1.3 Inter; color: var(--foreground); }
.cal-pop-when { font: 500 12px/1.4 Inter; color: var(--muted-foreground); }
.cal-pop-desc { font: 400 12.5px/1.5 Inter; color: var(--foreground); white-space: pre-wrap; overflow-wrap: break-word; }
/* A pasted URL reads as a named pill (Google Docs, Loom...), not a wall of hash. */
.desc-chip {
  display: inline-flex; align-items: center; gap: 5px; max-width: 100%;
  padding: 2px 9px 2px 7px; margin: 1px 0; border: 1px solid var(--line);
  border-radius: 999px; background: var(--surface);
  font: 500 11.5px/1.6 Inter; color: var(--foreground); text-decoration: none;
  vertical-align: middle; white-space: nowrap;
}
.desc-chip:hover { background: var(--surface-hover); border-color: var(--line-strong); }
.desc-chip .ico { width: 12px; height: 12px; flex: none; color: var(--muted-foreground); }
.desc-chip .t { overflow: hidden; text-overflow: ellipsis; }
.cal-pop-actions { display: flex; gap: 6px; margin-top: 3px; }
/* The primary action (Join call / Watch recording) spans the card - it is THE
   thing to press. Edit keeps its own size beside it. */
.cal-pop-actions .btn-green { flex: 1 1 auto; justify-content: center; }
.cal-pop-actions:empty { display: none; }
/* Peer groups - the body of a Peer Idea Sparring Gym event's popover. Groups are
   rows of member chips; admins also get the Shuffle button and a per-person
   group selector next to each chip. */
.cal-pop-peer {
  margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 8px;
  font: 500 12px/1.4 Inter; color: var(--muted-foreground);
}
.peer-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font: 600 11px/1 Inter; text-transform: uppercase; letter-spacing: .03em;
  color: var(--muted-foreground);
}
.peer-grp, .peer-unassigned { display: flex; flex-direction: column; gap: 5px; }
.peer-grp-n { font: 600 11px/1 Inter; color: var(--muted-foreground); }
.peer-unassigned { padding-top: 6px; border-top: 1px dashed var(--line); }
/* ONE SEAT PER LINE. They used to wrap as inline chips, and a long name plus a
   company plus the mover was wider than the 230px popover, so names ellipsised
   into nothing and the mover was clipped off the right edge (Matt, Aug 2026).
   A column cannot overflow sideways. */
.peer-grp-m { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.peer-seat { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; }
.peer-seat .hostchip { min-width: 0; }
/* "This week you're in Mixed Group 1" - where a visitor actually sparring with
   the other cohort finds that out, since their own cohort's list no longer
   holds them. */
.peer-you {
  font: 500 12px/1.45 Inter; color: var(--foreground);
  background: var(--amber-bg); border-left: 3px solid var(--amber);
  border-radius: 4px; padding: 6px 9px;
}
.peer-you b { font-weight: 600; }
.hc-team { font-style: normal; color: var(--muted-foreground); }
.peer-empty { font: 500 12px/1.5 Inter; color: var(--muted-foreground); }

/* The popover carrying groups is WIDER than a plain chip popover - it holds
   names, a cohort tag and a company each. Still read-only, so it never needs
   room for a control; the admin's mover lives in the manager modal. */
.pop:has(.cal-pop-peer) { width: 320px; }
.pop:has(.cal-pop-peer) .hc-name { max-width: 210px; }

/* ---------- the admin's group manager (a modal, not the popover) ---------- */
.peer-note {
  margin: 0 0 14px; font: 400 12.5px/1.55 Inter; color: var(--muted-foreground);
}
.peer-mgr { display: flex; flex-direction: column; gap: 18px; max-height: 62vh; overflow-y: auto; }
.peer-cohort { display: flex; flex-direction: column; gap: 10px; }
.peer-cohort-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-bottom: 7px; border-bottom: 1px solid var(--line);
}
.peer-cohort-n { font: 600 13px/1.3 Inter; color: var(--foreground); }
.peer-cohort-n i {
  font-style: normal; font-weight: 500; font-size: 11.5px; color: var(--muted-foreground);
  margin-left: 6px;
}
.peer-mgr-grp { display: flex; flex-direction: column; gap: 5px; }
/* One person per row: chip, cohort tag, then the mover pinned to the right so
   every dropdown lines up down the modal. */
.peer-row { display: flex; align-items: center; gap: 7px; }
.peer-row-gap { flex: 1 1 auto; min-width: 8px; }
/* The chip's 160px name cap is sized for a popover. There is a 900px modal
   around it here, so let the names have the room - a truncated "Alexandra
   Featherstoneh..." next to acres of white space helps nobody. */
.peer-row .hostchip { min-width: 0; }
.peer-row .hc-name { max-width: 440px; }
.peer-move {
  width: auto; min-width: 150px; max-width: 240px; height: 26px; padding: 0 24px 0 10px;
  font: 500 11.5px/1 Inter; border-radius: 999px; flex: none;
}

/* Admin-only inline recording editor - paste a Fathom link straight on the call. */
.cal-pop-rec { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
.cal-pop-rec .fl { display: inline-flex; align-items: center; gap: 4px; margin-bottom: 4px; }
.cal-pop-rec .fl .ico { width: 12px; height: 12px; }

/* ---------- hosts: the chip, the hover card, the member page ---------- */

/* "Hosted by" row in the event popover, and the chips in the admin picker. */
.cal-pop-hosts { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cal-pop-hosts .hosts-label { font: 600 11px/1 Inter; text-transform: uppercase;
  letter-spacing: .03em; color: var(--muted-foreground); margin-right: 2px; }
.hostchip {
  display: inline-flex; align-items: center; gap: 6px;
  font: 500 12px/1 Inter; color: var(--foreground);
  background: var(--surface); border: 1px solid var(--line);
  padding: 3px 10px 3px 3px; border-radius: 999px; cursor: pointer;
}
.hostchip:hover { background: var(--surface-hover); border-color: var(--line-strong); }
.hostchip.static { cursor: default; padding-right: 4px; }
.hc-photo {
  width: 22px; height: 22px; border-radius: 50%; overflow: hidden; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary-12); color: var(--primary);
  font: 600 11px/1 Inter;
}
.hc-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hc-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; }
.hostchip .hc-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; border: 0; cursor: pointer;
  background: transparent; color: var(--muted-foreground); padding: 0;
}
.hostchip .hc-x:hover { background: var(--surface-hover); color: var(--foreground); }
.hostchip .hc-x .ico { width: 11px; height: 11px; }

/* The hover card - floats over everything, including the event popover (.pop
   is z-index 300, so this must sit above that). */
.member-hover {
  position: fixed; z-index: 310; width: 280px;
  background: var(--card-bg); border: 1px solid var(--line);
  border-radius: var(--card-radius); box-shadow: var(--shadow-pop);
  padding: 12px; display: flex; flex-direction: column; gap: 8px;
  cursor: pointer;   /* the whole card opens the full profile, as its hint says */
}
.member-hover .mh-head { display: flex; align-items: center; gap: 10px; }
.member-hover .mh-photo {
  width: 44px; height: 44px; border-radius: 50%; overflow: hidden; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary-12); color: var(--primary); font: 600 17px/1 Inter;
}
.member-hover .mh-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.member-hover .mh-id { flex: 1; min-width: 0; }
.member-hover .mh-name { font: 600 13.5px/1.3 Inter; color: var(--foreground); }
.member-hover .mh-role { font: 500 11.5px/1.4 Inter; color: var(--muted-foreground); }
.member-hover .mh-li {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%; flex: 0 0 auto;
  color: var(--muted-foreground); border: 1px solid var(--line);
}
.member-hover .mh-li:hover { color: var(--foreground); background: var(--surface-hover); }
.member-hover .mh-li .ico { width: 14px; height: 14px; }
.member-hover .mh-bio { font: 400 12px/1.5 Inter; color: var(--foreground); }

/* The member page (#member/12). */
.member-page .member-head { display: flex; align-items: center; gap: 18px; margin-bottom: 16px; }
.member-page .member-photo { flex: 0 0 auto; }
.member-page .member-id { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; min-width: 0; }
.member-page .member-role {
  font: 500 12.5px/1.4 Inter; color: var(--muted-foreground);
  display: flex; align-items: center; gap: 5px;
}
.member-page .member-role .ico-sm { flex-shrink: 0; opacity: .6; }
.member-page .member-li .ico { width: 14px; height: 14px; }
.member-page .member-bio { border-top: 1px solid var(--line); padding-top: 16px; }

/* The admin event panel's host picker: chips above, matches below the input. */
.hostpick { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.hostpick:empty { display: none; }
.hostpick-list {
  margin-top: 6px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card-bg); overflow: hidden;
}
.hostpick-row {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 7px 10px; border: 0; background: transparent; cursor: pointer;
  font: 500 12.5px/1 Inter; color: var(--foreground);
}
.hostpick-row:hover { background: var(--surface-hover); }
.hostpick-row + .hostpick-row { border-top: 1px solid var(--line); }
/* "Create ... as a new person" - the row under "Nobody matches." */
.hostpick-new .hc-photo .ico { width: 12px; height: 12px; }
.hostpick-new .hc-name { max-width: none; }

/* Add person - the judge tier ticks EVERY cohort they judge, so checkboxes. */
.ap-cks { display: flex; flex-direction: column; gap: 7px; padding: 2px 0; }
.ap-ck {
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  font: 500 13px/1.3 Inter; color: var(--foreground);
}
.ap-ck input { width: 15px; height: 15px; margin: 0; cursor: pointer; accent-color: var(--primary); }

/* Admin event editor - a right-hand side panel (not a modal), so the calendar
   stays visible while you edit a day. */
.cal-panel-overlay { position: fixed; inset: 0; z-index: 120; background: rgba(0, 0, 0, .28); }
[data-theme="light"] .cal-panel-overlay { background: rgba(16, 24, 24, .18); }
.cal-panel {
  position: fixed; top: 0; right: 0; height: 100vh; width: min(420px, 94vw);
  background: var(--background); border-left: 1px solid var(--line);
  box-shadow: -12px 0 40px rgba(0, 0, 0, .22);
  transform: translateX(100%); transition: transform .28s cubic-bezier(.32, .72, 0, 1);
  display: flex; flex-direction: column;
}
[data-theme="light"] .cal-panel { box-shadow: -12px 0 40px rgba(16, 24, 24, .10); }
.cal-panel.show { transform: translateX(0); }
.cal-panel-head { display: flex; align-items: center; gap: 10px; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.cal-panel-head h3 { margin: 0; font: 600 16px/1.2 Inter; flex: 1; }
.cal-panel-body { flex: 1 1 auto; overflow-y: auto; padding: 18px 20px; display: flex; flex-direction: column; }
.cal-panel-body > * { flex: none; }
/* The description is the panel's last field and the one people actually write
   in, so it takes every pixel left down to the footer instead of an 80px slot. */
.cal-panel-body .field.ep-desc { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; margin-bottom: 0; }
.cal-panel-body .field.ep-desc textarea { flex: 1 1 auto; min-height: 120px; resize: none; }
.cal-panel-foot { display: flex; align-items: center; gap: 8px; padding: 14px 20px; border-top: 1px solid var(--line); }
/* The "Pacific Time" tag on the time-field labels - times are always entered in PT. */
.fl-tz { font: 500 10.5px/1 Inter; text-transform: none; letter-spacing: 0; color: var(--primary); margin-left: 6px; }

/* --- leaderboard --- */
.lb { display: grid; gap: 6px; }
.lb-row {
  display: flex; align-items: center; gap: 12px; padding: 9px 12px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
}
.lb-row.me { border-color: var(--primary-30); background: var(--primary-08); }
.lb-row .rank {
  width: 26px; text-align: center; font: 600 13px/1 Inter; color: var(--muted-foreground);
}
.lb-row .rank.gold { font-size: 16px; }
.lb-row .who { font-weight: 500; min-width: 120px; }
.lb-row .bar { flex: 1; height: 6px; border-radius: 4px; background: var(--bar-track); overflow: hidden; }
.lb-row .bar i { display: block; height: 100%; background: var(--primary-30); border-radius: 4px; }
.lb-row.me .bar i { background: var(--primary); }
.lb-row .pts { font: 600 13px/1 Inter; min-width: 62px; text-align: right; }

/* --- scorecard table --- */
table.data.score td.crit { padding-left: 22px; font-size: 12.5px; }
/* A criterion with fine print: dotted underline invites the hover, the full
   explanation pops instantly in the same dark tooltip the "?" hints use -
   a native title attribute would sit on the browser's ~1s hover delay. */
table.data.score .crit-hint {
  cursor: help; position: relative;
  text-decoration: underline dotted var(--line-strong);
  text-underline-offset: 3px;
}
table.data.score .crit-hint:hover::after,
table.data.score .crit-hint:focus-visible::after {
  content: attr(data-tip); position: absolute; left: 0; top: calc(100% + 7px);
  width: max-content; max-width: 340px;
  background: var(--foreground); color: var(--background);
  padding: 8px 11px; border-radius: 6px; font: 400 12px/1.55 Inter; text-align: left;
  z-index: 60; box-shadow: var(--shadow-pop); white-space: normal;
}
table.data.score tr.cat td {
  background: var(--surface); font-size: 12px; vertical-align: middle;
  text-transform: none; letter-spacing: 0; color: var(--foreground); font-weight: 600;
}
/* Collapsible category header - the chevron + name + always-on subtotals. */
table.data.score tr.cat { cursor: pointer; }
table.data.score tr.cat:hover td { background: var(--surface-hover); }
table.data.score tr.cat:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
table.data.score .cat-head { display: inline-flex; align-items: center; gap: 6px; }
table.data.score .cat-head b { font-size: 14px; }
table.data.score .cat-chev { color: var(--muted-foreground); transition: transform 150ms ease; }
table.data.score tr.cat.open .cat-chev { transform: rotate(90deg); }
table.data.score tr.cat .sub { font-size: 13px; }
table.data.score tr.cat .sub .muted { font-weight: 500; margin-left: 1px; }
/* Optional per-category progress - the viewer's own earned/max, in brand teal. */
table.data.score .cat-prog {
  display: block; height: 3px; max-width: 160px; margin-top: 5px;
  border-radius: 2px; background: var(--bar-track); overflow: hidden;
}
table.data.score .cat-prog i { display: block; height: 100%; background: var(--primary); border-radius: 2px; }
table.data.score tr.grand td {
  border-top: 2px solid var(--primary); background: var(--primary-08);
  font-size: 14px; color: var(--primary);
}
table.data.score th.me-col, table.data.score td.me-col { background: var(--primary-08); }
table.data.score th.me-col { color: var(--primary); }
table.data.score th.me-col .you { font-weight: 500; text-transform: none; letter-spacing: 0; }
.sc-in {
  width: 62px; text-align: center; padding: 5px; font-weight: 600;
  -moz-appearance: textfield;
}
.sc-in::-webkit-outer-spin-button, .sc-in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sc-in.bad { border-color: var(--red); }

@media (max-width: 760px) {
  .cal-cell { min-height: 64px; }
  .cal-ev .t { display: none; }
  .secs { overflow-x: auto; grid-auto-columns: minmax(150px, 1fr); padding-bottom: 6px; }

  /* A month squeezed into seven 50px columns is unreadable, so the phone gets
     an AGENDA instead: the same month as a vertical list of only the days that
     HAVE something - weekday + date on the left, that day's events stacked
     full-width beside it. Empty days and blank neighbouring-month days simply
     aren't rows. The shell minimums the desktop fit rules dropped come back
     first, so the page can grow past the viewport and scroll again. */
  html:has(.main.cal-view), body:has(.main.cal-view) { overflow: visible; }
  body:has(.main.cal-view) { min-height: 100vh; }
  .app-shell:has(.main.cal-view) { min-height: 100vh; }
  .content:has(.main.cal-view) { height: auto; min-height: calc(100vh - var(--topbar-h)); overflow: visible; }
  .main.cal-view { overflow: visible; padding: 12px 12px 40px; }
  .cal-fit { min-height: auto; }

  .cal-grid.fit { display: flex; flex-direction: column; flex: 0 0 auto; gap: 0; }
  .cal-grid.fit .cal-dow { display: none; }
  .cal-grid.fit .cal-cell { display: none; }             /* only days with events are rows */
  .cal-grid.fit .cal-cell:has(.cal-ev, .cal-band) {
    display: flex; flex-direction: row; align-items: flex-start; gap: 12px;
    min-height: 0; padding: 12px 10px; border-bottom: 1px solid var(--line);
  }
  /* Weekday over date, a compact left column; the Monday week-name tag has no
     room here and the week wash on the row already carries that signal. */
  .cal-grid.fit .cal-cell .cal-dayhead {
    flex: 0 0 40px; flex-direction: column; align-items: center; gap: 2px; margin: 0;
  }
  .cal-grid.fit .cal-wd {
    display: block; font: 600 9.5px/1 Inter; text-transform: uppercase;
    letter-spacing: .04em; color: var(--muted-foreground);
  }
  .cal-grid.fit .cal-cell .cal-dayhead .d { font: 600 15px/1.2 Inter; }
  .cal-grid.fit .cal-wk-tag { display: none; }

  /* Events: natural height, comfortable touch targets, full titles - no fixed
     thirds, no trimming, no "+N more" (fitCalCells skips auto-height rows). */
  .cal-grid.fit .cal-evs { flex: 1 1 auto; min-width: 0; overflow: visible; gap: 6px; }
  .cal-grid.fit .cal-ev,
  .cal-grid.fit .cal-band {
    flex: 0 0 auto; min-height: 0; height: auto;
    padding: 8px 10px; font-size: 12px; line-height: 1.35;
  }
  .cal-grid.fit .cal-ev .t,
  .cal-grid.fit .cal-band .t {
    display: block; white-space: normal; -webkit-line-clamp: unset; line-height: 1.35;
  }
  .cal-grid.fit .cal-more { display: none; }
}

/* ============================================================
   Rich-text editor
   ============================================================ */
.rte {
  position: relative;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--input-bg); overflow: hidden;
}
.rte:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }

.rte-bar {
  display: flex; align-items: center; gap: 3px; flex-wrap: wrap;
  padding: 6px 8px; border-bottom: 1px solid var(--line);
  background: var(--surface); position: sticky; top: 0; z-index: 2;
}
.rte-sep { width: 1px; height: 18px; background: var(--line); margin: 0 4px; }
.rte-btn {
  border: 1px solid transparent; background: transparent; color: var(--foreground);
  border-radius: var(--radius); padding: 5px 9px; font: 500 12px/1.3 Inter;
  transition: background .1s, border-color .1s;
}
.rte-btn:hover { background: var(--surface-hover); border-color: var(--line); }
.rte-btn.on { background: var(--primary); color: var(--primary-fg); }
.rte-html { font-family: ui-monospace, monospace; font-size: 11px; }

.rte-body {
  min-height: 240px; max-height: 60vh; overflow-y: auto;
  padding: 15px 16px; font-size: 14px; line-height: 1.7; outline: none;
}
.rte-body:empty::before {
  content: 'Write the lesson. Paste a Loom link to embed it, or drag in an image.';
  color: var(--muted-2);
}
.rte-body h3 { font: 600 15px/1.4 Inter; text-transform: none; letter-spacing: 0;
  color: var(--foreground); margin: 20px 0 8px; }
.rte-body p { margin: 0 0 10px; }
.rte-body a { color: var(--primary); }
.rte-body img { max-width: 100%; border-radius: var(--radius); display: block; margin: 10px auto; }
.rte-body ul, .rte-body ol { padding-left: 22px; margin: 0 0 10px; }
.rte-uploading { color: var(--muted-foreground); font-style: italic; }

.rte-drop {
  position: absolute; inset: 40px 0 0; display: none; place-items: center;
  background: var(--primary-08); border: 2px dashed var(--primary);
  font: 600 13px Inter; color: var(--primary); pointer-events: none;
}
.rte.dragging .rte-drop { display: grid; }

/* ---------- video embeds - editor AND reader ---------- */
.embed, .mod-body .embed, .rte-body .embed {
  position: relative; padding-bottom: 56.25%; height: 0;
  border-radius: var(--card-radius); overflow: hidden; margin: 12px 0;
  border: 1px solid var(--line); background: #000;
}
.embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.rte-body .embed::after { content: ''; position: absolute; inset: 0; }
.rte-body .embed { pointer-events: none; }

/* ============================================================
   1-on-1 Consulting - calls documentation.
   ============================================================ */
/* The call log reuses the Admin `.dtable` (muted 12px header, 46px rows, 1px
   separators, --surface hover). Fixed layout so the two write-up columns can
   ellipsis-truncate; a date column and a chevron column are pinned to width. */
.calls-table { table-layout: fixed; }
.calls-table col.c-date { width: 200px; }
.calls-table col.c-chev { width: 44px; }

.call-row { cursor: pointer; }
.call-date-cell { font-weight: 600; white-space: nowrap; }
.call-trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.call-chev {
  display: inline-flex; color: var(--muted-foreground);
  transition: transform .15s ease, color .15s ease;
}
.call-chev .ico { width: 16px; height: 16px; }
.call-row.open .call-chev { transform: rotate(90deg); color: var(--primary); }
/* An open row and its detail panel share one subtle fill and drop the hairline
   between them, so the pair reads as a single expanded unit. */
.call-row.open td { background: var(--surface); border-bottom-color: transparent; }

/* The inline detail row: the cell carries no padding - the panel owns the
   indent and the #fafafa (light) / --surface fill. */
.call-detail > td { padding: 0; height: auto; }
.call-detail-panel { padding: 14px 16px 18px 34px; background: var(--surface); }

.call-sec { margin-bottom: 14px; }
.call-sec:last-child { margin-bottom: 0; }
.call-sec h4 {
  font: 600 12px/1.4 Inter; text-transform: none; letter-spacing: 0;
  color: var(--muted-foreground); margin: 0 0 6px;
}

/* The empty recording slot (admin only). A call is usually written up before
   the recording has finished processing, so the slot is a waiting drop target,
   not a real action - dashed and muted until the consultant has the link. */
.rec-slot {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border: 1px dashed var(--line); border-radius: 9999px;
  background: transparent; color: var(--muted-foreground);
  font: 500 12.5px/1 Inter;
  transition: border-color .12s, color .12s;
}
.rec-slot:hover { border-color: var(--primary); color: var(--primary); }
.rec-slot .ico { width: 14px; height: 14px; }

/* What the slot becomes once clicked: paste, save, done - no modal for one URL. */
.rec-paste { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rec-paste input { max-width: 380px; }

/* Edit / Delete for the call, at the foot of an open row. */
.call-admin-row {
  display: flex; gap: 8px; margin-top: 16px;
  padding-top: 14px; border-top: 1px solid var(--line);
}

.terms-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0 20px; }

/* ---------- List: the section boxes ---------- */
.secs {
  display: grid; gap: 10px;
  grid-auto-flow: column;
  /* Never let a tile collapse below a legible width: at ~40px the title wraps to
     one character per line. Tiles hold >=130px and the strip scrolls instead. */
  grid-auto-columns: minmax(130px, 1fr);
  overflow-x: auto;
}
.sec-box {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  text-align: left; padding: 12px 12px 12px 14px;
  border: 1px solid var(--line); border-radius: var(--card-radius);
  background: var(--card); color: var(--foreground);
  transition: border-color .12s, background .12s;
  min-width: 120px;
}
.sec-box:hover { border-color: var(--line-strong); background: var(--surface); }
.sec-box.on { border-color: var(--primary-30); background: var(--primary-08); }
.sec-box.done { border-color: var(--line-strong); }
.sec-box .t { font: 600 12.5px/1.3 Inter; overflow-wrap: anywhere; }
.sec-box .n { font: 500 11.5px/1 Inter; color: var(--muted-foreground); }
.sec-box.on .n { color: var(--primary); }
.sec-box .progress-bar-wrap { width: 100%; height: 5px; margin-top: 2px; }
.sec-box.done .t::after { content: ' ✓'; color: var(--primary); }

/* ---------- Chapter page ---------- */
.chapter-title { font: 600 20px/1.3 Inter; letter-spacing: 0; margin: 0 0 14px; }

.mod-body h1, .mod-body h2 {
  font: 600 16px/1.4 Inter; margin: 22px 0 8px; color: var(--foreground);
}
.mod-body h1:first-child, .mod-body h2:first-child { margin-top: 0; }

/* impersonation "way back" - quiet */
.unmask {
  width: 26px; height: 26px; border-radius: var(--radius); padding: 0; flex: none;
  border: 1px dashed var(--line-strong); background: transparent;
  color: var(--muted-2); opacity: .5;
  display: inline-flex; align-items: center; justify-content: center;
  transition: opacity .12s, color .12s, border-color .12s;
}
.unmask:hover { opacity: 1; color: var(--primary); border-color: var(--primary); }
/* "Viewing as <person>" - the loud twin of .unmask, now that any member can be
   viewed. Accent at low opacity, pill, fixed bottom-left so it rides over every
   view; the whole pill is the way back. */
.viewas-pill {
  position: fixed; left: 16px; bottom: 16px; z-index: 9000;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 14px; border-radius: 9999px; cursor: pointer;
  background: var(--primary-12); color: var(--primary);
  border: 1px solid var(--primary-30);
  font: 600 12px/1.4 Inter, system-ui, sans-serif;
  box-shadow: var(--shadow);
}
.viewas-pill:hover { background: var(--primary-20); }
.viewas-pill .ico { width: 14px; height: 14px; }
.viewas-pill .sep { opacity: .5; }
.unmask .ico { width: 13px; height: 13px; }

/* ---------- lessons vs action items ---------- */
.lrow.lesson { cursor: pointer; }
.lrow.lesson .go { margin-left: 4px; display: inline-flex; color: var(--muted-foreground); opacity: .6; }
.lrow.lesson:hover .go { opacity: 1; color: var(--primary); }
.lrow.lesson:hover { border-color: var(--line-strong); }

.checklist li .txt { flex: 1; }
.checklist li .box { border-radius: 5px; }
.checklist li {
  padding: 9px 12px;
  font-size: 12.5px;
  background: transparent;
  border-color: var(--td-border);
}
.checklist li:hover { background: var(--surface); border-color: var(--line-strong); }

/* ============================================================
   Section naming - kicker + heading.
   ============================================================ */
.sec-box .k, .sec-kick, .kcol h4 .kk {
  display: block;
  font: 600 9.5px/1.4 Inter; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted-2);
}
.sec-box.on .k, .sec-box.on .t { color: var(--primary); }

.sec-box.lead {
  background: transparent;
  border-style: dashed;
}
.sec-box.lead .k { color: var(--primary); }

.sec-kick {
  margin-right: 8px; display: inline-block;
  text-transform: uppercase; letter-spacing: .04em;
  font: 600 9.5px/1 Inter; color: var(--muted-2);
}

.kcol h4 { display: block; }
.kcol h4 .kt { display: inline; }

/* ---------- PMO board columns, admin controls ----------
   Columns are data now: a grip to drag them, and rename/delete revealed on
   hover so the header stays quiet at rest. Scoped to [data-colhead] so the
   client's board and the Operations Board keep the plain header above. */
.kcol h4[data-colhead] { display: flex; align-items: center; gap: 8px; position: relative; }
.kcol h4[data-colhead] .col-title {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kcol h4[data-colhead] .n { margin-left: auto; }
.col-grip { cursor: grab; color: var(--muted-2); display: inline-flex; touch-action: none; }
.col-grip:active { cursor: grabbing; }
/* Overlaid, not in flow. A fit column bottoms out at 168px, and reserving
   40px for controls that are hidden at rest was enough to ellipsis "Month 1".
   Sitting over the count costs nothing at rest and shifts nothing on hover. */
.col-acts {
  position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  display: inline-flex; gap: 2px; padding-left: 8px; background: var(--surface);
  opacity: 0; pointer-events: none; transition: opacity .12s;
}
.kcol:hover .col-acts, .col-acts:focus-within { opacity: 1; pointer-events: auto; }
.col-act {
  background: none; border: 0; color: var(--muted-2); padding: 2px;
  border-radius: var(--radius); display: inline-flex;
}
.col-act:hover { color: var(--foreground); background: var(--surface-hover); }
.kcol.col-dragging { opacity: .55; }

/* The Ops badge doubles as the un-mirror control, so it needs to look pressable
   on hover without shouting at rest - it is a status first. */
.ops-badge.is-btn { cursor: pointer; border: 1px solid transparent; }
.ops-badge.is-btn:hover {
  border-color: color-mix(in srgb, var(--primary) 45%, transparent);
  background: var(--primary-12);
}

/* The pillar row in the chapter editor: the select, a swatch of the accent so
   the colour is visible while choosing, then edit / add / delete. */
.pillar-row { display: flex; align-items: center; gap: 6px; }
.pillar-row select { flex: 1; min-width: 0; }
.pillar-swatch {
  width: 16px; height: 16px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid var(--line-strong);
}

/* The colour field in the pillar editor: a native picker, the hex beside it for
   pasting a brand value, and a live preview of the chip the accent will make. */
.pillar-color { display: flex; align-items: center; gap: 10px; }
.pillar-color input[type="color"] {
  width: 42px; height: 32px; padding: 2px; flex: 0 0 auto; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--card);
}
.pillar-color input[type="text"] {
  width: 110px; flex: 0 0 auto; font: 500 13px ui-monospace, monospace;
}
.pillar-preview {
  display: inline-flex; align-items: center; padding: 4px 12px; border-radius: 9999px;
  border: 1px solid transparent; font: 600 11.5px Inter; letter-spacing: .02em;
}

/* Add-a-chapter, at the foot of a column. Quiet until the column is hovered:
   it is an authoring affordance, and six of them shouting at once would read
   as part of the board's content. */
.kcol-addcard {
  width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: none; border: 1px dashed var(--line-strong); color: var(--muted-2);
  border-radius: 9999px; padding: 7px 12px; font: 500 12px Inter; margin-top: 4px;
  opacity: 0; transition: opacity .12s;
}
.kcol:hover .kcol-addcard, .kcol-addcard:focus { opacity: 1; }
.kcol-addcard:hover { border-color: var(--primary); color: var(--primary); }
.kcol-addcard .ico { width: 13px; height: 13px; }

/* Lives in the toolbar, not at the end of the board. As a board child it was
   flex-sized alongside the columns, which pushed them to their 168px floor and
   made the board scroll on a full desktop - and it fell off the right edge once
   the columns filled the width. In the toolbar it is always reachable and costs
   the board nothing. A pill, like every other control. */
.tb-addcol {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  background: none; border: 1px solid var(--line-strong); color: var(--muted-foreground);
  border-radius: 9999px; padding: 6px 12px; font: 500 12.5px Inter;
}
.tb-addcol:hover { border-color: var(--primary); color: var(--primary); }
.tb-addcol .ico { width: 14px; height: 14px; }
.kcol h4 .n { float: right; }

/* ============================================================
   Goals - gold (knowledge/insight) rather than the course accent.
   ============================================================ */
.goals-card .page-section-title { color: var(--gold-fg); }
[data-theme="light"] .goals-card .page-section-title { color: var(--gold); }

.goals { display: grid; gap: 8px; }
.goal {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 15px; border-radius: var(--radius);
  border: 1px solid var(--gold-border); background: var(--gold-bg);
  font-size: 13px; font-weight: 500;
}
.goal.done { border-color: var(--primary-30); background: var(--primary-08); }
.goal.done .t { color: var(--muted-foreground); }
.goal-tick {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  border: 1.5px solid var(--gold-border); background: transparent;
  color: var(--primary-fg); cursor: pointer;
}
.goal.done .goal-tick { background: var(--primary); border-color: var(--primary); }

/* ---------- Operations board ----------
   A Linear/Trello board: the columns ARE the visual units, sitting directly on
   the page background (no outer card). All columns fit the viewport at once -
   no fixed widths, no horizontal scroll - and cards truncate rather than push a
   column wider. Inverse of the plan board: light-gray columns, white cards. */
/* align-items: flex-start, exactly like .kanban: a column is as tall as the cards
   in it plus its "+ Add", not as tall as the viewport. Stretching them made seven
   full-height slabs whatever they held, which is not what the PMO board does. */
.ops-board {
  display: flex; gap: 12px; width: 100%; align-items: flex-start;
}
.ops-board .kcol {
  flex: 1 1 0; min-width: 0;                       /* 7 columns share the width evenly */
  display: flex; flex-direction: column; gap: 8px;
  /* A touch darker than the page (light) / lighter (dark), so white cards pop. */
  background: var(--surface-hover); border: none; border-radius: var(--card-radius); padding: 8px;
}
.ops-board .kcol.dragover { background: var(--primary-08); }
.ops-board .kcol.dragover .ops-add { background: transparent; }

/* Column header - no card, one line, dark title + muted count. */
.ops-colhead { display: flex; align-items: baseline; gap: 8px; padding: 2px 4px; }
.ops-coltitle {
  font: 600 13px/1.4 Inter; color: var(--foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.ops-colcount { margin-left: auto; flex: none; font: 500 12px/1.4 Inter; color: var(--muted-2); }

/* Cards stack; the list grows so "+ Add" pins to the column bottom. */
/* Cards stack. It no longer GROWS - growing is what pushed "+ Add" to the foot of
   a full-height column. min-height keeps an empty column a real drop target. */
.ops-cards { display: flex; flex-direction: column; gap: 8px; min-height: 26px; }

/* `.ops-card`, NOT `.ops-board .kcard` - the same shape as `.pmo-card`, and for
   the same reason. The drag ghost is a clone appended to <body>, so any rule that
   needs an `.ops-board` ancestor stops applying the instant a card is lifted: the
   card you picked up lost its padding, its flex row, its title truncation and its
   footer spacing, and what followed the cursor re-flowed into something that was
   not the card you grabbed. The chapter tint below was already de-scoped for this
   exact reason; the rest of the card had been left behind. */
.ops-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 9px 10px; margin: 0; box-shadow: none;
  transition: box-shadow .12s, border-color .12s;
}
/* `:not(.dragging)` is load-bearing: the ghost is pointer-events:none, so the
   element under the cursor mid-drag is the placeholder itself. This rule sits
   AFTER `.kcard.dragging` and ties it on specificity, so without the guard the
   ops slot would re-gain a border and shadow - and the ops board's drag would
   look different from the plan board's, where the ordering happens to be the
   other way round. */
.ops-card:not(.dragging):hover { border-color: var(--line-strong); box-shadow: 0 1px 4px rgba(0,0,0,.08); }
.ops-card .t {                              /* truncate - never force the column wider */
  font: 500 13px/1.4 Inter;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ops-notes {
  font: 400 11.5px/1.4 Inter; color: var(--muted-foreground); margin: 3px 0 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Checkbox sits left of the title, mirroring the PMO board's .pmo-main. */
.ops-main { display: flex; align-items: flex-start; gap: 8px; }
.ops-main .t { flex: 1 1 auto; min-width: 0; }   /* let the existing ellipsis work in flex */
.ops-main .bcheck { margin-top: 0; }
.ops-card .foot { margin-top: 7px; flex-wrap: wrap; }
/* Icon-only chapter chip - square, just the book glyph. */
.ops-card .pill-tag.pill-icon { display: inline-flex; align-items: center; padding: 2px 5px; }
/* Tags become compact 11px chips. */
.ops-card .pill-tag,
.ops-card .badge { font: 500 11px/1.5 Inter; padding: 1px 7px; }

/* Chapter cards wear the same track treatment as the PMO board - tint body,
   tinted border, top accent bar, and the coloured label - so a track reads the
   same everywhere. Element-level selectors (no `.ops-board` ancestor) so the
   detached drag ghost, which lives on <body>, keeps the styling too - the rule
   the whole `.ops-card` block above now follows as well. `.kcard.is-chapter`
   outweighs `.ops-card`, so the tint still wins on background/border. */
.kcard.is-chapter {
  position: relative;
  background: var(--track-tint, var(--card));
  border-color: var(--track-border, var(--line));
}
.ops-track {
  margin-bottom: 4px;
  font: 500 11px/1.3 Inter; color: var(--track-label, var(--muted-foreground));
}

/* The 4px accent bar across the top edge, shared by PMO cards and Ops chapter
   cards. `border-radius: inherit` picks up each card's own corner radius, then
   the bottom corners are squared off. */
.pmo-card::before, .kcard.is-chapter::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  background: var(--track-accent);
  border-radius: inherit; border-bottom-left-radius: 0; border-bottom-right-radius: 0;
  pointer-events: none;
}

/* "+ Add" - a subtle full-width text button at the column bottom. No dashed box. */
.ops-add {
  width: 100%; margin-top: auto; padding: 6px 8px; border-radius: var(--radius);
  font: 500 13px/1 Inter; text-align: left;
  border: none; background: transparent; color: var(--muted-foreground);
  transition: background .12s, color .12s;
}
.ops-add:hover { color: var(--foreground); background: var(--surface-hover); }

.kcard .edit-pin[disabled] { opacity: .5; cursor: default; }
.edit-pin.is-on { opacity: .6; color: var(--primary); }
.kcard .edit-pin { font: 500 10.5px/1.4 Inter; opacity: 0; }
.kcard:hover .edit-pin { opacity: .75; }
.kcard .edit-pin:hover { opacity: 1; }

/* ============================================================
   Locked weeks (Hackathon pacing).
   ============================================================ */
.sec-box.locked, .lrow.locked {
  opacity: .55; cursor: not-allowed;
  background: transparent; border-style: dashed;
}
.sec-box.locked:hover, .lrow.locked:hover {
  border-color: var(--line);
}
.sec-box .n.lock, .lrow .tick.lock {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--muted-foreground); font-weight: 600;
}
.lrow .tick.lock { border: none; background: transparent; }
.lrow.locked .t { color: var(--muted-foreground); }

.mod-body .video-wrap { margin: 16px 0 20px; }

/* ============================================================
   PMO board - Asana-style cards.
   Month columns are bare (no card) on a light-gray fill, like the Ops board.
   ============================================================ */
.kanban.fit .kcol {
  background: var(--surface-hover); border: none; border-radius: var(--card-radius);
  padding: 8px;
}
.kanban.fit .kcol h4 {
  font: 600 13px/1.4 Inter; color: var(--foreground);
  margin: 0 0 8px; padding: 2px 4px;
}
.kanban.fit .kcol h4 .n { color: var(--muted-2); font-weight: 500; }

/* The card wears its track as colour: a light TINT body, a stronger tinted
   border, and a 4px accent bar across the top edge (see the shared ::before
   rule near the Ops board). Neutral-dark title for readability; NO min-height,
   so a title-only card is ~64px and nothing reserves empty space. The tint vars
   come from the track class (track-colors.js); plain cards fall back to --card. */
.pmo-card {
  position: relative;
  background: var(--track-tint, var(--card));
  border: 1px solid var(--track-border, var(--line)); border-radius: var(--card-radius);
  padding: 11px 12px 10px; margin-bottom: 8px; cursor: pointer;
  transition: border-color .12s, box-shadow .12s, filter .12s;
}
/* Lift on hover; keep the tinted border (don't neutralise the hue). */
.pmo-card:hover { box-shadow: 0 2px 8px rgba(16,24,24,.10); }
/* Completed: keep the tint, but desaturate so done cards read quieter. */
.pmo-card.done { filter: saturate(.45); }
/* A locked (paced) course chapter: dimmed, not clickable, its check a dashed disc. */
.pmo-card.locked { cursor: not-allowed; opacity: .6; }
.pmo-card.locked:hover { box-shadow: none; }
.pmo-card.locked .bcheck { border-style: dashed; background: transparent; cursor: not-allowed; }
.pmo-card.locked .bcheck .ico { color: var(--muted-2); }

/* THUMBNAIL - a chapter's own picture on its board card, when it has one.

   Bleeds to the card's edges (the card's padding is cancelled with a negative
   margin) and is CROPPED to a fixed 16:9 strip, so a column of cards keeps one
   rhythm no matter what shape anybody uploads. object-fit: cover means a tall
   image is centre-cropped rather than squashed.

   The 4px track bar keeps its place over the top edge: the picture says which
   chapter, the bar still says which track. */
.pmo-cover {
  margin: -11px -12px 9px;
  border-radius: var(--card-radius) var(--card-radius) 0 0;
  overflow: hidden;
  background: var(--surface);
  aspect-ratio: 16 / 9;
}
.pmo-cover img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* The editor's preview of that picture, next to Upload / Replace. Same crop as
   the card, so what an admin approves is what the board shows. */
.cover-prev {
  width: 190px; aspect-ratio: 16 / 9; flex: none;
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: var(--surface);
}
/* The slim in-form variant: one row with its buttons, not a quarter of it. */
.cover-prev.sm { width: 96px; font-size: 11px; }
.cover-prev img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cover-prev.empty {
  display: flex; align-items: center; justify-content: center;
  border-style: dashed; color: var(--muted-foreground); font: 500 12px/1 Inter;
}

/* Track label - the track NAME only (no dot), directly under the accent bar,
   in the hue's dark shade. */
.pmo-track {
  margin-bottom: 4px;
  font: 500 11px/1.3 Inter; color: var(--track-label, var(--muted-foreground));
}

/* Title line: inline check, then the title (max 3 lines, then ellipsis). */
.pmo-main { display: flex; align-items: flex-start; gap: 8px; }
.pmo-card .t {
  font: 500 13px/1.35 Inter; color: var(--foreground);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.pmo-card.done .t { color: var(--muted-foreground); text-decoration: line-through; }
.pmo-card .bcheck { margin-top: 0; }

/* GOAL cards - the "By month 6" goals column, back from the Asana board. A goal
   is an outcome you reach, not a lesson you work through, so the card type says
   so: no track tint (a plain card on the column gray), the ACCENT on its bar
   and kicker, and the check drawn as a DIAMOND - Asana's milestone glyph. All
   tokens, so both themes and a re-theme hold. Named `is-goal` like the ops
   board's `is-chapter` - a bare `.goal` already means the gold goal rows. */
.pmo-card.is-goal {
  --track-accent: var(--primary);
  --track-tint: var(--card);
  --track-border: var(--line);
  --track-label: var(--primary);
}
.pmo-card.is-goal .pmo-track { display: flex; align-items: center; gap: 5px; }
.pmo-card.is-goal .pmo-track .ico { width: 10px; height: 10px; }

/* The diamond check: the round .bcheck, shrunk and rotated 45deg so its points
   span the same 20px slot the circle used; the tick inside rotates back. */
.bcheck.is-goal { border-radius: 4px; width: 15px; height: 15px; margin: 2px 2.5px 0; transform: rotate(45deg); }
.bcheck.is-goal .ico { transform: rotate(-45deg); }

/* Metadata row: ONLY set fields (owner / due) + the mirrored Ops badge, all on ONE
   line - owner, then due, then "✓ Ops" to their right (Matt · Jul 28 · ✓ Ops).
   Empty = collapsed, so an untouched card stays compact.

   nowrap, NOT wrap: with wrap, the browser breaks a line on each chip's content
   width without considering shrink, so the Ops badge dropped to a second row even
   when there was clearly room. nowrap keeps the three together; the ONLY chip that
   may give ground is the owner (its name truncates with an ellipsis). The due date
   and the Ops status never shrink and never wrap, so toggling Ops on/off can't
   change the card's height. */
.pmo-meta { display: flex; align-items: center; flex-wrap: nowrap; gap: 6px; margin-top: 6px; min-width: 0; }
.pmo-meta:empty { display: none; }
.pmo-meta > .owner { flex: 0 1 auto; min-width: 0; }
.pmo-meta > .owner > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pmo-meta > .due, .pmo-meta > .ops-badge { flex: 0 0 auto; }
/* Course-board cards carry only informational badges (Video / Tool / checklist),
   with no owner chip to give ground - so let those wrap naturally in a narrow
   column instead of overflowing. The nowrap rule is for the PMO owner/due/Ops row. */
.course-board .pmo-meta { flex-wrap: wrap; }

/* ---------- Course board fits the viewport ----------
   The .fit columns already share the row equally, but the 168px per-column
   floor (line ~1398) makes an 8-column board - the Hackathon course - wider
   than a laptop window, so the whole board scrolled sideways. On a desktop
   the floor comes off: however many columns the board has, they split the
   width that exists and the page only ever scrolls DOWN. Card innards all
   shrink safely (titles line-clamp, covers scale, metachips wrap).
   Below 1200px eight columns would be slivers whose two header lines cannot
   hold the longer column names even at 11px, so the floor - and the board's
   existing sideways scroll - stays for narrow windows. */
@media (min-width: 1200px) {
  .kanban.fit.course-board .kcol { min-width: 0; }
}

/* ---------- Course board column header: always two lines tall ----------

   Two late rules had turned this header into a BLOCK box with a FLOATED count
   (.kcol h4 { display: block } and .kcol h4 .n { float: right }). A title too long
   for one line then wrapped, the float had nowhere left on that line and dropped
   to the next - and a block box does not contain its floats, so the count escaped
   the header entirely and landed on top of the first card.

   The second half of the same bug: a column whose title wrapped grew its header
   and pushed its own cards down, while its single-line neighbours stayed put. So
   no two columns started at the same height.

   Flex ends the escape. Reserving two lines' worth of height ends the
   misalignment: every column's first card starts on the same line whether its
   name wrapped or not, and a name longer than two lines clamps rather than
   growing the column. Specificity is deliberately above the PMO board's
   [data-colhead] rules, so an admin's header (grip + hover actions) and a
   member's are the same shape. */
.kanban.fit.course-board .kcol h4 {
  display: flex; align-items: flex-start; gap: 6px;
  /* A step DOWN from the boards' 13px: with the columns now splitting the
     viewport (no sideways scroll), a narrow window narrows every column, and at
     13px names like "Week 5: Customer Success" ran out of their two lines and
     ellipsised. 12px buys the second line the word that was falling off. */
  font-size: 12px;
}
.kanban.fit.course-board .kcol h4 .col-title {
  flex: 1 1 auto; min-width: 0;
  white-space: normal;                /* the PMO header keeps its one-line ellipsis */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
  /* The two reserved lines live on the TITLE, not on the header. On the header
     they would be a border-box min-height, so its 2px padding pushed a wrapping
     column 4px lower than a single-line one - the misalignment in miniature. */
  min-height: 2.8em;                  /* two lines at the header's own 1.4 leading */
}
/* One more step as the window - and with it every fit column - narrows, so the
   longest names ("Week 1: Positioning & Offer") still land in two whole lines. */
@media (max-width: 1366px) {
  .kanban.fit.course-board .kcol h4 { font-size: 11px; }
}

/* Card titles follow the header down a step - same reason, same scope: the
   course board's fit-to-viewport columns are narrower than the 280px kind. */
.course-board .pmo-card .t { font-size: 12px; }

/* The hover actions: what you'd SET (unset owner / due) plus the mirror button.
   ABSOLUTELY positioned, so revealing them on hover never changes the card's
   height - that height change was what made the row flicker as the pointer
   crossed the edge. Top-right, over a card-coloured chip so text stays legible. */
.pmo-actions {
  position: absolute; top: 8px; right: 7px;
  display: flex; align-items: center; gap: 3px;
  padding: 2px; border-radius: 7px; background: var(--track-tint, var(--card));
  opacity: 0; pointer-events: none; transition: opacity .12s;
}
.pmo-card:hover .pmo-actions,
.pmo-card:focus-within .pmo-actions { opacity: 1; pointer-events: auto; }
.pmo-actions .metachip { opacity: .6; }
.pmo-actions .metachip:hover { opacity: 1; }
.pmo-actions .edit-pin { opacity: 1; }

/* The "mirrored to Ops" badge - a stable, always-on status chip (no hover
   toggle, no re-mount), so it can't flicker. */
.ops-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 7px; border-radius: 4px;
  font: 500 11px/1.6 Inter; color: var(--primary); background: var(--primary-08);
}
.ops-badge svg { width: 12px; height: 12px; }

/* A metadata chip (owner / due). Set = a quiet gray chip; a ghost (in the hover
   actions) is a bare, faint icon you click to set the field. */
.metachip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 7px; border-radius: 4px; border: none;
  font: 500 11px/1.6 Inter; background: transparent; color: var(--muted-2);
}
.metachip svg { width: 12px; height: 12px; }
.metachip.set { background: var(--surface-hover); color: var(--muted-foreground); }
button.metachip { cursor: pointer; }
button.metachip:hover { background: var(--surface-hover); color: var(--foreground); }
.metachip.ghost { padding: 2px 5px; }
.metachip.due.set.late { background: rgba(209,47,47,.08); color: var(--red); }

/* Who a card belongs to, as a face rather than a name (see personDisc in
   board.js). The disc carries its own background, so the chip around it drops
   the usual .set fill and padding - otherwise it reads as a pill with a hole. */
.metachip.disc, .metachip.owner.disc { padding: 0; background: transparent; }
button.metachip.disc:hover { background: transparent; }
button.metachip.disc:hover .pdisc { box-shadow: 0 0 0 2px var(--primary-08); }

.pdisc {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  display: inline-grid; place-items: center; overflow: hidden;
  background: var(--surface-hover); color: var(--muted-foreground);
  border: 1px solid var(--line); font: 600 9px/1 Inter; letter-spacing: .2px;
  transition: box-shadow .12s;
}
.pdisc img { width: 100%; height: 100%; object-fit: cover; }

/* The little popover the chips open. */
.pop {
  position: absolute; z-index: 300; width: 230px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 12px; box-shadow: var(--shadow-pop);
  /* Taller than the screen (a long peer-group list on a small window): the
     popover pins inside the viewport and scrolls its own content instead. */
  max-height: calc(100vh - 24px); overflow-y: auto;
}

/* Complete a chapter straight from a board. */
.bcheck {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  display: grid; place-items: center; padding: 0; line-height: 0;
  border: 1.5px solid var(--line-strong); background: transparent;
  color: var(--primary-fg); cursor: pointer; transition: background .12s, border-color .12s;
}
.bcheck .ico { width: 11px; height: 11px; vertical-align: 0; color: var(--muted-2); }
.bcheck:hover { border-color: var(--primary); background: var(--surface-hover); }
.bcheck:hover .ico { color: var(--primary); }
.bcheck.on { background: var(--primary); border-color: var(--primary); }
.bcheck.on .ico { color: var(--primary-fg); }
.bcheck[disabled] { cursor: default; opacity: 1; }
.bcheck[disabled]:hover { border-color: var(--line-strong); background: transparent; }
.bcheck[disabled]:hover .ico { color: var(--muted-2); }

/* ============================================================
   Detail drawer - chapter slides in over the list, Asana-style.
   ============================================================ */
.detail-drawer {
  position: fixed; top: 0; right: 0; z-index: 92;
  height: 100vh; width: min(760px, 94vw);
  background: var(--background);
  border-left: 1px solid var(--line);
  box-shadow: -12px 0 40px rgba(0, 0, 0, .18);
  transform: translateX(100%);
  /* `width` animates too, so maximising (grow to the full content width) and
     restoring the side panel slide rather than jump. The right edge is pinned
     (`right:0`), so widening pushes the LEFT edge out over the board. */
  transition: transform .3s cubic-bezier(.32, .72, 0, 1),
              width .3s cubic-bezier(.32, .72, 0, 1);
  display: flex; flex-direction: column;
}
[data-theme="light"] .detail-drawer { box-shadow: -12px 0 40px rgba(16, 24, 24, .10); }
.detail-drawer.show { transform: translateX(0); }
/* Maximised over a board: no left border - it spans the whole content area, so
   there is no list peeking beside it for the seam to separate from. */
.detail-drawer.maximized { border-left: 0; }

/* Overlay push - the PMO / Operations board drawer. The list-split view opts out
   (`:not(.list-split)`): it shrinks the list to a real column instead of nudging
   the centred content sideways under an overlay. */
@media (min-width: 1200px) {
  body.drawer-open:not(.list-split) .main {
    margin-right: calc(min(760px, 94vw) + 32px);
    transition: margin .3s cubic-bezier(.32, .72, 0, 1);
  }
}

.drawer-body {
  flex: 1; overflow-y: auto; overscroll-behavior: contain;
  padding: 24px 28px 56px;
}

.lrow.selected, .kcard.selected {
  border-color: var(--primary);
  background: var(--primary-08);
}

/* ---------- List split view ----------
   A chapter opened from the LIST turns the drawer into the right pane of a split:
   the list shrinks to a fixed column (SPLIT_LIST_W in app.js sets the drawer's
   left edge to match) and the detail fills the rest. Only on wide screens; below
   900px the drawer falls back to the overlay above.
   The drawer's left / width are set inline by updateSplitMode(); these rules
   handle the list side. */
@media (min-width: 900px) {
  /* Fixed column - not a flexing max-width. The drawer's left edge is pinned to
     380px (SPLIT_LIST_W in app.js), so the list must hold exactly that width. */
  #main[data-panel-open].main {
    width: 380px; max-width: 380px; flex: none;
    margin: 0;
    padding-left: 20px; padding-right: 16px;
  }
  /* Keep only the compact context row and the open section's module list; drop the
     progress header, the Chapters strip and the goals card while the pane is open.
     Keyed off data-panel-open on #main (not a `.card` wrapper the de-boxed markup
     no longer emits), so it hides whatever the list renders. */
  #main[data-panel-open] > *:not(#sec-list):not(.list-context-row) { display: none; }
  #main[data-panel-open] #sec-list { margin: 0; }
  /* The section's own title line is redundant with the context row's switcher. */
  #main[data-panel-open] #sec-list > .page-section-title { display: none; }

  /* Rows collapse to circle + title, one line, ellipsis on overflow. */
  #main[data-panel-open] #sec-list .lrow .pill-tag,
  #main[data-panel-open] #sec-list .lrow .badge,
  #main[data-panel-open] #sec-list .lrow .muted,
  #main[data-panel-open] #sec-list .lrow .edit-pin { display: none; }
  #main[data-panel-open] #sec-list .lrow .t {
    min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* The module open in the pane gets a teal left border on top of the tint. */
  #main[data-panel-open] #sec-list .lrow.selected {
    border-left: 2px solid var(--primary);
  }
}

/* The compact context row: a single ~40px line - section switcher + completion
   count - that replaces the Chapters strip in the shrunk split column. Hidden
   in the full list view; only data-panel-open reveals it. */
.list-context-row { display: none; }
#main[data-panel-open] .list-context-row {
  display: flex; align-items: center; gap: 10px;
  height: 40px; margin: 0 0 12px; min-width: 0;
}
.list-sec-select {
  flex: 1; min-width: 0; height: 34px; padding: 0 30px 0 11px;
  font: 600 12.5px/1 Inter; color: var(--foreground);
  border: 1px solid var(--line-strong); border-radius: 9999px;
  background: var(--input-bg);
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center;
  cursor: pointer; text-overflow: ellipsis;
}
.list-sec-select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }
.list-sec-count { flex: none; font: 500 11.5px/1 Inter; color: var(--muted-foreground); white-space: nowrap; }

@media (max-width: 640px) {
  .detail-drawer { width: 100vw; }
  .drawer-body { padding: 18px 15px 50px; }
}
@media (prefers-reduced-motion: reduce) {
  .detail-drawer { transition: none; }
  .main { transition: none; }
  .sidebar { transition: none; }
  .content { transition: none; }
  .navrow .lbl { transition: none; }
  .nav-item::after { transition: none; }
}

/* ============================================================
   Positioning wizard - rich widgets. Tokens only, one accent at low
   opacity, 1px borders, pills. No new colour, no themed decoration - the
   life comes from the widgets and motion, not styling.
   ============================================================ */

/* ---- step transition: content slides + fades in (200ms) ---- */
@keyframes panelInFwd  { from { transform: translateX(14px);  opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes panelInBack { from { transform: translateX(-14px); opacity: 0; } to { transform: none; opacity: 1; } }
.panel-in-fwd  { animation: panelInFwd  .2s ease both; }
.panel-in-back { animation: panelInBack .2s ease both; }

/* ---- live mini-summary line under a step ---- */
.wiz-summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  margin-top: 22px; padding: 11px 14px;
  border: 1px solid var(--line); border-radius: var(--card-radius);
  background: var(--surface); animation: panelInFwd .25s ease both;
}
.wiz-summary-l { display: inline-flex; align-items: center; gap: 6px; font: 600 11px/1 Inter;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted-foreground); }
.wiz-summary-v { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.wiz-summary-chip { font: 600 12.5px/1.4 Inter; color: var(--foreground); }
.wiz-summary-dot { color: var(--muted-2); }

/* ---- rail: draw the check in only when a node has just completed ---- */
@keyframes railDraw { from { stroke-dashoffset: 22; } to { stroke-dashoffset: 0; } }
/* The overshoot is deliberately small. A segmented button is only 3px from the
   one beside it, so a 1.12 pop grew an 85px pill by ~10px and it visibly climbed
   over its neighbour for the length of the animation. 1.05 stays inside the gap. */
@keyframes markPop  { 0% { transform: scale(.5); } 60% { transform: scale(1.05); } 100% { transform: scale(1); } }
.wiz-mark.done.is-fresh { animation: markPop .3s ease both; }
.wiz-mark.done.is-fresh svg path { stroke-dasharray: 22; animation: railDraw .32s ease .04s both; }

/* ============================================================ region map + combo */
.pos-regions { display: flex; flex-direction: column; gap: 10px; }
/* The map is the screen, so it takes the height the viewport can spare. */
.pos-map {
  height: clamp(280px, 46vh, 560px);
  border: 1px solid var(--line-strong); border-radius: var(--card-radius);
  overflow: hidden; position: relative; background: var(--surface);
}
.pos-map.is-error { display: flex; align-items: center; justify-content: center; }
.pos-map-fail, .pos-map::after { color: var(--muted-foreground); font-size: 12.5px; }
.pos-map:empty::after { content: 'Loading map...'; position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; }
.pos-map-fail { padding: 16px; text-align: center; }
/* city pin - a teal teardrop */
.pos-map-pin {
  width: 16px; height: 16px; border-radius: 50% 50% 50% 0; transform: rotate(-45deg);
  background: var(--primary); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(16,24,24,.35);
}

/* combo type-ahead (shared by region + tool chip inputs) */
.pos-combo { position: relative; }
.pos-combo-in { display: flex; align-items: center; gap: 8px; padding: 0 11px;
  border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--input-bg);
  transition: border-color .12s, box-shadow .12s; }
.pos-combo-in:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }
.pos-combo-in .ico { color: var(--muted-2); flex: none; }
.pos-combo-in input { border: none; background: none; padding: 9px 0; }
.pos-combo-in input:focus { outline: none; box-shadow: none; }
.pos-suggest {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 4px);
  background: var(--card); border: 1px solid var(--line-strong); border-radius: var(--radius);
  box-shadow: var(--shadow-pop); overflow: hidden; max-height: 280px; overflow-y: auto;
}
.pos-suggest-row {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  padding: 8px 11px; background: none; border: none; cursor: pointer; color: var(--foreground);
  font: 500 13px/1.3 Inter; border-bottom: 1px solid var(--line);
}
.pos-suggest-row:last-child { border-bottom: none; }
/* One highlight at a time. The list opens under the pointer, so a row sitting
   beneath the mouse would tint alongside the arrow-key row and read as two
   selections; while the keyboard is driving (.is-kbd) hover stays quiet. */
.pos-suggest-row.is-active { background: var(--primary-08); }
.pos-suggest:not(.is-kbd) .pos-suggest-row:hover { background: var(--primary-08); }
.pos-suggest-row .ico { color: var(--muted-2); flex: none; }
.pos-suggest-l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pos-suggest-s { font-size: 11.5px; color: var(--muted-2); flex: none; }
.pos-suggest-custom .pos-suggest-l { color: var(--primary); font-weight: 600; }

/* chips */
.pos-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.pos-chips-empty { font-size: 12.5px; color: var(--muted-2); padding: 2px 0; }
.pos-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 6px 5px 10px;
  border: 1px solid var(--primary-30); border-radius: 9999px;
  background: var(--primary-08); color: var(--foreground); font: 600 12.5px/1 Inter;
  animation: markPop .18s ease both;
}
.pos-chip .ico { color: var(--primary); flex: none; }
.pos-chip-custom { border-color: var(--line-strong); background: var(--surface); }
.pos-chip-custom .ico { color: var(--muted-2); }
/* Exclusion ("except Russia") - a carve-out, so read it as muted/subtractive,
   not selected. Dashed edge + struck label; neutral, never a decorative red. */
.pos-chip-exclude { border-style: dashed; border-color: var(--line-strong); background: var(--surface); color: var(--muted-foreground); }
.pos-chip-exclude .ico { color: var(--muted-2); }
.pos-chip-exclude > span { text-decoration: line-through; text-decoration-thickness: 1px; }
.pos-chip-x {
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
  width: 18px; height: 18px; border: none; border-radius: 50%; cursor: pointer;
  background: none; color: var(--muted-2);
}
.pos-chip-x:hover { background: var(--surface-hover); color: var(--red); }

/* ============================================================ segmented control */
/* inline-flex, NOT `flex` + `width: fit-content`. fit-content resolves against
   the space available rather than the content, and on a WRAPPING flex container
   that lands you with a box narrower than the row it has to hold: the buttons
   reflowed inside it, several ended up at the same x with widths wider than
   their own container, and picking a size hit whichever one happened to be on
   top. inline-flex shrink-wraps to the content, which is what was wanted. */
.pos-seg {
  display: inline-flex; gap: 3px; padding: 3px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line); max-width: 100%;
  flex-wrap: wrap; vertical-align: top;
}
.pos-seg-btn { flex: none; white-space: nowrap; }
.pos-seg-btn {
  padding: 7px 14px; border: none; border-radius: 4px; background: none; cursor: pointer;
  font: 600 12.5px/1 Inter; color: var(--muted-foreground);
  transition: background .15s, color .15s, transform .15s;
}
.pos-seg-btn:hover { color: var(--foreground); }
.pos-seg-btn.is-on { background: var(--primary); color: var(--primary-fg); }
/* `seg-pop`, NOT `pop`. `.pop` is the popover class further up - `position:
   absolute; width: 230px; z-index: 300` - so adding it to retrigger an animation
   took the button out of the flow, stretched it to 230px and floated it over its
   neighbours. The control shrank with every pick as another button left the flow,
   and the one on top swallowed the clicks meant for the others. It was never
   removed, so it never came back. */
.pos-seg-btn.seg-pop { animation: markPop .15s ease; }
.pos-note { margin-top: 16px; max-width: 760px; }

/* ============================================================ employee bars */
/* The company-size picker. An ordered scale, so it draws the same rising bars
   Review does (.rv-scale) rather than a row of pills - you pick a place on a
   scale you can see. Each bar is its own toggle: an ICP can straddle sizes. */
.pos-empbars { display: flex; align-items: flex-end; gap: 16px; max-width: 760px; margin: 18px 0 2px; }
.pos-empbar {
  flex: 1 1 0; min-width: 0; padding: 12px 8px 10px; border: none; background: none;
  cursor: pointer; text-align: center; border-radius: var(--radius);
  transition: background .15s;
}
.pos-empbar:hover { background: var(--primary-08); }
.pos-empbar-col {
  display: block; width: 100%; max-width: 64px; margin: 0 auto;
  border-radius: 6px 6px 0 0;
  background: var(--primary-08); border: 1px solid var(--line);
  transition: background .18s, border-color .18s;
}
.pos-empbar:hover .pos-empbar-col { border-color: var(--primary); }
.pos-empbar.is-on .pos-empbar-col { background: var(--primary); border-color: var(--primary); }
.pos-empbar-t {
  display: block; margin-top: 10px; font: 600 13.5px/1.3 Inter; color: var(--muted-foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pos-empbar.is-on .pos-empbar-t { color: var(--primary); }
.pos-empbar.seg-pop { animation: markPop .15s ease; }

/* ============================================================ revenue slider */
.pos-slider { margin: 6px 0 14px; }
.pos-slider-val {
  font: 700 15px/1 Inter; color: var(--primary); margin-bottom: 10px;
}
.pos-slider.is-unset .pos-slider-val { color: var(--muted-2); font-weight: 500; font-size: 13px; }
.pos-slider input[type=range] {
  -webkit-appearance: none; appearance: none; width: 100%; height: 6px; padding: 0;
  border: none; border-radius: 4px; cursor: pointer;
  background: linear-gradient(to right, var(--primary) 0%, var(--primary) var(--fill, 40%),
    var(--bar-track) var(--fill, 40%), var(--bar-track) 100%);
}
.pos-slider.is-unset input[type=range] { background: var(--bar-track); }
.pos-slider input[type=range]:focus { outline: none; box-shadow: none; }
.pos-slider input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%;
  background: var(--card); border: 2px solid var(--primary); box-shadow: var(--shadow-pop); cursor: grab;
  transition: transform .12s;
}
.pos-slider input[type=range]:active::-webkit-slider-thumb { transform: scale(1.15); cursor: grabbing; }
.pos-slider input[type=range]::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%; background: var(--card);
  border: 2px solid var(--primary); cursor: grab;
}
.pos-slider-ticks { display: flex; justify-content: space-between; margin-top: 8px;
  font: 500 10.5px/1 Inter; color: var(--muted-2); }
.pos-slider-ticks span { flex: 1; text-align: center; }
.pos-slider-ticks span:first-child { text-align: left; }
.pos-slider-ticks span:last-child { text-align: right; }
.pos-rev-reason { min-height: 68px; }

/* ============================================================ ICP drafts
   The panel that drafts a later ICP answer from the earlier ones (the
   client's business model, and the unit economics that follow from it).
   GOLD, not the accent: this is knowledge offered, the same language the
   chapter Pro-tip box speaks - and it must never read as a completed step. */
.pos-draft {
  border: 1px solid var(--gold-border); background: var(--gold-bg);
  border-radius: var(--card-radius); padding: 12px 14px; margin-bottom: 16px;
}
.pos-draft-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font: 500 12.5px/1.5 Inter; color: var(--muted-foreground);
}
.pos-draft-head svg { color: var(--gold); flex: none; }
.pos-draft-head b { font: 600 13px/1.4 Inter; color: var(--foreground); }
.pos-draft-head span { flex: 1 1 260px; min-width: 0; }
.pos-draft-ctx { margin: 10px 0 0; }
.pos-draft-actions { align-items: center; gap: 10px; margin-top: 12px; }
.pos-draft-note { font: 500 12px/1.45 Inter; color: var(--red); }
.pos-draft-out {
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--gold-border);
  display: flex; flex-direction: column; gap: 10px;
  animation: draftIn .22s ease-out both;
}
.pos-draft-part { font: 400 13px/1.55 Inter; color: var(--foreground); white-space: pre-wrap; overflow-wrap: anywhere; }
.pos-draft-k { display: block; font: 600 11px/1.4 Inter; color: var(--muted-foreground); margin-bottom: 2px; }
.pos-draft-acts { gap: 8px; flex-wrap: wrap; margin-top: 2px; }
@keyframes draftIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .pos-draft-out { animation: none; } }

/* The business model asked as the five questions the guidance asks, in the
   guidance's order. The two broad ones take a row each; "Type of business" is
   a one-liner, so it takes a full row too but stays short - a half-width box
   with nothing beside it reads as a missing field. */
.pos-bm-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.pos-bm-part.is-wide, .pos-bm-part.is-line { grid-column: 1 / -1; }
.pos-bm-part .fl { display: block; margin-bottom: 5px; }
.pos-bm-part textarea { min-height: 62px; }
.pos-bm-part.is-wide textarea { min-height: 84px; }
.pos-bm-part.is-line textarea { min-height: 0; height: 42px; }
@media (max-width: 720px) { .pos-bm-grid { grid-template-columns: 1fr; } }

/* The persona photo row: the URL input, a preview of what is in it, and the
   button that puts one there. */
.pos-photo { display: flex; align-items: center; gap: 10px; }
.pos-photo input[type=text] { flex: 1 1 auto; min-width: 0; }
.pos-photo .btn-sec { flex: none; }
.pos-photo.is-drop { outline: 2px dashed var(--primary); outline-offset: 3px; border-radius: var(--radius); }
.pos-photo-prev { flex: none; width: 34px; height: 34px; }
.pos-photo-prev img {
  width: 34px; height: 34px; border-radius: 50%; object-fit: cover;
  border: 1px solid var(--line);
}

/* ============================================================ tool chip inputs */
.pos-toolchips { display: flex; flex-direction: column; gap: 10px; }
.pos-chip-tool { border-color: var(--line-strong); background: var(--card); }
.pos-tool-ava {
  display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;
  border-radius: 4px; background: var(--surface-hover); border: 1px solid var(--line);
  font: 700 10px/1 Inter; color: var(--muted-foreground); flex: none;
}

/* ============================================================ persona roster */
/* Fills whatever width the stage gives it - 2 cards on a laptop, 4 on a wide
   screen - instead of two stretched columns with the rest of the row empty. */
.pos-persona-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: 10px; align-items: start;
}
.pos-persona {
  border: 1px solid var(--line); border-radius: var(--card-radius); background: var(--surface);
  padding: 12px; transition: border-color .2s, background .2s, box-shadow .2s;
}
.pos-persona[open] { grid-column: 1 / -1; background: var(--card); border-color: var(--line-strong); }
.pos-persona.is-active { border-color: var(--primary-30); background: var(--primary-04); }
.pos-persona > summary { cursor: pointer; display: flex; align-items: flex-start; gap: 12px; }
.pos-persona > summary::-webkit-details-marker { display: none; }
.pos-persona-ava {
  flex: none; width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid var(--line-strong); background: var(--card);
  display: flex; align-items: center; justify-content: center; color: var(--muted-2);
  transition: color .2s, border-color .2s, background .2s;
}
.pos-persona-ava svg { width: 30px; height: 30px; }
.pos-persona.is-active .pos-persona-ava {
  color: var(--primary); border-color: var(--primary-30); background: var(--primary-12);
}
.pos-persona-meta { flex: 1; min-width: 0; }
.pos-persona-meta h4 { font: 600 13.5px/1.35 Inter; margin: 0; }
.pos-persona-role { font-size: 11px; color: var(--muted-foreground); margin-top: 3px; line-height: 1.45; }
.pos-persona-state { display: inline-block; margin-top: 6px; font-size: 11px; color: var(--muted-2); }
.pos-persona.is-active .pos-persona-state { color: var(--primary); }
.pos-persona-jt { margin-top: 6px; }
.pos-persona-chev { color: var(--muted-2); flex: none; margin-top: 2px; transition: transform .2s; width: 16px; height: 16px; }
.pos-persona[open] .pos-persona-chev { transform: rotate(180deg); }
.pos-persona-body { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.pos-persona-body .field { margin-bottom: 12px; }
.pos-pop { animation: markPop .2s ease; }

/* ranking lineup */
.pos-ranking { display: flex; flex-direction: column; gap: 7px; }
.pos-ranking-empty { font-size: 12.5px; color: var(--muted-2); padding: 4px 0; line-height: 1.5; }
.pos-ranking-note {
  display: flex; align-items: baseline; gap: 6px; font-size: 12px; color: var(--muted-foreground);
  padding: 9px 11px; margin-bottom: 4px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); line-height: 1.5;
}
.pos-ranking-note .ico { flex: none; color: var(--muted-2); align-self: center; }
.pos-ranking-note span { color: var(--foreground); font-weight: 500; }
.pos-rank-row {
  display: flex; align-items: center; gap: 11px; padding: 9px 12px;
  border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--card);
  cursor: grab; transition: box-shadow .15s, border-color .15s, opacity .15s;
}
.pos-rank-row:hover { border-color: var(--primary-30); }
.pos-rank-row.is-dragging { opacity: .5; box-shadow: var(--shadow-pop); cursor: grabbing; }
.pos-rank-num {
  flex: none; width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; background: var(--primary-12); color: var(--primary); font: 700 12px/1 Inter;
}
.pos-rank-grip { color: var(--muted-2); display: inline-flex; }
.pos-rank-ava { flex: none; width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center; color: var(--primary); }
.pos-rank-ava svg { width: 20px; height: 20px; }
.pos-rank-title { font: 600 13px/1.3 Inter; }

/* ============================================================ touchpoint tiles */
/* align-items: start, so an unticked tile is its own height. Grid rows stretch
   by default, so a collapsed tile sharing a row with an expanded one was held
   open to match it - which is why some appeared to collapse and others did not,
   depending only on what happened to sit beside them. */
.pos-tiles {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: 10px; align-items: start;
}
.pos-tile {
  border: 1px solid var(--line-strong); border-radius: var(--card-radius); background: var(--card);
  overflow: hidden; transition: border-color .18s, background .18s, box-shadow .18s;
}
.pos-tile.is-on { border-color: var(--primary); background: var(--primary-04); }
.pos-tile-head {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 13px 14px; background: none; border: none; cursor: pointer; color: var(--foreground);
}
.pos-tile-ico { display: inline-flex; color: var(--muted-foreground); flex: none; }
.pos-tile-ico .ico { width: 20px; height: 20px; }
.pos-tile.is-on .pos-tile-ico { color: var(--primary); }
.pos-tile-name { flex: 1; font: 600 13.5px/1.3 Inter; }
.pos-tile-check {
  flex: none; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; background: var(--primary); color: var(--primary-fg);
  opacity: 0; transform: scale(.5); transition: opacity .18s, transform .18s;
}
.pos-tile-check .ico { width: 12px; height: 12px; }
.pos-tile.is-on .pos-tile-check { opacity: 1; transform: scale(1); }
.pos-tile-why { padding: 0 14px 13px; }
.pos-tile-why textarea { min-height: 54px; }
.pos-tile-add {
  display: flex; align-items: center; justify-content: center; gap: 7px; padding: 13px;
  border-style: dashed; color: var(--muted-foreground); cursor: pointer; font: 600 13px/1 Inter;
}
.pos-tile-add:hover { border-color: var(--primary-30); color: var(--primary); }
.pos-tile-add .ico { width: 16px; height: 16px; }

/* ============================================================ review page */
.rv-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.rv-count { flex: none; font: 700 26px/1 Inter; color: var(--muted-foreground); letter-spacing: -.01em; }
.rv-count.is-complete { color: var(--primary); }
.rv-count-sep { opacity: .4; margin: 0 1px; }
.rv-map {
  height: clamp(200px, 30vh, 380px);
  border: 1px solid var(--line-strong); border-radius: var(--card-radius);
  overflow: hidden; position: relative; margin-bottom: 20px; background: var(--surface);
}
@keyframes rvIn { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }
.rv-doc.rv-stagger > * { animation: rvIn .4s ease both; }

@media (max-width: 640px) {
  .pos-persona-grid, .pos-tiles { grid-template-columns: 1fr; }
  .pos-map { height: 260px; }
}
@media (prefers-reduced-motion: reduce) {
  .panel-in-fwd, .panel-in-back, .wiz-summary, .pos-chip, .pos-pop, .pos-seg-btn.seg-pop, .pos-empbar.seg-pop,
  .wiz-mark.done.is-fresh, .wiz-mark.done.is-fresh svg path, .rv-doc.rv-stagger > * { animation: none; }
}

/* ============================================================
   COMMUNITY - the cohort chat.
   Written against the tokens only, so a re-theme stays a token swap. Own-message
   bubbles use the accent at low opacity (--primary-12); everyone else is neutral
   surface. Pills on every control. Red stays status-only - a mention badge uses
   the accent, a plain unread uses neutral ink.
   ============================================================ */

/* --- the sidebar unread badge on the Community nav row --- */
.navrow-badge {
  flex: none; min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  font: 700 11px/1 Inter; border-radius: 9999px;
  background: var(--surface-hover); color: var(--muted-foreground);
  border: 1px solid var(--line-strong);
}
.navrow-badge.mention {
  background: var(--primary-12); color: var(--primary);
  border-color: var(--primary-30);
}
/* Reveal-state tag on a Tools row ("Admin Only" / "Beta") - temporary
   labelling while the tools are admin-only; delete when they open to
   everyone. Neutral for Admin Only, the status blue for Beta. */
.navrow-tag {
  flex: none; padding: 1px 6px; border-radius: 9999px;
  font: 600 9.5px/1.6 Inter; letter-spacing: .02em;
  background: var(--surface-hover); color: var(--muted-foreground);
}
.navrow-tag.beta { background: rgba(13,127,158,.12); color: var(--blue); }
[data-sidebar="collapsed"] .navrow-tag { display: none; }
/* In the collapsed rail there's no room for a number - show a small dot on the
   icon instead, reusing the same accent/neutral distinction. */
[data-sidebar="collapsed"] .navrow-badge { display: none; }
[data-sidebar="collapsed"] .nav-item[data-unread] .navrow-ico::after {
  content: ""; position: absolute; top: -2px; right: -2px;
  width: 7px; height: 7px; border-radius: 9999px;
  background: var(--muted-foreground); box-shadow: 0 0 0 2px var(--card);
}
[data-sidebar="collapsed"] .nav-item[data-mention] .navrow-ico::after { background: var(--primary); }
[data-sidebar="collapsed"] .navrow-ico { position: relative; }

/* --- two-pane layout --- */
.cm-wrap {
  display: grid; grid-template-columns: 240px 1fr; gap: 16px;
  height: calc(100vh - var(--topbar-h) - 48px); min-height: 420px;
}

/* --- channel list: de-boxed, mirrors the main sidebar .navrow rows --- */
.cm-channels {
  overflow-y: auto; padding: 0 4px;
  display: flex; flex-direction: column; gap: 2px;
}
.cm-ch-head {
  font: 700 11px/1 Inter; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted-2); padding: 4px 6px 6px;
}
.cm-ch {
  display: flex; align-items: center; gap: 10px; width: 100%;
  height: 40px; padding: 0 9px; border: 0; background: transparent; cursor: pointer;
  border-radius: var(--radius); color: var(--muted-foreground); text-align: left;
  font: 500 13px/1.2 Inter;
}
.cm-ch:hover { background: var(--surface-hover); color: var(--foreground); }
.cm-ch.on { background: var(--surface-hover); color: var(--primary); }
.cm-ch-ico { flex: none; width: 16px; height: 16px; display: grid; place-items: center; color: var(--muted-foreground); }
.cm-ch.on .cm-ch-ico { color: var(--primary); }
.cm-ch-ico svg { width: 16px; height: 16px; }
.cm-ch-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-ch[data-unread] .cm-ch-name { font-weight: 700; color: var(--foreground); }
.cm-ch-badge {
  flex: none; min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  font: 700 11px/1 Inter; border-radius: 9999px;
  background: var(--surface-hover); color: var(--muted-foreground);
}
.cm-ch-badge.mention { background: var(--primary-20); color: var(--primary); }

/* --- the channel pane --- */
.cm-main {
  position: relative;
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--line); border-radius: var(--card-radius);
  background: var(--card); overflow: hidden;
}
.cm-head {
  flex: none; display: flex; align-items: center; gap: 10px;
  height: 48px; padding: 0 16px; border-bottom: 1px solid var(--line);
}
.cm-head-ico { flex: none; display: grid; place-items: center; color: var(--muted-foreground); }
.cm-head-ico svg { width: 17px; height: 17px; }
.cm-head-name { flex: none; font: 700 14px/1 Inter; letter-spacing: -.01em; }
.cm-head-count { flex: none; font: 400 12px/1 Inter; color: var(--muted-2); }
.cm-head-desc {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 400 12px/1 Inter; color: var(--muted-foreground);
  padding-left: 6px; border-left: 1px solid var(--line);
}

/* --- pinned strip --- */
.cm-pins {
  flex: none; border-bottom: 1px solid var(--line);
  background: var(--gold-bg); padding: 6px 12px;
  display: flex; flex-direction: column; gap: 4px; max-height: 128px; overflow-y: auto;
}
.cm-pin {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: none; border: 0; cursor: pointer; text-align: left;
  font: 500 12.5px/1.3 Inter; color: var(--foreground); padding: 3px 2px;
}
.cm-pin-ico { flex: none; color: var(--gold-fg); display: grid; place-items: center; }
.cm-pin-ico svg { width: 14px; height: 14px; }
.cm-pin-who { flex: none; font-weight: 700; }
.cm-pin-body { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--muted-foreground); }
.cm-pin-body a { pointer-events: none; }

/* --- the message stream ---
   The scroll container is .cm-stream; .cm-stream-inner is a flex column pinned
   to the bottom (justify-content:flex-end) so a short channel hugs the composer
   with no whitespace above it, yet overflows and scrolls normally once full. */
.cm-stream { flex: 1; min-height: 0; overflow-y: auto; }
.cm-stream-inner {
  min-height: 100%; max-width: 760px; margin: 0 auto;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 2px;
  padding: 12px 12px 16px;
}
.cm-empty, .cm-loading {
  margin: auto; color: var(--muted-foreground); font: 400 13px/1.4 Inter;
}
.cm-older {
  align-self: center; margin: 4px 0 10px; padding: 6px 14px;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted-foreground);
  border-radius: 9999px; font: 600 12px/1 Inter; cursor: pointer;
}
.cm-older:hover { background: var(--surface-hover); }

/* --- day + unread dividers: a centered label on a 1px line --- */
.cm-day, .cm-unread {
  display: flex; align-items: center; margin: 12px 0 6px; font: 600 11px/1 Inter;
}
.cm-day::before, .cm-day::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.cm-day span { padding: 0 10px; color: var(--muted-2); }
.cm-unread::before, .cm-unread::after { content: ""; flex: 1; height: 1px; background: var(--primary-30); }
.cm-unread span {
  padding: 0 10px; color: var(--primary); text-transform: uppercase; letter-spacing: .04em;
}

/* --- "New messages" jump pill, floating over the stream when scrolled up --- */
.cm-jump {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 78px; z-index: 5;
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px;
  border: none; border-radius: 9999px; cursor: pointer;
  background: var(--primary); color: var(--primary-fg);
  box-shadow: var(--shadow-pop); font: 600 12px/1 Inter;
}
.cm-jump svg { width: 15px; height: 15px; }
.cm-jump:hover { filter: brightness(1.05); }

/* --- a message: a flat left-aligned row, avatar gutter + content column --- */
.cm-msg {
  position: relative; display: flex; flex-direction: row; gap: 10px;
  padding: 3px 8px; border-radius: var(--radius);
}
.cm-msg:hover { background: var(--surface-hover); }
.cm-msg.grouped { padding-top: 1px; padding-bottom: 1px; }
/* Mention wash as an inset left bar, so it coexists with the hover background. */
.cm-msg.mention { box-shadow: inset 2px 0 0 var(--primary), inset 0 0 0 999px var(--primary-08); }
.cm-gutter { flex: none; width: 28px; display: flex; justify-content: center; }
.cm-avatar {
  flex: none; width: 28px; height: 28px; border-radius: 9999px; overflow: hidden;
  display: grid; place-items: center; background: var(--surface-hover);
  font: 700 12px/1 Inter; color: var(--muted-foreground);
}
.cm-avatar img { width: 100%; height: 100%; object-fit: cover; }
.cm-gtime { font: 400 10px/28px Inter; color: var(--muted-2); opacity: 0; transition: opacity .12s; }
.cm-msg:hover .cm-gtime { opacity: 1; }
.cm-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.cm-msg-head { display: flex; align-items: baseline; gap: 8px; }
.cm-author { font: 700 13px/1.2 Inter; }
.cm-tag {
  font: 700 9.5px/1 Inter; letter-spacing: .05em; text-transform: uppercase;
  color: var(--primary); background: var(--primary-12); border-radius: 9999px; padding: 3px 6px;
  align-self: center;
}
.cm-time { font: 400 11px/1 Inter; color: var(--muted-2); }
.cm-deleted { color: var(--muted-2); font-style: italic; font-size: 13px; }
.cm-body { font: 400 14px/1.5 Inter; color: var(--foreground);
  white-space: pre-wrap; word-wrap: break-word; overflow-wrap: anywhere; }
.cm-body a { color: var(--primary); text-decoration: underline; }
.cm-at { color: var(--primary); background: var(--primary-12); border-radius: 4px; padding: 0 3px; font-weight: 600; }
.cm-edited { font: 400 10.5px/1 Inter; color: var(--muted-2); margin-left: 6px; }
.cm-img { display: block; padding: 0; margin: 2px 0; border: 0; background: none; cursor: zoom-in; }
.cm-img img { max-width: 360px; max-height: 240px; width: auto; height: auto;
  border-radius: 8px; display: block; object-fit: cover; }

/* --- full-size image lightbox --- */
.cm-lightbox {
  position: fixed; inset: 0; z-index: 100; background: rgba(0, 0, 0, .8);
  display: grid; place-items: center; padding: 32px;
}
.cm-lightbox img { max-width: 92vw; max-height: 92vh; border-radius: var(--card-radius); box-shadow: var(--shadow-pop); }
.cm-lightbox-x {
  position: fixed; top: 16px; right: 16px; width: 40px; height: 40px;
  display: grid; place-items: center; cursor: pointer;
  border-radius: 9999px; border: 1px solid var(--line); background: var(--card); color: var(--foreground);
}
.cm-lightbox-x:hover { background: var(--surface-hover); }
.cm-lightbox-x svg { width: 18px; height: 18px; }

/* quoted reply */
.cm-quote {
  display: flex; flex-direction: column; gap: 1px; margin-bottom: 6px;
  padding: 4px 8px; border-left: 3px solid var(--primary-30);
  background: var(--surface-hover); border-radius: 0 6px 6px 0;
  font-size: 12px; max-width: 100%;
}
.cm-quote.deleted { border-left-color: var(--line-strong); }
.cm-quote-who { font-weight: 700; color: var(--primary); }
.cm-quote-body { color: var(--muted-foreground); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.cm-quote-body a { pointer-events: none; color: inherit; text-decoration: none; }

/* reactions */
.cm-reacts { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; align-items: center; }
.cm-react {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px;
  border: 1px solid var(--line); background: var(--surface); border-radius: 9999px;
  font: 600 11.5px/1 Inter; color: var(--muted-foreground); cursor: pointer;
}
.cm-react.mine { background: var(--primary-12); border-color: var(--primary-30); color: var(--primary); }
.cm-react-emo { font-size: 13px; }
.cm-react-add {
  display: inline-flex; padding: 3px; border: 1px solid transparent; background: none;
  color: var(--muted-2); border-radius: 9999px; cursor: pointer; opacity: 0; transition: opacity .12s;
}
.cm-react-add svg { width: 15px; height: 15px; }
.cm-msg:hover .cm-react-add { opacity: 1; }
.cm-msg:hover .cm-react-add:hover { color: var(--foreground); background: var(--surface-hover); }

/* hover actions */
.cm-actions {
  position: absolute; top: -12px; right: 8px; display: flex; gap: 2px;
  background: var(--card); border: 1px solid var(--line); border-radius: 9999px;
  padding: 2px; opacity: 0; transition: opacity .12s; box-shadow: var(--shadow-pop);
}
.cm-msg:hover .cm-actions { opacity: 1; }
.cm-actions button {
  display: inline-flex; padding: 5px; border: 0; background: none; cursor: pointer;
  color: var(--muted-foreground); border-radius: 9999px;
}
.cm-actions button:hover { background: var(--surface-hover); color: var(--foreground); }
.cm-actions button.danger:hover { color: var(--red); background: var(--surface-hover); }
.cm-actions svg { width: 15px; height: 15px; }

/* reaction picker popover */
.cm-picker {
  position: fixed; z-index: 60; display: flex; gap: 2px; padding: 5px;
  background: var(--card); border: 1px solid var(--line-strong);
  border-radius: 9999px; box-shadow: var(--shadow-pop);
}
.cm-picker-e {
  border: 0; background: none; cursor: pointer; font-size: 18px; line-height: 1;
  padding: 4px 6px; border-radius: 9999px;
}
.cm-picker-e:hover { background: var(--surface-hover); }

/* --- composer: a pill field holding the input + attach/emoji, send beside it --- */
.cm-composer {
  flex: none; border-top: 1px solid var(--line); padding: 10px 12px 14px;
  background: var(--card);
}
.cm-composer.drag { background: var(--primary-08); }
.cm-composer-row { display: flex; align-items: flex-end; gap: 8px; max-width: 760px; margin: 0 auto; }
.cm-field {
  position: relative; flex: 1; min-width: 0; display: flex; align-items: flex-end;
  border: 1px solid var(--line-strong); background: var(--input-bg);
  border-radius: 20px; padding: 6px 8px 6px 14px;
}
.cm-field:focus-within { border-color: var(--primary-30); box-shadow: 0 0 0 3px var(--ring); }
.cm-input {
  flex: 1; min-width: 0; resize: none; max-height: calc(1.4em * 6);
  border: 0; background: none; color: var(--foreground);
  padding: 4px 0; font: 400 14px/1.4 Inter;
}
.cm-input:focus { outline: none; }
.cm-field-tools { flex: none; display: flex; align-items: center; gap: 2px; padding-bottom: 2px; }
.cm-attach, .cm-emoji {
  width: 30px; height: 30px; display: grid; place-items: center; cursor: pointer;
  border: 0; background: none; color: var(--muted-2); border-radius: 9999px;
}
.cm-attach:hover, .cm-emoji:hover { background: var(--surface-hover); color: var(--foreground); }
.cm-attach svg, .cm-emoji svg { width: 18px; height: 18px; }
.cm-send {
  flex: none; width: 38px; height: 38px; display: grid; place-items: center;
  border-radius: 9999px; cursor: pointer; border: 1px solid var(--primary);
  background: var(--primary); color: var(--primary-fg);
}
.cm-send:hover { filter: brightness(1.05); }
.cm-send svg { width: 17px; height: 17px; }

.cm-reply-strip, .cm-img-strip {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  padding: 6px 10px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 8px; font: 500 12px/1.3 Inter;
}
.cm-reply-ico { color: var(--primary); display: grid; place-items: center; }
.cm-reply-ico svg { width: 14px; height: 14px; }
.cm-reply-who { font-weight: 700; color: var(--primary); flex: none; }
.cm-reply-body { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--muted-foreground); }
.cm-reply-body a { pointer-events: none; color: inherit; text-decoration: none; }
.cm-reply-x, .cm-img-x {
  flex: none; display: inline-flex; padding: 4px; border: 0; background: none;
  color: var(--muted-2); border-radius: 9999px; cursor: pointer;
}
.cm-reply-x:hover, .cm-img-x:hover { background: var(--surface-hover); color: var(--foreground); }
.cm-reply-x svg, .cm-img-x svg { width: 13px; height: 13px; }
.cm-img-thumb { width: 40px; height: 40px; object-fit: cover; border-radius: 6px; }
.cm-img-loading { color: var(--muted-foreground); }

/* --- phone: collapse to one pane at a time --- */
@media (max-width: 720px) {
  .cm-wrap { grid-template-columns: 1fr; height: calc(100vh - var(--topbar-h) - 32px); }
  .cm-channels { max-height: 168px; }
  .cm-stream-inner, .cm-composer-row { max-width: 100%; }
}

/* --- admin: community moderation --- */
.dtable td.dt-num { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted-foreground); }
.dtable tr.is-dim { opacity: .55; }
.dtable td.dt-actions { text-align: right; white-space: nowrap; }
.dtable td.dt-actions .btn-sec { margin-left: 6px; }
.cm-pnl-add { display: flex; gap: 8px; margin-bottom: 16px; }
.cm-pnl-add select {
  flex: 1; min-width: 0; border: 1px solid var(--line-strong); background: var(--input-bg);
  color: var(--foreground); border-radius: var(--radius); padding: 8px 10px; font: 400 13px/1 Inter;
}
.cm-pnl-list { display: flex; flex-direction: column; gap: 2px; }
.cm-pnl-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 4px; border-bottom: 1px solid var(--line);
}
.cm-pnl-row:last-child { border-bottom: 0; }
.cm-pnl-name { font: 500 13px/1.3 Inter; display: flex; align-items: center; gap: 8px; }

/* ============================================================
   CRM - the deal board. Reuses .kcol / .ops-* board geometry; only the
   card internals, the column value sum, the segmented view toggle and the
   Call-Tracking grid are CRM-specific.
   ============================================================ */
.crm-board { }
.crm-colsum { font: 700 12px/1 Inter; color: var(--muted-foreground); padding: 0 4px 8px; }
.crm-cards { display: flex; flex-direction: column; gap: 8px; min-height: 8px; }
.crm-card { padding: 10px 12px; }
.crm-title { font: 600 13.5px/1.35 Inter; color: var(--foreground); }
.crm-contact { font: 400 12px/1.3 Inter; color: var(--muted-foreground); margin-top: 2px; }
.crm-foot { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.crm-val { font: 700 12.5px/1 Inter; color: var(--foreground); }

/* Segmented view toggle (Board / List / Grid) in the top bar. */
.seg { display: inline-flex; background: var(--surface); border: 1px solid var(--line);
  border-radius: 9999px; padding: 2px; }
.seg-btn { border: 0; background: transparent; color: var(--muted-foreground);
  font: 600 12.5px/1 Inter; padding: 6px 13px; border-radius: 9999px; cursor: pointer; }
.seg-btn:hover { color: var(--foreground); }
.seg-btn.on { background: var(--primary); color: var(--primary-fg); }

/* The Call-Tracking grid - dense, every cell an input, like the sheet. */
.crm-grid-wrap { overflow-x: auto; }
.crm-grid td { padding: 3px 4px; }
.crm-grid .grid-sel, .crm-grid .grid-date, .crm-grid .grid-text {
  width: 100%; min-width: 116px; border: 1px solid transparent; background: transparent;
  color: var(--foreground); font: 400 13px/1.3 Inter; padding: 6px 7px; border-radius: var(--radius); }
.crm-grid .grid-sel:hover, .crm-grid .grid-date:hover, .crm-grid .grid-text:hover { background: var(--surface-hover); }
.crm-grid .grid-sel:focus, .crm-grid .grid-date:focus, .crm-grid .grid-text:focus {
  outline: none; border-color: var(--primary); background: var(--card); }
.crm-grid .grid-new { background: var(--surface); }

/* ============================================================
   KPI dashboard - the Sales KPI sheet, computed. A dense read-only grid:
   periods across the top, sections down the side, breakdown rows indented
   under each total, conversion-rate cells tinted against their benchmark.
   ============================================================ */
/* A sheet wider than the window needs a scrollbar you can SEE and a keyboard
   that can reach it. Neither was true: macOS hides overlay scrollbars until a
   trackpad gesture, so on a mouse there was no affordance and nothing to drag,
   and a plain div is not focusable, so the arrow keys had no target. Giving the
   bar an explicit height opts this element out of overlay behaviour, so it is
   always there. The markup carries tabindex, which is what makes the arrows,
   Home and End work. */
/* Deliberately NO `scrollbar-width` here: setting the standard property makes
   Chrome ignore the ::-webkit-scrollbar rules below, and those are what opt this
   element out of overlay scrollbars so the bar is always visible. Firefox draws a
   classic scrollbar regardless, so it loses nothing. */
/* Bounded height, so this element is the SCROLL CONTAINER in both directions.
   It matters for more than tidiness: `position: sticky` resolves against the
   nearest scrolling ancestor, and while this box was as tall as its content the
   header stuck to a container that never scrolled - so it scrolled away with the
   page like any other row. Bounding it puts the header back in charge, and
   brings the horizontal scrollbar to the bottom of the WINDOW instead of leaving
   it thousands of pixels down the page.

   The height is what is LEFT after the furniture, measured off the same
   --topbar-h the shell uses rather than a guessed constant: the page's own top
   padding, the pagination bar under the table, and the trimmed bottom padding
   below. Get this wrong and the page grows a second scrollbar beside the table's
   own, which is the thing this is here to avoid. */
/* Bounded ONLY inside .sheet-view, which is the call sheet: a thousand rows need a
   header that stays put, and that needs a box that scrolls. The Sales KPI sheet is
   about thirty rows and reads as an ordinary page - bounding it there made the
   cycle cards sit below a scrolling box, which looks pinned to the bottom of the
   window and is not what a summary at the end of a page should do. */
.kpi-wrap { overflow-x: auto; padding-bottom: 8px; }
.main.sheet-view .kpi-wrap { overflow: auto; max-height: calc(100vh - var(--topbar-h) - 92px); }

/* This page ends at the table. The shell's 72px bottom padding exists to keep a
   long article clear of the viewport edge; here it just pushed a self-contained,
   already-scrolling box past the bottom and put a second scrollbar on the window. */
.main.sheet-view { padding-bottom: 16px; }
.kpi-wrap::-webkit-scrollbar { height: 10px; }
.kpi-wrap::-webkit-scrollbar-track { background: var(--surface-hover); border-radius: 9999px; }
.kpi-wrap::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 9999px; }
.kpi-wrap::-webkit-scrollbar-thumb:hover { background: var(--muted-2); }
/* NO focus ring. The box carries tabindex so the arrow keys can scroll it, and
   browsers treat a click on a keyboard-scrollable container as keyboard focus -
   so an accent outline drew a permanent green box around the whole sheet the
   moment anybody touched it. The scrollbars are the affordance here. */
.kpi-wrap:focus, .kpi-wrap:focus-visible { outline: none; }

.kpi-table { border-collapse: collapse; font: 400 12.5px/1.3 Inter; min-width: 100%; }
.kpi-table th, .kpi-table td { padding: 5px 10px; text-align: right; white-space: nowrap;
  border-bottom: 1px solid var(--line); }
.kpi-table thead th { position: sticky; top: 0; background: var(--background); z-index: 1;
  color: var(--muted-foreground); font-weight: 600; }
/* THE ROW LABEL COLUMN READS LEFT. It has to be `.kpi-table .kpi-rowhead` and not
   `.kpi-rowhead`: the latter is one class (0,1,0) and loses to `.kpi-table td`
   (0,1,1) above, so every label in the sheet was quietly right-aligned against
   its own numbers - a whole column of text pushed to the wrong edge by a single
   element selector. */
/* The label column pins left while the months scroll past it. It needs a z-index
   as well as a background: without one, every cell later in the row paints OVER
   it, and numbers show through the labels as they slide underneath. */
.kpi-table .kpi-rowhead {
  text-align: left; position: sticky; left: 0; z-index: 1; background: var(--background);
  color: var(--foreground); min-width: 260px; padding-left: 14px;
}
.kpi-table thead th.kpi-rowhead { z-index: 3; }
.kpi-table .kpi-rowhead.sub { padding-left: 30px; color: var(--muted-foreground); font-weight: 400; }

/* A SECTION is a banner across the sheet, on the left where you read it - the
   spreadsheet's coloured bands, in this app's one accent at low opacity. It is
   the thing that makes three stacked tables read as three sections rather than
   one long undifferentiated run of rows. */
.kpi-table .kpi-section td {
  text-align: left; font: 700 12.5px/1.4 Inter; letter-spacing: .01em;
  background: var(--primary-12); color: var(--primary);
  padding: 9px 0; border-bottom: none;
}
/* The band's CELL spans every column, so pinning the cell would pin something
   wider than the window. Pin the text inside it instead - then the heading stays
   beside the labels it belongs to instead of sliding away and leaving a coloured
   stripe with nothing written on it. Same for the rates sub-heading. */
.kpi-table .kpi-section td > span,
.kpi-table .kpi-subhead td > span { position: sticky; left: 0; padding-left: 14px; display: inline-block; }
/* Air above a new section, so the bands separate blocks rather than sit flush
   against the row before them. The first one needs none. */
.kpi-table .kpi-section:not(:first-child) td { border-top: 14px solid var(--background); }

/* A group heading inside a section - the totals line each breakdown hangs off.
   Tinted and ruled above, so the eye can find where one breakdown ends and the
   next begins without counting rows. */
.kpi-table .kpi-total td { font-weight: 700; color: var(--foreground);
  background: var(--surface-hover); border-top: 1px solid var(--line-strong); }
.kpi-table .kpi-total .kpi-rowhead { font-weight: 700; background: var(--surface-hover); }
/* The first total after a band sits right under it and needs no second rule. */
.kpi-table .kpi-section + .kpi-total td { border-top: none; }

.kpi-table .kpi-subhead td { text-align: left; font: 700 11px/1.4 Inter; color: var(--muted-foreground);
  text-transform: uppercase; letter-spacing: .05em; padding: 14px 0 6px; border-bottom: none; }
.kpi-zero { color: var(--muted-foreground); opacity: .45; }
.kpi-bench { color: var(--muted-foreground); font-size: 10.5px; margin-left: 4px; }
.kpi-rate .kpi-good { background: var(--primary-12); color: var(--primary); font-weight: 700; border-radius: 4px; }
.kpi-rate .kpi-warn { background: var(--amber-bg); color: var(--amber); font-weight: 700; border-radius: 4px; }
.kpi-rate .kpi-bad  { background: var(--red-bg); color: var(--red); font-weight: 700; border-radius: 4px; }

/* ---------- the Setter to Closer call sheet ----------

   AFTER the .kpi-table rules on purpose. Both sheets share that table class, and
   `.kpi-table td` and `.call-sheet td` are the same specificity - so whichever
   comes last wins. Placed earlier, every one of these lost silently: the cells
   went on reading right, which on a sheet of names and emails put the text at the
   far end of a 1963px row where you had to scroll to find it.

   A row per call rather than a tally, so the cells read LEFT, like the text they
   hold. The ID column pins while the other nineteen scroll past it. */
.call-sheet th, .call-sheet td { text-align: left; }
/* The ID column holds a four-digit number, not a row label. It inherits
   `min-width: 260px` from `.kpi-table .kpi-rowhead`, which is the right width for
   the Sales sheet's metric names ("Number of discovery calls booked") and absurd
   for "1001" - a quarter of the window spent on four characters, pushing Date and
   the names off to the right. `width: 1%` with the nowrap those cells already
   carry is the shrink-to-fit idiom for an auto-layout table: the column takes what
   its content needs and the freed space goes to the columns holding actual text.
   Sized from content rather than a fixed px so a five or six digit id still fits. */
.call-sheet th.kpi-rowhead, .call-sheet td.kpi-rowhead {
  text-align: left; min-width: 0; width: 1%;
}
/* Lead Score is derived, and looks it: the criteria that feed it sit immediately
   to its right, so the pair reads as one block. Gold is insight, not success. */
.call-sheet th.sc-derived { color: var(--gold-fg); }
[data-theme="light"] .call-sheet th.sc-derived { color: var(--gold); }
.call-sheet th.sc-crit { position: relative; padding-right: 26px; }
.call-sheet th.sc-crit .col-act { position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  opacity: 0; transition: opacity .12s; }
.call-sheet th.sc-crit:hover .col-act, .call-sheet th.sc-crit .col-act:focus-visible { opacity: 1; }
.call-sheet th.sc-add { width: 34px; }

/* ---------- column filters ----------
   The funnel sits in the header next to the column name and stays VISIBLE rather
   than appearing on hover, the way the criterion delete does: a control you have
   to discover by sweeping the mouse across twenty headers is a control nobody
   finds. Muted until it is doing something, then the accent - which is the only
   way to tell, at a glance, why the sheet is showing forty rows out of a
   thousand. */
.th-in { display: flex; align-items: center; gap: 4px; min-width: 0; }
.th-lab { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.col-flt {
  flex: none; display: grid; place-items: center; padding: 0;
  width: 18px; height: 18px; border: 0; border-radius: 9999px;
  background: transparent; color: var(--muted-2); cursor: pointer;
  transition: color .12s, background .12s;
}
.col-flt:hover { color: var(--foreground); background: var(--surface-hover); }
.col-flt .ico { width: 11px; height: 11px; vertical-align: 0; }
.col-flt.on { color: var(--primary); background: var(--primary-12); }
.col-flt.on:hover { background: var(--primary-20); }
/* The criterion headers already spend their right edge on the delete button. */
.call-sheet th.sc-crit .th-in { padding-right: 2px; }

/* The filter popup. Wider than the default .pop, and it scrolls its LIST rather
   than itself, so the search box and the OK button stay put while you scroll a
   few hundred values. */
.pop:has(.flt-pop) { width: 250px; padding: 0; overflow: hidden; max-height: none; }
.flt-pop { display: flex; flex-direction: column; max-height: min(420px, calc(100vh - 40px)); }
.flt-head {
  font: 700 12px/1.3 Inter; color: var(--foreground);
  padding: 11px 12px 9px; border-bottom: 1px solid var(--line);
}
.flt-q { padding: 9px 10px 6px; }
.flt-q input, .flt-range input {
  width: 100%; border-radius: var(--card-radius); border: 1px solid var(--line);
  background: var(--input-bg); color: var(--foreground);
  font: 400 12.5px Inter; padding: 6px 11px;
}
.flt-q input:focus, .flt-range input:focus { outline: none; border-color: var(--primary); }
.flt-acts { display: flex; align-items: center; gap: 6px; padding: 2px 12px 7px; }
.flt-lnk {
  border: 0; background: none; padding: 0; cursor: pointer;
  color: var(--primary); font: 600 11.5px Inter; text-decoration: underline;
}
.flt-lnk:hover { color: var(--foreground); }
.flt-sep { color: var(--muted-2); font: 400 11.5px Inter; }
.flt-count { margin-left: auto; color: var(--muted-foreground); font: 400 11px Inter; }
.flt-list { overflow-y: auto; padding: 0 6px 4px; }
.flt-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 6px;
  border-radius: 9999px; cursor: pointer;
}
.flt-row:hover { background: var(--surface-hover); }
.flt-row[hidden] { display: none; }
.flt-row input { accent-color: var(--primary); flex: none; margin: 0; cursor: pointer; }
.flt-v {
  flex: 1; min-width: 0; font: 400 12.5px Inter; color: var(--foreground);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.flt-blank { color: var(--muted-2); font-style: italic; }
.flt-n { flex: none; color: var(--muted-2); font: 400 11px Inter; }
.flt-range {
  display: grid; grid-template-columns: auto 1fr; gap: 8px 10px;
  align-items: center; padding: 11px 12px 4px;
}
.flt-fl { color: var(--muted-foreground); font: 600 11px Inter; }
.flt-note { margin: 0; padding: 6px 12px 0; color: var(--muted-2); font: 400 11px/1.5 Inter; }
.flt-foot {
  display: flex; align-items: center; gap: 8px; margin-top: 6px;
  padding: 10px 12px 11px; border-top: 1px solid var(--line);
}
.flt-spacer { flex: 1; }
.sheet-foot .flt-lnk { font: 600 12px Inter; }

/* ---------- Sales Cycle Duration ----------
   An average is one number, so it is a card, not a table row. ACROSS rather than
   stacked: the whole reason to split it by segment is to compare them, and a
   column of cards makes you scroll to do that. Wraps on a narrow window rather
   than shrinking the cards past readable. */
.cycle-wrap { padding-top: 18px; }
.cycle-title { font: 800 13px/1.3 Inter; color: var(--foreground); margin-bottom: 10px; }
.cycle-cards { display: flex; flex-wrap: wrap; gap: 12px; }
.cycle-card { flex: 1 1 190px; max-width: 260px; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--card-radius); padding: 12px 14px 14px; }
.cyc-seg { font: 600 11px/1.4 Inter; color: var(--muted-foreground); text-transform: none;
  margin-bottom: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cyc-lab { font: 400 11.5px/1.4 Inter; color: var(--muted-foreground); }
.cyc-num { font: 300 40px/1.1 Inter; color: var(--primary); margin: 2px 0 2px; }
.cyc-sub { font: 400 11px/1.4 Inter; color: var(--muted-2); }
/* The ID column pins left AND the header row pins top, so the two cross at the
   corner - that cell has to outrank both or the other headers slide over it. */
.call-sheet thead th.kpi-rowhead { z-index: 3; }
.call-sheet tbody td.kpi-rowhead { z-index: 1; }

/* Pagination, under the sheet. */
.sheet-foot { display: flex; align-items: center; gap: 10px; padding: 10px 2px 0; }
.sheet-foot .pg-info { color: var(--muted-foreground); font: 400 12.5px Inter; }
.sheet-foot .spacer { flex: 1; }
/* A lookup value is a chip, so the eye groups a thousand rows by colour rather
   than reading every one. Tinted background with a matching text colour, from the
   status tokens - no new palette. */
.tchip { display: inline-block; padding: 1px 8px; border-radius: 9999px; font: 500 11.5px/1.6 Inter;
  white-space: nowrap; max-width: 190px; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; }
.tc-teal   { background: var(--primary-12);   color: var(--primary); }
.tc-blue   { background: var(--blue-bg);      color: var(--blue); }
.tc-purple { background: var(--purple-bg);    color: var(--purple); }
.tc-amber  { background: var(--amber-bg);     color: var(--amber); }
.tc-green  { background: var(--primary-12);   color: var(--primary); }
.tc-red    { background: var(--red-bg);       color: var(--red); }
.tc-gold   { background: var(--gold-bg);      color: var(--gold-fg); }
.tc-slate  { background: var(--surface-hover); color: var(--muted-foreground); }
/* A HAND-PICKED chip colour. The eight above stay the vocabulary a re-theme can
   swap; this is the escape hatch for a label that needs its own accent, and it
   arrives as data on `--chip` rather than as a hardcoded pair of colours. The
   shades are derived here, per theme, with the same recipe the pillar accents
   use - so one hex still reads correctly in both light and dark instead of being
   a fixed colour that goes muddy in one of them. */
.tc-custom { background: color-mix(in srgb, var(--chip) 16%, transparent);
             color:      color-mix(in srgb, var(--chip) 62%, #fff); }
[data-theme="light"] .tc-custom { background: color-mix(in srgb, var(--chip) 18%, #fff);
             color:      color-mix(in srgb, var(--chip) 62%, #000); }
/* The derived total reads as insight, like its header; the parts stay quiet. */
.call-sheet td.sc-total { color: var(--gold-fg); font-weight: 700; }
[data-theme="light"] .call-sheet td.sc-total { color: var(--gold); }
.call-sheet td.sc-val { color: var(--muted-foreground); }
.call-sheet .kpi-empty { color: var(--muted-foreground); padding: 18px 10px; text-align: left; }

/* ============================================================
   Booking admin - a booking page, set up in numbered steps.

   The word "host" is gone from every label here: a page belongs to a PERSON, and
   is named after them. What the screen talks about is "booking pages", "call
   types" and "hours", which is what they are.
   ============================================================ */
.bkp-top { display: flex; align-items: flex-start; gap: 20px; margin-bottom: 18px; }
.bkp-lede { flex: 1; min-width: 0; }
.bkp-lede p { margin: 4px 0 0; max-width: 62ch; color: var(--muted-foreground); font: 400 13px/1.6 Inter; }
.bkp-note { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 16px; font-size: 12.5px; line-height: 1.6; }
.bkp-note .ico { width: 15px; height: 15px; flex: none; margin-top: 2px; }

.bkp-empty { text-align: center; padding: 46px 24px; border: 1px dashed var(--line-strong);
  border-radius: var(--card-radius); background: var(--card); }
.bkp-empty .ico { width: 26px; height: 26px; color: var(--muted-2); }
.bkp-empty h3 { font: 700 15px Inter; margin: 10px 0 6px; color: var(--foreground); }
.bkp-empty p { margin: 0 auto 16px; max-width: 44ch; color: var(--muted-foreground); font: 400 13px/1.6 Inter; }

/* ---------- one booking page, as an accordion ----------
   A finished page is a bar you scroll past; the steps only matter while somebody
   is working through them. The bar carries everything you need at a glance: whose
   page, where it lives, its links, whether it is bookable, and the delete. */
.bkp { margin-bottom: 12px; padding: 0; overflow: hidden; }
.bkp-steps { display: none; }
.bkp.open .bkp-steps { display: block; }
.bkp-head {
  display: flex; align-items: center; gap: 12px; padding: 14px 18px;
  cursor: pointer; user-select: none;
}
.bkp.open .bkp-head { border-bottom: 1px solid var(--line); }
.bkp-head:hover { background: var(--surface); }
.bkp-caret { flex: none; display: grid; place-items: center; width: 18px; height: 18px;
  color: var(--muted-2); transition: transform .14s; }
.bkp-caret .ico { width: 14px; height: 14px; vertical-align: 0; }
.bkp.open .bkp-caret { transform: rotate(90deg); }
/* The call links, on the bar, so the thing you came for is reachable without
   opening anything. Hidden on narrow screens, where the bar has no room. */
.bkp-headlinks { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; min-width: 0; }
.bkp-headlinks a {
  font: 500 11.5px Inter; color: var(--muted-foreground); text-decoration: none;
  border: 1px solid var(--line); border-radius: 9999px; padding: 3px 10px; white-space: nowrap;
}
.bkp-headlinks a:hover { border-color: var(--primary); color: var(--primary); }
@media (max-width: 860px) { .bkp-headlinks { display: none; } }
.bkp-who { flex: 1; min-width: 0; }
.bkp-name { font: 700 16px/1.3 Inter; color: var(--foreground); margin: 0; }
.bkp-where { font: 400 12px Inter; color: var(--muted-2); margin-top: 2px;
  font-family: ui-monospace, monospace; }
/* Whether a stranger can book anything. The one thing this card must answer. */
.bkp-badge { flex: none; display: inline-flex; align-items: center; gap: 5px; padding: 5px 11px;
  border-radius: 9999px; font: 600 11.5px Inter; }
.bkp-badge .ico { width: 12px; height: 12px; vertical-align: 0; }
.bkp-badge.ok { background: var(--primary-12); color: var(--primary); }
.bkp-badge.todo {
  background: var(--red-bg); color: var(--red);
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
}
.bkp-head-del { flex: none; }

/* ---------- the steps ---------- */
.bkp-steps { list-style: none; margin: 0; padding: 0; }
.bkp-step { display: flex; gap: 14px; padding: 18px; border-bottom: 1px solid var(--line); }
.bkp-step:last-child { border-bottom: 0; }
/* The number, or a tick once the step is done. */
.bkp-marker { flex: none; width: 26px; height: 26px; border-radius: 9999px; display: grid;
  place-items: center; font: 700 12.5px Inter; border: 1px solid var(--line-strong);
  color: var(--muted-foreground); background: var(--surface); }
.bkp-marker .ico { width: 13px; height: 13px; vertical-align: 0; }
.bkp-step.done .bkp-marker { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); }
/* An optional step never nags: no number, no tick, just a quiet dot. */
.bkp-step.opt .bkp-marker { background: var(--surface); border-style: dashed;
  border-color: var(--line-strong); color: var(--muted-2); }
.bkp-step.opt.done .bkp-marker { background: var(--primary-12); border-style: solid;
  border-color: transparent; color: var(--primary); }
.bkp-step-body { flex: 1; min-width: 0; }
.bkp-step-head { display: flex; align-items: center; gap: 8px; margin-bottom: 3px; }
.bkp-step-head h4 { font: 700 13.5px/1.4 Inter; color: var(--foreground); margin: 0; }
.bkp-opt { font: 600 10.5px Inter; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted-2); border: 1px solid var(--line); border-radius: 9999px; padding: 2px 7px; }
.bkp-spacer { flex: 1; }
.bkp-state { font: 500 12px Inter; color: var(--muted-foreground); text-align: right; }
.bkp-state.ok { color: var(--primary); }
.bkp-hint { margin: 0 0 12px; color: var(--muted-foreground); font: 400 12.5px/1.6 Inter; max-width: 66ch; }
.bkp-hint.bkp-inline { margin: 0; }
.bkp-blank { margin: 0; color: var(--muted-2); font: 400 12.5px/1.6 Inter; max-width: 66ch; }
.bkp-step-foot { display: flex; align-items: center; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
.bkp-lnk { border: 0; background: none; padding: 0; cursor: pointer; color: var(--primary);
  font: 600 12px Inter; text-decoration: underline; }
.bkp-lnk:hover { color: var(--foreground); }
.bkp-swatch { width: 16px; height: 16px; border-radius: 9999px; background: var(--sw);
  border: 1px solid var(--line); flex: none; }

/* ---------- call types, each with the link it produces ---------- */
.bkp-types { display: flex; flex-direction: column; gap: 8px; }
.bkp-type { display: flex; align-items: center; gap: 14px; padding: 11px 12px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.bkp-type-id { flex: none; min-width: 150px; }
.bkp-type-name { display: block; font: 600 13px Inter; color: var(--foreground); }
.bkp-type-meta { display: flex; align-items: center; gap: 6px; font: 400 11.5px Inter; color: var(--muted-foreground); }
.bkp-dot { width: 3px; height: 3px; border-radius: 50%; background: var(--muted-2); }
/* THE LINK. It is the output of this whole screen, so it reads like a field you
   can take away, not like an afterthought at the end of a row. */
.bkp-link { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px;
  background: var(--card); border: 1px solid var(--line); border-radius: 9999px; padding: 4px 4px 4px 12px; }
.bkp-link code { flex: 1; min-width: 0; font-family: ui-monospace, monospace; font-size: 11.5px;
  color: var(--muted-foreground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bkp-copy { flex: none; display: inline-flex; align-items: center; gap: 5px; border: 0;
  border-radius: 9999px; padding: 5px 11px; cursor: pointer; font: 600 11.5px Inter;
  background: var(--surface-hover); color: var(--foreground); transition: background .12s, color .12s; }
.bkp-copy .ico { width: 12px; height: 12px; vertical-align: 0; }
.bkp-copy:hover { background: var(--primary-12); color: var(--primary); }
.bkp-copy.ok { background: var(--primary); color: var(--primary-fg); }
.bkp-open { flex: none; display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: 9999px; color: var(--muted-foreground); }
.bkp-open .ico { width: 13px; height: 13px; vertical-align: 0; }
.bkp-open:hover { background: var(--surface-hover); color: var(--foreground); }
.bkp-type-del { flex: none; }

/* ---------- time zone, at the foot where it belongs ---------- */
.bkp-tz { display: flex; align-items: center; gap: 10px; padding: 13px 18px;
  border-top: 1px solid var(--line); background: var(--surface); flex-wrap: wrap; }
.bkp-tz-lab { font: 600 12px Inter; color: var(--foreground); }
.bkp-tz select { max-width: 220px; }

/* ---------- the weekly hours ----------
   Each row is a clear on/off day pill plus its hours; an off day hides its times
   and says so. Fixed columns so the time boxes line up down the week rather than
   stepping in and out as day names change width. */
.bk-avail-grid { display: flex; flex-direction: column; gap: 6px; }
.bk-avail-row { display: grid; grid-template-columns: 74px 1fr; align-items: center; gap: 12px; }
.bk-day-toggle { width: 74px; text-align: center; padding: 7px 0; border-radius: 9999px; cursor: pointer;
  font: 700 12.5px Inter; border: 1px solid var(--line); background: var(--surface);
  color: var(--muted-foreground); flex: none; }
.bk-day-toggle:hover { border-color: var(--primary); }
.bk-avail-row.on .bk-day-toggle { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); }
.bk-avail-times { display: flex; align-items: center; gap: 8px; }
.bk-avail-row:not(.on) .bk-avail-times { display: none; }
.bk-avail-to { color: var(--muted-foreground); font: 400 12.5px Inter; }
.bk-avail-off { display: none; font: 400 12.5px Inter; color: var(--muted-2); }
.bk-avail-row:not(.on) .bk-avail-off { display: inline; }
/* Width pinned: `time` is in the global field rule now, which is width:100%, and
   a day's two boxes must stay side by side rather than each filling the row. */
.bk-avail-row input[type="time"] { width: auto; border: 1px solid var(--line);
  background: var(--surface); border-radius: var(--card-radius); padding: 5px 10px;
  font: 400 13px Inter; color: var(--foreground); }

/* ---------- the modals ----------
   Pills, like every other control in this section and like the design guide asks
   for. The app's default field is a 6px box that, on focus, takes an accent
   border AND a 3px ring of --ring on top of it - which on a field this size reads
   as a heavy double outline rather than "you are typing here". One soft ring at
   low opacity does the same job quietly. */
/* width is set HERE rather than left to the global field rule, which is keyed on
   `input[type=text]` - these inputs carry no type attribute, so they never
   matched it and rendered at the browser's default ~170px next to full-width
   selects, which is what made the form look broken rather than merely plain. */
.bkp-in {
  width: 100%; box-sizing: border-box;
  border-color: var(--line); padding: 9px 12px; font-size: 13px;
}
select.bkp-in { padding-right: 34px; }

.bkp-modal-lede { margin: -4px 0 16px; color: var(--muted-foreground); font: 400 13px/1.6 Inter; }
.bkp-fieldhint { display: block; margin-top: 5px; color: var(--muted-2); font: 400 11.5px/1.5 Inter; }
.bkp-err { background: var(--red-bg); color: var(--red); border-radius: var(--radius);
  padding: 9px 12px; font: 400 12.5px Inter; margin-bottom: 12px; }
/* Duration: the five anybody actually picks, plus a box for the rest. */
.bkp-durs { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.bkp-dur { border: 1px solid var(--line); background: var(--surface); color: var(--foreground);
  border-radius: 9999px; padding: 7px 13px; font: 600 12.5px Inter; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s; }
.bkp-dur:hover { border-color: var(--primary); }
.bkp-dur.on { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); }
.bkp-dur-custom { display: inline-flex; align-items: center; gap: 6px; margin-left: 2px;
  color: var(--muted-foreground); font: 400 12px Inter; }
.bkp-dur-custom input { width: 68px; border: 1px solid var(--line); background: var(--surface);
  border-radius: var(--card-radius); padding: 6px 10px; font: 400 12.5px Inter; color: var(--foreground); }

/* ---------- how the booking page looks ---------- */
.bkp-sub { font: 700 13px/1.4 Inter; color: var(--foreground); margin: 18px 0 4px; }
.bkp-sub-hint { margin: 0 0 10px; }
.bkp-logo-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
/* Inheriting is the DEFAULT, so it reads as a normal state rather than a warning.
   The swatches dim while it is on, because they are not what is in force. */
.bkp-inherit { display: flex; align-items: center; gap: 8px; margin-top: 12px;
  font: 400 12.5px Inter; color: var(--muted-foreground); cursor: pointer; }
.bkp-inherit input { accent-color: var(--primary); margin: 0; cursor: pointer; }
.sb-swatches.off { opacity: .45; }
/* The gradient itself, at a size you can judge - a swatch of one flat colour says
   nothing about what four blurred pools of it will look like. */
.bkp-preview {
  margin-top: 16px; height: 96px; border-radius: var(--card-radius); overflow: hidden;
  position: relative; display: grid; place-items: center;
  background:
    radial-gradient(52% 38% at 16% 22%, color-mix(in srgb, var(--sc-accent) 62%, transparent), transparent 70%),
    radial-gradient(46% 42% at 84% 30%, color-mix(in srgb, var(--sc-accent) 44%, transparent), transparent 72%),
    radial-gradient(60% 45% at 28% 88%, color-mix(in srgb, var(--sc-accent) 50%, transparent), transparent 70%),
    radial-gradient(52% 40% at 92% 84%, color-mix(in srgb, var(--sc-accent) 34%, transparent), transparent 72%),
    #0a0e0e;
}
/* A miniature of the real thing: the gradient AND a card on it carrying the
   controls the accent drives. A bare gradient swatch would not have shown that
   picking a colour also moves the calendar and the button. */
.bkp-preview { padding: 14px; place-items: stretch; }
.bkp-prev-card {
  background: var(--card); border-radius: 8px; padding: 10px 12px;
  display: flex; align-items: center; gap: 8px;
}
.bkp-prev-day {
  width: 26px; height: 26px; border-radius: 6px; display: grid; place-items: center;
  background: var(--sc-accent); color: var(--sc-fg, #fff); font: 700 11.5px Inter; flex: none;
}
.bkp-prev-dim {
  width: 26px; height: 26px; border-radius: 6px; flex: none;
  background: color-mix(in srgb, var(--sc-accent) 12%, transparent);
}
.bkp-prev-btn {
  margin-left: auto; padding: 6px 14px; border-radius: 9999px;
  background: var(--sc-accent); color: var(--sc-fg, #fff); font: 700 11px Inter;
}


/* The two the CRM tracks by name. The chip says which, and the lock says why
   there is no delete on that row. */
.bkp-role {
  margin-left: 7px; font: 600 10px Inter; text-transform: uppercase; letter-spacing: .05em;
  color: var(--primary); background: var(--primary-12); border-radius: 9999px; padding: 2px 7px;
  vertical-align: 1px;
}
.bkp-type-fixed { flex: none; display: grid; place-items: center; width: 26px; height: 26px;
  color: var(--muted-2); }
.bkp-type-fixed .ico { width: 12px; height: 12px; vertical-align: 0; }
/* What the copy-hours control just did. It used to do its work in silence. */
.bkp-said { font: 500 12px Inter; color: var(--primary); }

/* ---------- the look editor's footer ----------
   The preview is a full-bleed block; without this the buttons sat flush against
   it with nothing between the two. */
.bkp-preview { margin-bottom: 18px; }

/* ---------- one confirm for the whole section ---------- */
.bkp-confirm { position: fixed; inset: 0; z-index: 900; background: rgba(8,12,12,.5);
  display: grid; place-items: center; padding: 20px; }
.bkp-confirm-box { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--card-radius); padding: 20px 22px; max-width: 430px; width: 100%;
  box-shadow: var(--shadow-pop); }
.bkp-confirm-box h4 { font: 700 15px/1.4 Inter; color: var(--foreground); margin: 0 0 8px; }
.bkp-confirm-body p { font: 400 13px/1.6 Inter; color: var(--muted-foreground); margin: 0 0 9px; }
.bkp-confirm-body p:last-child { margin-bottom: 0; }
.bkp-confirm-acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }

@media (max-width: 720px) {
  .bkp-top { flex-direction: column; gap: 12px; }
  .bkp-type { flex-wrap: wrap; gap: 10px; }
  .bkp-type-id { min-width: 0; flex: 1; }
  .bkp-link { flex: 1 0 100%; order: 3; }
}

/* Narrow screens: stop trying to fit every column on screen. `fit` squeezes
   columns towards their 168px floor, which on a phone means six unreadable
   slivers. Below this width they take a readable width and the board scrolls,
   snapping gently column to column. */
@media (max-width: 900px) {
  .kanban.fit { scroll-snap-type: x proximity; }
  .kanban.fit .kcol { flex: 0 0 264px; min-width: 264px; scroll-snap-align: start; }
}

/* Touch: a finger must be able to pan the board. `touch-action: none` on a card
   claims the whole gesture for the pointer-drag, and cards cover most of the
   board's surface - so on a phone there was almost nowhere left to swipe from,
   and the columns off-screen were genuinely unreachable. On a coarse pointer,
   scrolling wins; dragging a card is a mouse gesture. */
@media (pointer: coarse) {
  .kcard[data-drag] { touch-action: auto; }
  .col-grip { touch-action: none; }   /* the grip still drags: it is a deliberate target */
}

/* ============================================================
   [hidden] MEANS HIDDEN.

   The browser's own `[hidden] { display: none }` lives in the UA stylesheet, and
   ANY rule in this file beats the UA sheet regardless of specificity. So every
   component that sets its own `display` - .wiz-summary, .cm-reply-strip,
   .cm-img-strip, .bulk-bar and friends - silently cancelled the attribute: JS
   set `el.hidden = true`, the element kept its box, and an empty bordered strip
   sat there collecting clicks. That is what was swallowing the region dropdown
   in the Positioning Framework.

   Declared LAST and WITHOUT `!important`, both deliberately. Last, so it wins
   the tie against a class rule of equal weight. Without !important, so a
   considered override at higher specificity still wins - `body.nav-open
   .nav-scrim` is one, and says so where it is written.
   ============================================================ */
[hidden] { display: none; }

/* ============================================================
   REVIEW: the positioning presentations.

   Tokens only, one accent, low opacity. The scales, the pyramid and the flow
   carry meaning that a label/value pair could only state - where on a range a
   client sits, which persona leads, who hurts and what fixes it.
   ============================================================ */

/* A band picked out of an ordered scale. */
/* Capped overall, so it stays a compact chart. Left to fill the column the bars
   drifted apart until the shape stopped reading as one scale. */
.rv-scale { display: flex; align-items: flex-end; gap: 8px; margin: 6px 0 10px; max-width: 340px; }
.rv-bar { flex: 1 1 0; min-width: 0; text-align: center; }
/* Capped, so the bars stay bars. Left to fill an equal share of the column they
   came out as wide flat slabs, which reads as a table of blocks rather than as a
   scale you can see a position on. */
.rv-bar-col {
  width: 100%; max-width: 26px; margin: 0 auto;
  border-radius: 3px 3px 0 0;
  background: var(--primary-08); border: 1px solid var(--line);
  transition: background .18s, border-color .18s;
}
.rv-bar.is-on .rv-bar-col { background: var(--primary); border-color: var(--primary); }
.rv-bar-t {
  margin-top: 6px; font: 500 10.5px/1.3 Inter; color: var(--muted-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rv-bar.is-on .rv-bar-t { color: var(--primary); font-weight: 600; }

/* Needs and anti-needs, told apart by their edge. The red is the MEANING of an
   anti-need - a thing that must not be true - not decoration. */
.rv-needs { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.rv-need { padding: 9px 12px; border-radius: var(--radius); background: var(--surface); border-left: 3px solid var(--line-strong); }
.rv-need.is-yes { border-left-color: var(--primary); }
.rv-need.is-no { border-left-color: var(--red); }
.rv-need-h { font: 600 10.5px/1.3 Inter; letter-spacing: .04em; text-transform: uppercase; color: var(--muted-2); margin-bottom: 4px; }
.rv-need.is-yes .rv-need-h { color: var(--primary); }
.rv-need.is-no .rv-need-h { color: var(--red); }

/* The ranking pyramid, with the cards it ranks beside it. */
.rv-pyr { display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 14px; align-items: stretch; }
/* Centred against the cards, and tall enough to read as a pyramid rather than a
   row of chips. Row-for-row alignment with the cards is not attempted - a card
   grows with what is written in it, and a shape that tracked that would stop
   being a pyramid. */
/* The tiers SHARE the height of the cards they rank, so the pyramid is always as
   tall as the thing beside it however much is written in the cards. Fixed-height
   tiers left a stub next to a full column. */
.rv-pyr-col { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.rv-tier {
  flex: 1 1 0; min-height: 38px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 3px; background: var(--primary); color: var(--primary-fg);
  font: 700 13px/1 Inter; opacity: .92;
}
.rv-tier:nth-child(2) { opacity: .74; }
.rv-tier:nth-child(3) { opacity: .58; }
.rv-tier:nth-child(n+4) { opacity: .44; }
.rv-pyr-cards { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.rv-pcard {
  display: flex; align-items: flex-start; gap: 10px; min-width: 0;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
}
.rv-rank { flex: none; font: 700 11px/1 Inter; color: var(--muted-2); padding-top: 11px; }
.rv-face { flex: none; width: 34px; height: 34px; border-radius: 50%; object-fit: cover; border: 1px solid var(--line); }
.rv-face-ph { display: flex; align-items: center; justify-content: center; background: var(--primary-08); color: var(--primary); }
.rv-face-ph svg { width: 19px; height: 19px; }
.rv-pmain { min-width: 0; }
.rv-pname { font: 600 13px/1.35 Inter; }
.rv-pjob { font: 500 12px/1.4 Inter; color: var(--primary); }
.rv-psays { font: 400 12px/1.5 Inter; color: var(--muted-foreground); margin-top: 3px; }

/* Problem-solution fit, read as the sentence it is. */
.rv-flow {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 10px 12px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
}
.rv-flow-who {
  font: 600 11px/1.4 Inter; color: var(--primary);
  background: var(--primary-08); border-radius: 9999px; padding: 3px 10px;
}
.rv-flow-pain { font: 500 13px/1.45 Inter; }
.rv-flow-arrow { color: var(--muted-2); display: inline-flex; }
.rv-flow-fix { font: 600 13px/1.45 Inter; color: var(--foreground); }
.rv-flow-aware {
  margin-left: auto; font: 500 11px/1.4 Inter; color: var(--muted-foreground);
  border: 1px solid var(--line); border-radius: 9999px; padding: 2px 9px; white-space: nowrap;
}

/* ============================================================
   Call Scripts - the interactive setter call guide (lobby, runner, editor).
   ============================================================ */
.cs-lobby { max-width: 620px; margin: 8px auto; display: flex; flex-direction: column; gap: 16px; }
.cs-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 24px;
}
.cs-eyebrow { font: 700 11px/1 Inter; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-2); margin-bottom: 8px; }
.cs-h { font: 700 20px/1.2 Inter; letter-spacing: -.01em; color: var(--foreground); margin: 0; }
.cs-sub { font: 400 13.5px/1.5 Inter; color: var(--muted-foreground); margin: 6px 0 0; }

.cs-pick { margin-top: 18px; }
.cs-search {
  display: flex; align-items: center; gap: 8px; padding: 0 12px;
  border: 1px solid var(--line-strong); background: var(--input-bg); border-radius: 9999px;
}
.cs-search:focus-within { border-color: var(--primary-30); box-shadow: 0 0 0 3px var(--ring); }
.cs-search svg { width: 16px; height: 16px; color: var(--muted-2); flex: none; }
.cs-search input { flex: 1; border: 0; background: none; outline: none; padding: 10px 0; font: 400 14px/1.2 Inter; color: var(--foreground); }
.cs-results { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; max-height: 240px; overflow-y: auto; }
.cs-result {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 8px 10px; border: 0; background: none; cursor: pointer; border-radius: var(--radius);
}
.cs-result:hover, .cs-result.active { background: var(--surface-hover); }
.cs-result.active { box-shadow: inset 2px 0 0 var(--primary); }
.cs-result-avatar, .cs-picked-avatar {
  flex: none; width: 30px; height: 30px; border-radius: 9999px; display: grid; place-items: center;
  background: var(--surface-hover); font: 700 13px/1 Inter; color: var(--muted-foreground);
}
.cs-result-main { display: flex; flex-direction: column; min-width: 0; }
.cs-result-name { font: 600 13.5px/1.3 Inter; color: var(--foreground); }
.cs-result-sub { font: 400 12px/1.3 Inter; color: var(--muted-foreground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-noresults { font: 400 12.5px/1.4 Inter; color: var(--muted-2); padding: 8px 10px; }
.cs-addnew { margin-top: 8px; display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px;
  border: 0; background: none; cursor: pointer; color: var(--primary); font: 600 12.5px/1 Inter; border-radius: 9999px; }
.cs-addnew:hover { background: var(--surface-hover); }
.cs-addnew svg { width: 15px; height: 15px; }
.cs-newform { margin-top: 8px; padding: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: 8px; }

.cs-picked {
  display: flex; align-items: center; gap: 10px; margin-top: 14px;
  padding: 10px 12px; background: var(--primary-08); border: 1px solid var(--primary-20); border-radius: 10px;
}
.cs-picked-main { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.cs-picked-name { font: 700 13.5px/1.3 Inter; color: var(--foreground); }
.cs-picked-sub { font: 400 12px/1.3 Inter; color: var(--muted-foreground); }
.cs-picked-x {
  flex: none; display: inline-flex; padding: 5px; border: 0; background: none; cursor: pointer;
  color: var(--muted-2); border-radius: 9999px;
}
.cs-picked-x:hover { background: var(--surface-hover); color: var(--foreground); }
.cs-picked-x svg { width: 15px; height: 15px; }
.cs-start { width: 100%; margin-top: 18px; justify-content: center; }

.cs-recent { background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius); padding: 14px 18px; }
.cs-recent-h { font: 700 11px/1 Inter; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-2); margin-bottom: 8px; }
.cs-recent-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-top: 1px solid var(--line); font: 400 13px/1.3 Inter; }
.cs-recent-row:first-of-type { border-top: 0; }
.cs-recent-who { font-weight: 600; color: var(--foreground); }
.cs-recent-out { flex: 1; min-width: 0; color: var(--muted-foreground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-recent-when { color: var(--muted-2); font-size: 12px; flex: none; }

/* score pill: neutral bands, accent for a strong lead (no decorative red/green) */
.cs-score-pill { flex: none; min-width: 24px; text-align: center; padding: 2px 8px; border-radius: 9999px; font: 700 12px/1.4 Inter;
  background: var(--surface-hover); color: var(--muted-foreground); }
.cs-score-pill.mid { background: var(--primary-12); color: var(--primary); }
.cs-score-pill.hi { background: var(--primary); color: var(--primary-fg); }

/* --- runner: one scrolling page of steps, the nav pinned to the top --- */
/* Full-width, per Matt: the card fills the content frame instead of a narrow
   660px column with dead space either side. */
.cs-runner { margin: 8px 0; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--card-radius); padding: 0 28px 24px; }
/* The pinned bar: contact + End call, progress, step counter and Back / Next.
   Sticky under the app topbar, spanning the card edge to edge, with the card's
   own background so the sections scroll away beneath it - the buttons never
   move while the script scrolls. */
.cs-run-head { position: sticky; top: var(--topbar-h); z-index: 5; background: var(--card);
  margin: 0 -28px; padding: 16px 28px 12px; border-bottom: 1px solid var(--line);
  border-radius: var(--card-radius) var(--card-radius) 0 0; }
.cs-run-nav { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 8px; }
.cs-nav-btns { display: flex; gap: 8px; }
.cs-run-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cs-run-who { display: flex; align-items: center; gap: 8px; }
.cs-run-name { font: 700 13.5px/1 Inter; color: var(--foreground); }
.cs-run-cancel { display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; border: 1px solid var(--line);
  background: none; cursor: pointer; color: var(--muted-foreground); border-radius: 9999px; font: 600 12px/1 Inter; }
.cs-run-cancel:hover { background: var(--surface-hover); color: var(--foreground); }
.cs-run-cancel svg { width: 13px; height: 13px; }
.cs-progress { height: 6px; background: var(--surface-hover); border-radius: 9999px; margin: 14px 0 6px; overflow: hidden; }
.cs-progress-fill { height: 100%; background: var(--primary); border-radius: 9999px; transition: width .2s ease; }
.cs-step-meta { font: 600 11px/1 Inter; text-transform: uppercase; letter-spacing: .05em; color: var(--muted-2); }
/* One section per step; a dashed rule separates them so free-scrolling still
   reads as distinct moments of the call. */
.cs-sect { padding: 22px 0 26px; border-bottom: 1px dashed var(--line); }
/* Tabbing into a control must land it BELOW the pinned bar, not underneath it -
   the margin covers the app topbar plus the runner's sticky header. */
.cs-sect input, .cs-sect textarea, .cs-sect select { scroll-margin-top: 200px; }
.cs-sect:last-of-type { border-bottom: 0; padding-bottom: 4px; }
.cs-done-row { display: flex; justify-content: flex-end; margin-top: 20px; }
.cs-note { font: 400 13px/1.5 Inter; color: var(--muted-foreground); margin: 6px 0 14px; }
.cs-items { display: flex; flex-direction: column; gap: 14px; }
.cs-info { padding: 12px 14px; background: var(--surface); border-left: 3px solid var(--primary-30); border-radius: 0 8px 8px 0;
  font: 400 14px/1.55 Inter; color: var(--foreground); white-space: pre-wrap; word-wrap: break-word; overflow-wrap: anywhere; }
.cs-check { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; font: 400 14px/1.4 Inter; color: var(--foreground); }
.cs-check input { margin-top: 1px; width: 16px; height: 16px; accent-color: var(--primary); }

/* --- the closer's briefing: the lead's last recorded call (Fathom) --- */
.cs-brief { margin: 20px 0 0; padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); }
.cs-brief-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.cs-brief-eyebrow { display: inline-flex; align-items: center; gap: 6px; font: 700 11px/1 Inter;
  text-transform: uppercase; letter-spacing: .05em; color: var(--muted-2); }
.cs-brief-eyebrow svg { width: 13px; height: 13px; }
.cs-brief-link { display: inline-flex; align-items: center; gap: 5px; font: 600 12.5px/1 Inter; color: var(--primary); }
.cs-brief-link svg { width: 12px; height: 12px; }
.cs-brief-title { margin-top: 8px; font: 600 13.5px/1.4 Inter; color: var(--foreground); }
.cs-brief-sum { margin-top: 8px; max-height: 260px; overflow-y: auto; font: 400 13px/1.55 Inter;
  color: var(--muted-foreground); white-space: pre-wrap; overflow-wrap: anywhere; }

/* --- call settings dialog (Fathom) --- */
.cs-set-sec h4 { display: flex; align-items: center; gap: 7px; margin: 4px 0 8px; font: 700 14px/1 Inter; color: var(--foreground); }
.cs-set-sec h4 svg { width: 15px; height: 15px; color: var(--primary); }
.cs-set-steps { margin: 0 0 14px; padding-left: 20px; display: flex; flex-direction: column; gap: 7px;
  font: 400 13px/1.5 Inter; color: var(--muted-foreground); }
.cs-set-steps a { color: var(--primary); font-weight: 600; }
.cs-fathom-ok { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 12px; padding: 12px 14px;
  background: var(--primary-12); color: var(--primary); border-radius: 10px; font: 500 13px/1.5 Inter; }
.cs-fathom-ok svg { width: 16px; height: 16px; flex: none; margin-top: 1px; }
.cs-fathom-status { display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
  font: 400 12.5px/1.4 Inter; color: var(--muted-foreground); }
.cs-fathom-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cs-fathom-recent-h { margin: 14px 0 6px; font: 700 11px/1 Inter; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted-2); }
.cs-fathom-recent { max-height: 220px; overflow-y: auto; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: 10px; padding: 4px 12px; background: var(--surface); }
.cs-fathom-rec { display: flex; align-items: baseline; gap: 8px; padding: 7px 0;
  border-bottom: 1px solid var(--line); font: 400 12.5px/1.4 Inter; }
.cs-fathom-rec:last-child { border-bottom: 0; }
.cs-fathom-rec a { color: var(--primary); font-weight: 600; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.cs-fathom-rec-when { flex: none; color: var(--muted-2); font-size: 11.5px; }
.cs-fathom-rec-match { flex: none; margin-left: auto; color: var(--muted-2); font-size: 11.5px; }
.cs-fathom-rec-match.on { color: var(--primary); font-weight: 600; }

/* --- the closer-call scheduler section --- */
.cs-sched-meta { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  font: 600 12.5px/1.4 Inter; color: var(--muted-foreground); margin-bottom: 12px; }
.cs-sched-change { border: 0; background: none; color: var(--primary); font: 600 12px/1 Inter; cursor: pointer; padding: 0; flex: none; }
.cs-sched-pages { display: flex; flex-direction: column; gap: 8px; }
.cs-sched-page { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 10px 14px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface); cursor: pointer; text-align: left; }
.cs-sched-page:hover { border-color: var(--primary); }
.cs-sched-page-name { font: 600 13.5px/1.3 Inter; color: var(--foreground); }
.cs-sched-page-sub { font: 400 12px/1.3 Inter; color: var(--muted-foreground); }
/* The calendar column is capped: on the full-width runner the aspect-ratio
   date cells would otherwise balloon with the card. */
.cs-sched-grid { display: grid; grid-template-columns: minmax(260px, 420px) minmax(0, 1fr); gap: 24px; align-items: start; }
.cs-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cs-cal-title { font: 700 13.5px/1 Inter; color: var(--foreground); }
.cs-cal-nav { width: 28px; height: 28px; display: grid; place-items: center; border: 1px solid var(--line);
  background: var(--surface); border-radius: 8px; cursor: pointer; color: var(--foreground); }
.cs-cal-nav:hover:not([disabled]) { border-color: var(--primary); color: var(--primary); }
.cs-cal-nav[disabled] { opacity: .35; cursor: not-allowed; }
.cs-cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.cs-cal-dow { text-align: center; font: 600 10px/1 Inter; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted-2); padding-bottom: 4px; }
/* min-height: 0 for the same reason as book.html's date cells: aspect-ratio
   must size the square from the track, not from any inherited floor. */
.cs-cal-cell { aspect-ratio: 1; min-height: 0; display: grid; place-items: center; border: 0;
  background: transparent; border-radius: 8px; font: 500 12.5px/1 Inter; color: var(--muted-2); }
.cs-cal-cell.empty { visibility: hidden; }
button.cs-cal-cell.has { cursor: pointer; color: var(--primary); font-weight: 700; background: var(--primary-12); }
button.cs-cal-cell.has:hover { background: color-mix(in srgb, var(--primary) 22%, transparent); }
button.cs-cal-cell.sel { background: var(--primary); color: var(--primary-fg); }
.cs-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); gap: 6px;
  max-height: 260px; overflow-y: auto; align-content: start; }
.cs-slot { border: 1px solid var(--line); background: var(--surface); border-radius: 9999px; padding: 8px 6px;
  font: 600 12.5px/1 Inter; cursor: pointer; color: var(--foreground); text-align: center; }
.cs-slot:hover { border-color: var(--primary); color: var(--primary); }
.cs-slot.sel { background: var(--primary); color: var(--primary-fg); border-color: var(--primary); }
.cs-sched-pick { margin-top: 14px; display: flex; flex-direction: column; gap: 10px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
.cs-sched-when { display: inline-flex; align-items: center; gap: 8px; font: 600 13px/1.3 Inter; color: var(--foreground); }
.cs-sched-when svg { width: 15px; height: 15px; color: var(--primary); }
.cs-sched-pick .btn-primary { align-self: flex-start; }
.cs-sched-done { display: flex; gap: 12px; align-items: flex-start; padding: 14px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
.cs-sched-done-ic { width: 30px; height: 30px; border-radius: 9999px; background: var(--primary-12); color: var(--primary);
  display: grid; place-items: center; flex: none; }
.cs-sched-done-ic svg { width: 15px; height: 15px; }
.cs-sched-done-main { display: flex; flex-direction: column; gap: 2px; font: 400 13px/1.5 Inter; color: var(--muted-foreground); }
.cs-sched-done-main b { color: var(--foreground); font-size: 13.5px; }
.cs-sched-done-main a { color: var(--primary); font-weight: 600; }
@media (max-width: 640px) {
  .cs-sched-grid { grid-template-columns: 1fr; }
  .cs-slots { max-height: 200px; }
}

/* --- scoring screen --- */
.cs-score { display: flex; flex-direction: column; gap: 12px; }
.cs-score-cat .fl { font-weight: 600; }
.cs-pts { font-style: normal; color: var(--primary); font-weight: 700; margin-left: 4px; }
.cs-score-total { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 4px; padding-top: 12px; border-top: 1px solid var(--line); font: 700 14px/1 Inter; color: var(--foreground); }
.cs-outcome { margin-top: 18px; }

/* --- success --- */
.cs-success { text-align: center; }
.cs-success-ic { width: 48px; height: 48px; margin: 0 auto 12px; display: grid; place-items: center;
  border-radius: 9999px; background: var(--primary-12); color: var(--primary); }
.cs-success-ic svg { width: 24px; height: 24px; }
.cs-success-score { display: inline-flex; align-items: center; gap: 10px; margin: 14px 0; padding: 8px 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 9999px; font: 600 13px/1 Inter; color: var(--muted-foreground); }
.cs-success-actions { display: flex; gap: 10px; justify-content: center; margin-top: 16px; }

/* --- the flow editor: the script as a draw.io-style diagram --- */
.main.cs-flow-main { padding-bottom: 24px; }
.cs-flow { display: flex; flex-direction: column; height: calc(100vh - var(--topbar-h) - 48px); min-height: 420px; }
.cs-flow-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.cs-flow-name { flex: 0 1 280px; min-width: 160px; }
.cs-flow-hint { font: 400 12px/1.4 Inter; color: var(--muted-2); }
.cs-flow-body { flex: 1; min-height: 0; display: flex; gap: 14px; }
/* The canvas scroll pane, with a dot grid so it reads as a drawing surface. */
.cs-flow-scroll { flex: 1; min-width: 0; overflow: auto; border: 1px solid var(--line);
  border-radius: var(--card-radius); background: var(--background);
  background-image: radial-gradient(color-mix(in srgb, var(--line) 60%, transparent) 1px, transparent 1px);
  background-size: 22px 22px; }
.cs-flow-canvas { position: relative; min-width: 100%; min-height: 100%; }
.cs-flow-links { position: absolute; inset: 0; pointer-events: none; }
.cs-link { fill: none; stroke: color-mix(in srgb, var(--muted-foreground) 55%, transparent); stroke-width: 1.5; }
.cs-link-head { fill: color-mix(in srgb, var(--muted-foreground) 55%, transparent); }
/* A step box. touch-action: none is what lets a finger drag it. */
.cs-node { position: absolute; width: 260px; box-sizing: border-box; background: var(--card);
  border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px;
  cursor: grab; user-select: none; -webkit-user-select: none; touch-action: none;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--foreground) 8%, transparent); }
.cs-node.dragging { cursor: grabbing; box-shadow: 0 6px 18px color-mix(in srgb, var(--foreground) 14%, transparent); z-index: 3; }
.cs-node.sel { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-12); }
.cs-node-top { display: flex; align-items: center; gap: 8px; }
.cs-node-num { flex: none; width: 20px; height: 20px; border-radius: 9999px; background: var(--primary-12);
  color: var(--primary); display: grid; place-items: center; font: 700 11px/1 Inter; }
.cs-node-title { font: 600 13px/1.3 Inter; color: var(--foreground); overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.cs-node-meta { margin-top: 4px; padding-left: 28px; font: 400 11.5px/1.3 Inter; color: var(--muted-2); }
/* The wrap-up terminus: part of every run, so drawn as fixed (dashed, no grab). */
.cs-node-end { border-style: dashed; cursor: pointer; }
.cs-node-end .cs-node-num { background: var(--surface-hover); color: var(--muted-foreground); }

/* The side panel where a clicked box is edited - half the screen, the flow keeps the left. */
.cs-insp { flex: none; width: 46%; min-width: 340px; overflow-y: auto; border: 1px solid var(--line);
  border-radius: var(--card-radius); background: var(--card); padding: 18px 22px; }
.cs-insp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;
  font: 700 13px/1 Inter; color: var(--foreground); }
.cs-insp-head > div { display: flex; align-items: center; gap: 6px; }
.cs-insp-sec { display: flex; align-items: center; justify-content: space-between; margin: 16px 0 8px;
  font: 700 11px/1 Inter; text-transform: uppercase; letter-spacing: .05em; color: var(--muted-2); }
.cs-insp-items, .cs-insp-cats { display: flex; flex-direction: column; gap: 8px; }
.cs-insp-item, .cs-insp-cat { border: 1px solid var(--line); border-radius: 10px; padding: 12px;
  background: var(--surface); display: flex; flex-direction: column; gap: 8px; }
.cs-insp-item-top { display: flex; align-items: center; gap: 4px; }
.cs-insp-item-top select { flex: 1; min-width: 0; }
.cs-insp-ilabel { width: 100%; box-sizing: border-box; resize: vertical; min-height: 120px;
  font: 400 14px/1.5 Inter; }
.cs-insp-item .cs-insp-ilabel[rows="2"] { min-height: 56px; }
.cs-insp-hint { margin: 0; font: 400 12.5px/1.5 Inter; color: var(--muted-foreground); }
.cs-insp-hint svg { width: 14px; height: 14px; vertical-align: -2px; margin-right: 2px; }
.cs-insp-cat-top { display: flex; align-items: center; gap: 4px; }
.cs-insp-cat-top input { flex: 1; min-width: 0; }
.cs-insp-cat-move { display: flex; align-items: center; gap: 4px; }
.cs-insp-opts { display: flex; flex-direction: column; gap: 6px; }
.cs-insp-opt { display: flex; align-items: center; gap: 4px; }
.cs-insp-opt input[type="text"] { flex: 1; min-width: 0; }
.cs-insp-opt input[type="number"], .cs-insp-cpts { width: 64px; flex: none; }
.cs-insp-bonus { display: flex; align-items: center; gap: 8px; font: 500 13px/1 Inter; color: var(--muted-foreground); }

@media (max-width: 900px) {
  .cs-flow { height: auto; }
  .cs-flow-body { flex-direction: column; }
  .cs-flow-scroll { height: 55vh; }
  .cs-insp { width: auto; max-height: 45vh; }
}

/* ============================================================
   The sparring coach - the wizard's third column.

   It is a COLUMN, not an overlay: the point of the panel is that you can
   read the question you are being asked and the field you are answering at
   the same time, so it takes width from the wizard rather than covering it.
   Under 1100px there is no width to take and it becomes a sheet.

   Tokens only, and the accent stays where the accent always is: the send
   button and the draft card's rule. The coach's own bubbles are surface,
   not brand - a wall of tinted panels would make the one thing that IS
   actionable (a draft) stop reading as actionable.
   ============================================================ */
.coach {
  width: 380px; flex: none; align-self: stretch; min-height: 0;
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--card-radius);
  background: var(--card); overflow: hidden;
}
/* The panel mounts hidden and stays that way until the topbar button opens it.
   Our display: flex above would silently beat the hidden attribute (any author
   display rule does), leaving an empty white card on the right of every wizard
   - so restate what hidden means. */
.coach[hidden] { display: none; }
/* The wizard's own gap is 44px, which is a lot of dead air next to a panel. */
.coach-open .wiz { gap: 28px; }

.coach-head {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 12px 12px 12px 15px; border-bottom: 1px solid var(--line);
}
.coach-h {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px;
  font: 600 13px/1 Inter; color: var(--foreground);
}
.coach-h .ico { color: var(--primary); }
.coach-head-actions { display: flex; align-items: center; gap: 2px; }
.coach-icon {
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  border: none; background: none; border-radius: var(--radius);
  color: var(--muted-foreground); cursor: pointer; transition: background .12s, color .12s;
}
.coach-icon:hover { background: var(--surface-hover); color: var(--foreground); }
.coach-icon .ico { width: 15px; height: 15px; }

.coach-log {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 16px 15px; display: flex; flex-direction: column; gap: 12px;
}

/* Their turn is the one that stands out - they are the one doing the work. */
.coach-msg { max-width: 100%; border-radius: 10px; font: 400 13.5px/1.55 Inter; }
.coach-msg p { margin: 0 0 8px; }
.coach-msg p:last-child { margin-bottom: 0; }
.coach-msg.is-me {
  align-self: flex-end; max-width: 88%;
  background: var(--primary-08); border: 1px solid var(--primary-20);
  color: var(--foreground); padding: 9px 12px;
}
.coach-msg.is-coach { color: var(--foreground); padding: 0 2px; }
/* Sent while the coach was mid-turn: on screen at once, a shade quieter until
   it actually goes out as the next turn. */
.coach-msg.is-queued { opacity: .65; }

.coach-empty {
  margin: auto 0; text-align: center; color: var(--muted-2);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.coach-empty .ico { width: 22px; height: 22px; }
.coach-empty p { margin: 0; font: 400 13px/1.5 Inter; max-width: 220px; }

/* Before activation: the mascot, one line, one big button, all centred in the
   space the conversation will take over. */
.coach-activate {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 24px 20px; text-align: center;
}
.coach-activate p { margin: 0; font: 400 13px/1.55 Inter; color: var(--muted-foreground); max-width: 260px; }
.coach-activate-btn { height: 40px; padding: 0 20px; font-size: 13.5px; }

/* The mascot. Line art in the muted ink; the brain wears the site accent and
   holds still - only the eyes blink. The eye whites are FILLED with the card
   colour so the wire reads as passing behind them, not through them. */
.coach-mascot { width: 80px; height: 118px; color: var(--muted-2); }
.coach-mascot-eye { fill: var(--card); }
.coach-mascot-brain { color: var(--primary); }
.coach-mascot-eyes {
  transform-box: fill-box; transform-origin: center;
  animation: coachBlink 4.2s infinite;
}
@keyframes coachBlink { 0%, 90%, 96%, 100% { transform: scaleY(1); } 92%, 94% { transform: scaleY(.08); } }
@media (prefers-reduced-motion: reduce) {
  .coach-mascot-eyes { animation: none; }
}

/* Three dots while it thinks. Opus thinks before it speaks, so this is on
   screen for a second or two before the first token - long enough that its
   absence reads as a broken panel. */
.coach-typing { display: flex; gap: 4px; padding: 4px 2px; }
/* Same trap as .coach itself: an authored display rule overrides the hidden
   attribute, so the dots showed even when the logic hid them. */
.coach-typing[hidden] { display: none; }
.coach-typing span {
  width: 5px; height: 5px; border-radius: 50%; background: var(--muted-2);
  animation: coachDot 1.1s infinite ease-in-out;
}
.coach-typing span:nth-child(2) { animation-delay: .18s; }
.coach-typing span:nth-child(3) { animation-delay: .36s; }
@keyframes coachDot { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

/* ---------- a draft ----------
   The one thing in the panel you can act on, so it is the one thing with a
   frame and an accent rule. The value sits in a real textarea because the
   promise made to the user is that they can change it before they take it. */
.coach-draft {
  border: 1px solid var(--line); border-left: 2px solid var(--primary);
  border-radius: var(--card-radius); background: var(--surface);
  padding: 11px 12px; transition: opacity .18s, transform .18s;
}
.coach-draft.is-gone { opacity: 0; transform: translateY(-4px); }
.coach-draft-h { display: flex; align-items: baseline; gap: 7px; margin-bottom: 8px; }
.coach-draft-h .ico { width: 13px; height: 13px; flex: none; color: var(--primary); align-self: center; }
.coach-draft-f { font: 600 12.5px/1.3 Inter; color: var(--foreground); }
.coach-draft-s { font: 500 11px/1.3 Inter; color: var(--muted-2); margin-left: auto; white-space: nowrap; }
.coach-draft-v {
  width: 100%; resize: vertical; min-height: 62px;
  font: 400 13px/1.5 Inter; color: var(--foreground);
  background: var(--input-bg); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 8px 10px;
}
.coach-draft-v:focus { outline: none; border-color: var(--primary-30); box-shadow: 0 0 0 3px var(--ring); }
.coach-draft-why { margin-top: 7px; font: 400 11.5px/1.45 Inter; color: var(--muted-foreground); }
.coach-draft-actions { margin-top: 10px; display: flex; align-items: center; gap: 10px; }
.coach-draft-done {
  display: inline-flex; align-items: center; gap: 5px;
  font: 600 12px/1 Inter; color: var(--primary);
}
.coach-draft.is-used { border-left-color: var(--line-strong); opacity: .8; }
.coach-draft.is-used .coach-draft-v { background: var(--surface); color: var(--muted-foreground); }
.coach-link {
  border: none; background: none; padding: 0; cursor: pointer;
  font: 500 12px/1 Inter; color: var(--muted-foreground); text-decoration: underline;
  text-underline-offset: 2px;
}
.coach-link:hover { color: var(--foreground); }

/* ---------- a jump proposal ----------
   The coach suspects the signal lives on another screen and offers the walk.
   Same card language as a draft, minus the textarea - there is nothing to
   edit, only a door to open or leave shut. */
.coach-jump {
  border: 1px solid var(--line); border-left: 2px solid var(--primary);
  border-radius: var(--card-radius); background: var(--surface);
  padding: 11px 12px; transition: opacity .18s, transform .18s;
}
.coach-jump.is-gone { opacity: 0; transform: translateY(-4px); }
.coach-jump-why { font: 400 11.5px/1.45 Inter; color: var(--muted-foreground); }
.coach-jump.is-used { border-left-color: var(--line-strong); opacity: .8; }

/* ---------- composer ---------- */
.coach-compose {
  flex: none; display: flex; align-items: flex-end; gap: 8px;
  padding: 10px 12px; border-top: 1px solid var(--line);
}
.coach-compose textarea {
  flex: 1; min-width: 0; resize: none; max-height: 160px;
  font: 400 13.5px/1.5 Inter; color: var(--foreground);
  background: var(--input-bg); border: 1px solid var(--line); border-radius: 10px;
  padding: 9px 12px;
}
.coach-compose textarea:focus { outline: none; border-color: var(--primary-30); box-shadow: 0 0 0 3px var(--ring); }
.coach-send {
  flex: none; width: 34px; height: 34px; border-radius: 9999px; border: none;
  background: var(--primary); color: var(--primary-fg);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  transition: opacity .12s;
}
.coach-send:hover { opacity: .88; }
.coach-send:disabled { opacity: .4; cursor: default; }
.coach-send .ico { width: 15px; height: 15px; }
.coach-foot {
  flex: none; padding: 0 14px 11px; font: 400 11px/1.45 Inter; color: var(--muted-2);
}

/* Narrow: no width left to give it, so it becomes a sheet over the wizard. */
@media (max-width: 1100px) {
  .coach {
    position: fixed; z-index: 60; top: var(--topbar-h); right: 0; bottom: 0;
    width: min(420px, 100%); border-radius: 0; border-right: none;
    box-shadow: var(--shadow-pop);
  }
  .coach-open .wiz { gap: 44px; }
}

/* A turn that did not land. Red is status, never decoration - and the copy
   carries the recovery, because their words go back into the composer. */
.coach-error {
  display: flex; align-items: flex-start; gap: 7px;
  border: 1px solid var(--red); background: var(--red-bg); border-radius: var(--radius);
  padding: 9px 11px; font: 400 12.5px/1.5 Inter; color: var(--foreground);
}
.coach-error .ico { flex: none; color: var(--red); margin-top: 2px; }

/* ---------- the call sheet's vocabulary editor ----------

   Five lists of labels, edited together and saved once. Big popup, because the
   accounts list alone runs past forty values and a small one would be a scroll
   with no context. */

/* The way in. Deliberately a primary button rather than the quiet icon buttons in
   the sheet header: this is the only route to editing the vocabulary, and an
   affordance nobody finds is the same as one that does not exist. */
.btn-edit-vocab { display: inline-flex; align-items: center; gap: 7px; }
.btn-edit-vocab svg { width: 15px; height: 15px; }

.modal.vocab-modal { width: min(920px, 94vw); max-height: 88vh; display: flex; flex-direction: column;
  position: relative; }
.vocab-modal .modal-scroll { flex: 1 1 auto; overflow-y: auto; }
.vocab-intro { font: 400 12.5px/1.5 Inter; color: var(--muted-foreground); margin: 0 0 16px; }

.vocab-sec { margin-bottom: 22px; }
.vocab-sec h4 { font: 700 12px/1.4 Inter; color: var(--muted-foreground); text-transform: uppercase;
  letter-spacing: .04em; margin: 0 0 8px; }
.vocab-rows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }

/* One row: colour, name, what it will look like, whether it is in use, and the
   delete. The preview sits between the inputs and the verdict because it is the
   only way to judge a hex before saving it. */
.vocab-row { display: grid; grid-template-columns: 34px minmax(120px, 1fr) minmax(90px, 190px) auto 30px;
  align-items: center; gap: 10px; }
.vocab-color { width: 30px; height: 30px; padding: 0; border: 1px solid var(--line);
  border-radius: 9999px; background: none; cursor: pointer; overflow: hidden; }
.vocab-color::-webkit-color-swatch-wrapper { padding: 2px; }
.vocab-color::-webkit-color-swatch { border: none; border-radius: 9999px; }
.vocab-color::-moz-color-swatch { border: none; border-radius: 9999px; }
.vocab-name { width: 100%; padding: 6px 12px; border: 1px solid var(--line); border-radius: 9999px;
  background: var(--background); color: var(--foreground); font: 400 12.5px/1.4 Inter; }
.vocab-name:focus-visible { outline: none; border-color: var(--primary); }
.vocab-preview { overflow: hidden; }
.vocab-used { font: 400 11px/1.4 Inter; color: var(--muted-2); white-space: nowrap; text-align: right; }

.vocab-del { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 9999px;
  border: none; background: none; color: var(--muted-foreground); cursor: pointer; transition: background .12s; }
.vocab-del:hover:not(:disabled) { background: var(--red-bg); color: var(--red); }
.vocab-del svg { width: 15px; height: 15px; }
/* A label already on a call is not deletable. Dimmed rather than hidden, with the
   count beside it saying why - a control that vanishes reads as a bug, and the
   reason is the part somebody actually needs. */
.vocab-del:disabled { opacity: .3; cursor: not-allowed; }

/* People rows carry the two role toggles between the preview and the usage
   count - which of the call form's Setter / Closer dropdowns offer this person. */
.vocab-row-person { grid-template-columns: 34px minmax(120px, 1fr) minmax(90px, 190px) auto auto 30px; }
.vocab-roles { display: flex; gap: 6px; }
.vocab-role { padding: 4px 12px; border: 1px solid var(--line); border-radius: 9999px;
  background: none; color: var(--muted-2); font: 600 11px/1.4 Inter; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s; }
.vocab-role:hover { border-color: var(--line-strong); color: var(--muted-foreground); }
.vocab-role.on { background: var(--primary-12); border-color: var(--primary-30); color: var(--primary); }

/* Who a NEW call starts preselected with, one per role. Rebuilt from the rows
   above as toggles change, so they only ever offer people actually in the role. */
.vocab-defaults { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 14px; }
.vocab-defaults label { display: flex; align-items: center; gap: 8px; }
.vocab-defaults label span { font: 600 11.5px/1.4 Inter; color: var(--muted-foreground); white-space: nowrap; }
.vocab-defaults select { padding: 6px 28px 6px 12px; border: 1px solid var(--line); border-radius: 9999px;
  background: var(--input-bg); color: var(--foreground); font: 400 12.5px/1.4 Inter; }
.vocab-defaults select:disabled { opacity: .4; cursor: not-allowed; }
.vocab-defaults-hint { margin: 6px 0 0; }

.vocab-err { margin: 0 var(--modal-pad, 20px); padding: 9px 13px; border-radius: var(--card-radius);
  background: var(--red-bg); color: var(--red); font: 500 12.5px/1.45 Inter; }

/* The discard confirmation, layered over this popup rather than replacing it.
   Covers the modal only, so the popup is plainly still there underneath and the
   question reads as being about it. */
.vocab-confirm { position: absolute; inset: 0; z-index: 5; display: grid; place-items: center;
  background: color-mix(in srgb, var(--background) 72%, transparent); border-radius: inherit; }
.vocab-confirm-box { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--card-radius); padding: 22px 24px; width: min(400px, 88%); text-align: center; }
.vocab-confirm-box h4 { font: 700 15px/1.3 Inter; color: var(--foreground); margin: 0 0 8px; }
.vocab-confirm-box p { font: 400 12.5px/1.5 Inter; color: var(--muted-foreground); margin: 0 0 18px; }
.vocab-confirm-acts { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

/* The two buttons sit at opposite ends: Discard is a way out, Save is the point,
   and pushing them apart makes the destructive one harder to hit by momentum.
   `width: auto` because .btn-primary is full-width by default in this design
   system - it is normally a form's submit - and here it is one of a pair. */
.vocab-modal .modal-foot.vocab-foot { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; }
.vocab-modal .modal-foot.vocab-foot .btn-primary,
.vocab-modal .modal-foot.vocab-foot .btn-sec { width: auto; min-width: 140px; white-space: nowrap; }

/* ---------- one call, as a form ----------
   Twenty columns will not hold seven dropdowns and a comments box, so a row opens
   into this. Two columns on a wide window, one when there is no room. */
.modal.call-row-modal { width: min(760px, 94vw); max-height: 88vh; display: flex;
  flex-direction: column; position: relative; }
.call-row-modal .modal-scroll { flex: 1 1 auto; overflow-y: auto; }

.cr-sec { margin-bottom: 20px; }
.cr-sec h4 { font: 700 12px/1.4 Inter; color: var(--muted-foreground); text-transform: uppercase;
  letter-spacing: .04em; margin: 0 0 10px; }
.cr-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px 16px; }

.cr-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.cr-field > span { font: 500 11.5px/1.4 Inter; color: var(--muted-foreground); }
.cr-field input, .cr-field select, .cr-comments {
  width: 100%; padding: 7px 11px; border: 1px solid var(--line); border-radius: var(--card-radius);
  background: var(--background); color: var(--foreground); font: 400 12.5px/1.4 Inter; }
.cr-field input:focus-visible, .cr-field select:focus-visible, .cr-comments:focus-visible {
  outline: none; border-color: var(--primary); }
/* A textarea is the one control that cannot be a pill - a rounded multi-line box
   crops its own last line. Card radius instead, which is the other shape here. */
.cr-comments { border-radius: var(--card-radius); resize: vertical; min-height: 62px; }
.cr-hint { font: 400 11.5px/1.5 Inter; color: var(--muted-2); margin: 9px 0 0; }

.cr-err { margin: 0 20px; padding: 9px 13px; border-radius: var(--card-radius);
  background: var(--red-bg); color: var(--red); font: 500 12.5px/1.45 Inter; }

/* Delete sits far from Save, in the opposite corner. The two destructive-adjacent
   controls should never be a mis-click apart. */
.call-row-modal .modal-foot.cr-foot { display: flex; align-items: center;
  justify-content: space-between; gap: 12px; }
.cr-foot-right { display: flex; align-items: center; gap: 10px; }
.call-row-modal .modal-foot .btn-primary,
.call-row-modal .modal-foot .btn-sec { width: auto; min-width: 108px; white-space: nowrap; }
.cr-del { display: inline-flex; align-items: center; gap: 7px; color: var(--red); }
.cr-del:hover { background: var(--red-bg); }
.cr-del svg { width: 14px; height: 14px; }

/* The grid rows are now openable, so they have to look it. */
.call-sheet tbody tr.call-row { cursor: pointer; }
.call-sheet tbody tr.call-row:hover td { background: var(--surface-hover); }
.call-sheet tbody tr.call-row:hover td.kpi-rowhead { background: var(--surface-hover); }
.call-sheet tbody tr.call-row:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

/* --- the wizard's Introduction screen (the template's page 3) --- */
/* One reading measure for the whole Introduction - the full stage. The
   paragraph and the tip boxes end on the same right edge; anything narrower
   read as dead space and forced scrolling. */
.wiz-measure, .wiz-welcome { max-width: none; }
.buzz-row { display: inline-flex; flex-wrap: wrap; gap: 6px; margin: 8px 8px 8px 0; vertical-align: middle; }
.buzz-chip { padding: 2px 10px; border: 1px solid var(--line); border-radius: 999px; font: 600 12px/1.7 Inter; color: var(--muted-foreground); background: var(--surface); }

/* --- prose screens: a section's introduction cell / a field's explainer,
       rendered as their own step before the fields they frame --- */
.wiz-prose { max-width: none; }
.wiz-prose p { font: 400 14.5px/1.7 Inter; color: var(--foreground); margin: 0 0 14px; }
.wiz-prose p:last-child { margin-bottom: 0; }
.wiz-prose .wiz-quote { font-style: italic; padding-left: 14px; border-left: 3px solid var(--line-strong); }
/* The phrases that name the axes drawing's lines, in the line's own ink. */
.ink-tech { color: var(--amber); }
.ink-region { color: var(--red); }
.ink-niche { color: var(--blue); }
/* The value-chain roles, marked highlighter-style in their triangle's fill.
   Text sits in --background: every role token is dark where the page is
   light and light where it is dark, so the contrast holds in both themes. */
.ink-chain { padding: 0 7px; border-radius: 6px; color: var(--background); white-space: nowrap; }
.ink-chain-you { background: var(--foreground); }
.ink-chain-client { background: var(--primary); color: var(--primary-fg); }
.ink-chain-cc { background: var(--amber); }
.ink-chain-consumer { background: var(--blue); }

/* --- the industry brainstorming tree (icp.industry) --- */
.ind-top { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.ind-in { flex: 1; min-width: 260px; }
.ind-legend { display: flex; gap: 14px; font: 500 12px/1 Inter; color: var(--muted-foreground); }
.ind-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.ind-key { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }
/* Node keys, shared by both boards so a colour means the same thing on each:
   picked is yours, gold is the sparring partner thinking out loud, faded and
   struck through is what the conversation ruled out. */
.bd-key { width: 12px; height: 12px; border-radius: 4px; display: inline-block; box-sizing: border-box; }
.bd-key-pick { border: 1.5px solid var(--primary); background: var(--primary-12); }
.bd-key-sketch { border: 1.5px dotted var(--gold-border); background: var(--gold-bg); }
.bd-key-dim { border: 1.5px solid var(--line-strong); background: var(--surface); opacity: .4; }
/* A box being moved leads with the pointer, and nothing under it should
   highlight or select while it travels. */
.ind-canvas.is-dragging { cursor: grabbing; user-select: none; }
.ind-canvas.is-dragging .ind-node, .ind-canvas.is-dragging .vc-node { cursor: grabbing; }
.ind-key-sim { background: var(--amber); }
.ind-key-sub { background: var(--blue); }
.ind-canvas {
  position: relative; height: 520px; overflow: hidden; cursor: grab;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  background-image: radial-gradient(color-mix(in srgb, var(--line-strong) 55%, transparent) 1px, transparent 1px);
  background-size: 22px 22px; touch-action: none;
}
.ind-canvas.is-panning { cursor: grabbing; }
.ind-canvas.is-full {
  position: fixed; inset: 14px; height: auto; z-index: 500;
  box-shadow: 0 18px 60px rgb(0 0 0 / 28%);
}
.ind-world { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
.ind-edges { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
.ind-e { fill: none; stroke-width: 1.6; opacity: .5; }
.ind-e-sub { stroke: var(--blue); }
.ind-e-sim { stroke: var(--amber); }
.ind-node {
  position: absolute; display: flex; align-items: center; gap: 2px;
  min-height: 34px; padding: 4px 6px; box-sizing: border-box; border-radius: 10px;
  background: var(--surface); border: 1.5px dashed var(--line-strong);
}
.ind-node .ind-node-t {
  flex: 1; min-width: 0; border: 0; background: none; padding: 2px 4px; text-align: left;
  font: 500 12.5px/1.25 Inter; color: var(--muted-foreground); cursor: pointer;
}
.ind-sub:not(.is-picked) { border-color: color-mix(in srgb, var(--blue) 45%, transparent); }
.ind-sim:not(.is-picked) { border-color: color-mix(in srgb, var(--amber) 50%, transparent); }
/* Two lines of label, never three. The layout gives every row 46px and anchors
   its edges 17px down, so a third line would sit on top of the node below -
   which is exactly what a long drafted industry used to do. */
.ind-node .ind-node-t {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}
.ind-node.is-picked { border-style: solid; border-color: var(--primary); background: var(--primary-12); }
.ind-node.is-picked .ind-node-t { color: var(--foreground); font-weight: 600; cursor: default; }
/* The sparring partner's own sketch: GOLD, the house colour for knowledge
   offered. It is thinking made visible, not an answer - dotted, because you
   have not picked it and nothing here is in your framework yet. */
.ind-node.is-sketch { border-style: dotted; border-color: var(--gold-border); background: var(--gold-bg); }
.ind-node.is-sketch .ind-node-t { color: var(--gold-fg); }
/* The live hypothesis, and the branches the conversation has ruled out. */
.ind-node.is-focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }
.ind-node.is-dim { opacity: .34; }
.ind-node.is-dim .ind-node-t { text-decoration: line-through; }
.ind-node-b {
  flex: none; width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 7px; background: none; color: var(--muted-foreground); cursor: pointer;
}
.ind-node-b:hover { background: var(--primary-12); color: var(--primary); }
.ind-node-x:hover { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--red); }
.ind-dots { display: inline-flex; gap: 3px; padding: 0 5px; }
.ind-dots span { width: 4px; height: 4px; border-radius: 50%; background: var(--muted-2); animation: coachDot 1.1s infinite ease-in-out; }
.ind-dots span:nth-child(2) { animation-delay: .18s; }
.ind-dots span:nth-child(3) { animation-delay: .36s; }
.ind-zoom { position: absolute; right: 12px; top: 12px; display: flex; flex-direction: column; gap: 4px; }
.ind-zoom button {
  min-width: 30px; height: 30px; padding: 0 6px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--foreground); cursor: pointer;
  font: 600 11px/1 Inter; display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 2px rgb(0 0 0 / 6%);
}
.ind-zoom button:hover { border-color: var(--line-strong); }
.ind-empty, .ind-error {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font: 500 13px/1.5 Inter; color: var(--muted-foreground); pointer-events: none;
  text-align: center; max-width: 340px;
}
.ind-error { top: auto; bottom: 14px; transform: translateX(-50%); color: var(--red); background: var(--surface); border: 1px solid var(--line); border-radius: 8px; padding: 6px 12px; }
.ind-picked { margin-top: 10px; }

/* The city-dot hover tag on the region map - pointer-events off so it never
   swallows the click it is labelling. */
.pos-map-tip {
  position: absolute; z-index: 5; pointer-events: none;
  padding: 3px 9px; border-radius: 6px;
  background: var(--card); border: 1px solid var(--line);
  font: 600 12px/1.5 Inter; color: var(--foreground);
}

/* The shortcut pill worn ON a filled button (the wizard's Continue, the call
   runner's Next) - the standard .kbd colors vanish on a primary background,
   so this variant is cut from the button's own foreground, translucent. */
.btn-green .kbd-btn, .btn-primary .kbd-btn {
  background: color-mix(in srgb, var(--primary-fg) 16%, transparent);
  border-color: color-mix(in srgb, var(--primary-fg) 38%, transparent);
  color: var(--primary-fg); margin-left: 2px;
}

/* --- the positioning launcher board: Create-new card + a card per version --- */
.vb-new {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  margin: 0; cursor: pointer; min-height: 150px;
  border-style: dashed; background: var(--surface); text-align: center;
}
.vb-new:hover { border-color: var(--primary); }
.vb-new:hover .vb-new-plus { color: var(--primary); }
.vb-new-plus { color: var(--muted-foreground); }
.vb-new-plus .ico { width: 26px; height: 26px; }
.vb-new-t { font: 600 14px/1.3 Inter; color: var(--foreground); }
.vb-new-s { font: 400 11.5px/1.4 Inter; color: var(--muted-foreground); }
.vb-card { margin: 0; cursor: pointer; }
.vb-card:hover { border-color: var(--primary); }
.vb-active {
  display: inline-block; margin-left: 8px; padding: 2px 8px; border-radius: 999px;
  font: 600 10.5px/1.5 Inter; letter-spacing: .02em; text-transform: uppercase;
  color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
}
.vb-acts { display: flex; gap: 2px; margin-top: 12px; }
.vb-act {
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  border: none; background: none; border-radius: var(--radius); cursor: pointer;
  color: var(--muted-foreground); transition: background .12s, color .12s;
}
.vb-act:hover { background: var(--surface-hover); color: var(--foreground); }
.vb-act .ico { width: 14px; height: 14px; }
.vb-act-danger:hover { color: var(--red); }

/* --- the regions explainer's three-axes drawing (Technology / Region / Niche).
       Each line draws on via stroke-dashoffset, its label fading in as the
       stroke lands; dasharray matches each line's length so they draw at one
       speed. Colors are the slide's, said in tokens. --- */
.wiz-axes { max-width: 520px; margin: 4px 0 18px; }
.wiz-axes svg { width: 100%; height: auto; display: block; }
.ax { stroke-width: 7; stroke-linecap: round; fill: none; animation: axDraw .7s ease-out forwards; }
.ax-tech   { stroke: var(--amber); stroke-dasharray: 220; stroke-dashoffset: 220; animation-delay: .2s; }
.ax-region { stroke: var(--red);   stroke-dasharray: 283; stroke-dashoffset: 283; animation-delay: 1s; }
.ax-niche  { stroke: var(--blue);  stroke-dasharray: 410; stroke-dashoffset: 410; animation-delay: 1.8s; }
.axt { font: 700 19px Inter; opacity: 0; animation: axLabel .4s ease-out forwards; }
.axt-tech   { fill: var(--amber); animation-delay: .75s; }
.axt-region { fill: var(--red);   animation-delay: 1.55s; }
.axt-niche  { fill: var(--blue);  animation-delay: 2.35s; }
@keyframes axDraw { to { stroke-dashoffset: 0; } }
@keyframes axLabel { to { opacity: 1; } }

/* --- the value-chain explainer's four-triangles drawing (You / Your client /
       Client of your client / Consumer). Each triangle slides in left to
       right - the way value flows down the chain - and its label fades in
       behind it. Fills are the roles' highlighter inks, said in tokens. --- */
.wiz-chain { max-width: 560px; margin: 4px 0 18px; }
.wiz-chain svg { width: 100%; height: auto; display: block; }
.vc-tri { opacity: 0; transform: translateX(-16px); animation: vcSlide .5s ease-out forwards; }
.vc-tri.vc-you      { fill: var(--foreground); animation-delay: .2s; }
.vc-tri.vc-client   { fill: var(--primary);    animation-delay: .75s; }
.vc-tri.vc-cc       { fill: var(--amber);      animation-delay: 1.3s; }
.vc-tri.vc-consumer { fill: var(--blue);       animation-delay: 1.85s; }
.vct { font: 700 15px Inter; opacity: 0; animation: axLabel .4s ease-out forwards; }
.vct.vc-you      { fill: var(--foreground); animation-delay: .55s; }
.vct.vc-client   { fill: var(--primary);    animation-delay: 1.1s; }
.vct.vc-cc       { fill: var(--amber);      animation-delay: 1.65s; }
.vct.vc-consumer { fill: var(--blue);       animation-delay: 2.2s; }
@keyframes vcSlide { to { opacity: 1; transform: translateX(0); } }

/* --- the industry explainer's brainstorming-tree drawing (a miniature of the
       board on the next screen). The typed root runs out under a caret, blue
       sub-niche branches dig deeper, an amber similar-industry branch goes
       sideways, and the deepest node is picked. Node fill, dashed border,
       radius, legend and branch colours are the board's own tokens, so the
       drawing rehearses the screen it precedes; --d carries each beat. --- */
.wiz-tree { max-width: 620px; margin: 4px 0 18px; }
.wiz-tree svg { width: 100%; height: auto; display: block; }
.it-e {
  fill: none; stroke-width: 1.6; opacity: .5;
  stroke-dasharray: 140; stroke-dashoffset: 140;
  animation: itDraw .5s ease-out var(--d) forwards;
}
.it-e-sub { stroke: var(--blue); }
.it-e-sim { stroke: var(--amber); }
.it-n { opacity: 0; animation: itPop .45s ease-out var(--d) forwards; }
.it-box { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1.5; stroke-dasharray: 4 4; }
.it-t { font: 500 11.5px Inter; fill: var(--muted-foreground); }
.it-sub .it-box { stroke: color-mix(in srgb, var(--blue) 45%, transparent); }
.it-sim .it-box { stroke: color-mix(in srgb, var(--amber) 50%, transparent); }
/* Typed, so already in the field - the root wears the picked node's solid ink
   from the first frame, the way pressing Enter lands it picked on the board. */
.it-root .it-box { stroke: var(--primary); stroke-dasharray: 0; fill: var(--primary-12); }
.it-root .it-t { fill: var(--foreground); font-weight: 600; }
/* The one click the drawing shows: a suggested branch adopted into the field.
   The dashed card fades out under the solid picked one - see the note in
   industryTreeSvg() for why this is two rects and not one animated stroke. */
.it-boxp { fill: var(--primary-12); stroke: var(--primary); stroke-width: 1.5; opacity: 0; animation: itPop .45s ease-out 3.05s forwards; }
.it-pick .it-box { animation: itFade .45s ease-out 3.05s forwards; }
.it-pick .it-t { animation: itPickTxt .45s ease-out 3.05s forwards; }
.it-ring {
  fill: none; stroke: var(--primary); stroke-width: 2; opacity: 0;
  transform-box: fill-box; transform-origin: center;
  animation: itRing .6s ease-out 3.05s forwards;
}
/* The caret runs the root's letters out from under the clip - "start typing".
   Clip and caret share one 67px travel so the bar stays at the last letter. */
.it-caret { fill: var(--primary); opacity: 0; animation: itCaret 1.3s linear .1s forwards, itTypeCaret .8s steps(10) .25s both; }
.it-typebox { animation: itType .8s steps(10) .25s backwards; }
.it-legend { opacity: 0; animation: itPop .45s ease-out .1s forwards; }
.it-key-sim { fill: var(--amber); }
.it-key-sub { fill: var(--blue); }
.it-keyt { font: 500 9.5px Inter; fill: var(--muted-foreground); }
.it-cap { font: 600 9.5px Inter; opacity: 0; animation: itPop .4s ease-out var(--d) forwards; }
.it-cap-broad { fill: var(--red); }
.it-cap-good { fill: var(--primary); }
@keyframes itDraw { to { stroke-dashoffset: 0; } }
@keyframes itPop { to { opacity: 1; } }
@keyframes itFade { to { opacity: 0; } }
@keyframes itType { from { width: 0; } to { width: 67px; } }
@keyframes itTypeCaret { from { transform: translateX(0); } to { transform: translateX(63px); } }
@keyframes itCaret { 0% { opacity: 0; } 6%, 78% { opacity: 1; } 100% { opacity: 0; } }
@keyframes itPickTxt { to { fill: var(--foreground); font-weight: 600; } }
@keyframes itRing { from { opacity: .55; transform: scale(1); } to { opacity: 0; transform: scale(1.06); } }

/* --- the three later ICP explainer drawings (business model, unit economics,
       technology stack). Each rehearses the screen behind Continue, so the
       cards, chips, slider and chip input borrow that widget's own tokens.
       Shared scaffolding first: one container, one entry animation, one card,
       and the type sizes the miniatures label themselves with. --- */
.wiz-draw { max-width: 620px; margin: 4px 0 18px; }
.wiz-draw svg { width: 100%; height: auto; display: block; }
.wiz-in { opacity: 0; animation: wizIn .4s ease-out var(--d) forwards; }
.wiz-card { fill: var(--surface); stroke: var(--line); stroke-width: 1; }
.wiz-lbl { font: 600 9px Inter; fill: var(--muted-2); letter-spacing: .04em; }
.wiz-txt { font: 500 10px Inter; fill: var(--muted-foreground); }
.wiz-cap { font: 600 9.5px Inter; fill: var(--primary); }
.wiz-note { font: 500 9px Inter; fill: var(--muted-foreground); }
@keyframes wizIn { to { opacity: 1; } }
@keyframes wizGrow { from { width: 0; } }
@keyframes wizSlide { from { transform: translateX(0); } to { transform: translateX(var(--t)); } }

/* Business model: the next screen's five boxes writing themselves, then the
   revenue bar the guidance describes - fee, extras, and the unbilled sliver
   that is the easiest profit to hand back. The last box wears the accent
   because it is the one the whole screen is for. */
.bm-plabel { font: 500 9.5px Inter; fill: var(--foreground); }
.bm-answer { font: 500 8.5px Inter; fill: var(--muted-foreground); }
.bm-key { stroke: var(--primary-30); fill: var(--primary-04); }
.bm-answer.bm-key { fill: var(--foreground); font-weight: 600; }
.bm-arrow path { fill: none; stroke: var(--muted-2); stroke-width: 1.4; }
.bm-arrow polygon { fill: var(--muted-2); }
.bm-seg-fee { fill: var(--primary-12); stroke: var(--primary-30); }
.bm-seg-extra { fill: var(--amber-bg); stroke: color-mix(in srgb, var(--amber) 45%, transparent); }
.bm-seg-leak { fill: var(--red-bg); stroke: var(--red); stroke-dasharray: 3 3; }
.bm-segt { font: 500 8.5px Inter; fill: var(--muted-foreground); }
.bm-leak { font: 600 9px Inter; fill: var(--red); }

/* Unit economics: the screen's own slider, dragged. Track, fill, thumb and
   tick type are .pos-slider's, only smaller - the drawing IS the control. */
.ue-track { fill: var(--bar-track); }
.ue-fill { fill: var(--primary); animation: wizGrow 1s ease-out .4s backwards; }
.ue-thumb { fill: var(--card); stroke: var(--primary); stroke-width: 2; animation: wizSlide 1s ease-out .4s both; }
.ue-tick { font: 500 8.5px Inter; fill: var(--muted-2); }
.ue-val { font: 700 15px Inter; fill: var(--primary); }
.ue-arrow { fill: none; stroke: var(--muted-2); stroke-width: 1.3; }
.ue-win { fill: var(--primary-08); stroke: var(--primary-30); }
.ue-wint { font: 600 10px Inter; fill: var(--foreground); }

/* Technology stack: the chip input, typing. The menu opens, the highlighted
   suggestion lands as a chip, and the guidance's two questions are drawn as
   brackets under chips that are otherwise identical - because the real input
   draws them identically, and the split is a reading, not a state. */
.ts-in { fill: var(--input-bg); stroke: var(--line); stroke-width: 1; }
.ts-int { font: 500 11px Inter; fill: var(--foreground); }
.ts-caret { fill: var(--primary); opacity: 0; animation: tsCaret 1s linear .75s forwards, tsTypeCaret .6s steps(4) .9s both; }
.ts-typebox { animation: tsType .6s steps(4) .9s backwards; }
/* The menu stays open once it has opened: it IS open at the moment the chip
   lands, and closing it would leave the drawing ending on a hole. */
.ts-menu { opacity: 0; animation: wizIn .3s ease-out 1.55s forwards; }
.ts-menubox { fill: var(--card); stroke: var(--line-strong); stroke-width: 1; }
.ts-on { fill: var(--primary-08); }
.ts-namet { font: 600 10px Inter; fill: var(--foreground); }
.ts-ava { fill: var(--surface-hover); stroke: var(--line); stroke-width: 1; }
.ts-avat { font: 700 8px Inter; fill: var(--muted-foreground); }
.ts-chip { fill: var(--card); stroke: var(--line-strong); stroke-width: 1; }
.ts-chipt { font: 600 10.5px Inter; fill: var(--foreground); }
.ts-keep path { fill: none; stroke: var(--primary); stroke-width: 1.3; }
.ts-keep text { font: 600 9.5px Inter; fill: var(--primary); }
.ts-drop path { fill: none; stroke: var(--red); stroke-width: 1.3; }
.ts-drop text { font: 600 9.5px Inter; fill: var(--red); }
@keyframes tsType { from { width: 0; } to { width: 26px; } }
@keyframes tsTypeCaret { from { transform: translateX(0); } to { transform: translateX(22px); } }
@keyframes tsCaret { 0% { opacity: 0; } 8%, 72% { opacity: 1; } 100% { opacity: 0; } }

/* --- ICP section intro: the field of companies and the ring that closes on
       five of them, then the dimensions that make up one profile. --- */
.ic-dot { fill: var(--line-strong); opacity: 0; animation: wizIn .3s ease-out .1s forwards, icFade .4s ease-out 1.5s forwards; }
.ic-dot.ic-pick { animation: wizIn .3s ease-out .1s forwards, icPick .4s ease-out 1.5s forwards; }
.ic-ring { fill: none; stroke: var(--primary); stroke-width: 1.6; stroke-dasharray: 5 4; opacity: 0; animation: wizIn .4s ease-out 1.55s forwards; }
@keyframes icFade { to { opacity: .3; } }
@keyframes icPick { to { opacity: 1; fill: var(--primary); } }

/* --- Number of employees: the picker's own rising bars, two of them lit. --- */
.em-bar { fill: var(--bar-track); }
.em-bar.em-on { fill: var(--primary); }
.em-t { font: 500 9.5px Inter; fill: var(--muted-foreground); }
.em-t.em-on { fill: var(--primary); font-weight: 700; }
.em-note { font: 500 8.5px Inter; fill: var(--muted-2); }
.em-brace { font: 600 9.5px Inter; fill: var(--primary); }

/* --- Needs and anti-needs: the two boxes, ticked in and crossed out. --- */
.ne-h { font: 500 10px Inter; }
.ne-h-yes { fill: var(--primary); }
.ne-h-no { fill: var(--red); }
.ne-box-yes { stroke: var(--primary-30); }
.ne-box-no { stroke: color-mix(in srgb, var(--red) 35%, transparent); }
.ne-t { font: 500 9.5px Inter; fill: var(--foreground); }
.ne-yes { fill: none; stroke: var(--primary); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ne-no { fill: none; stroke: var(--red); stroke-width: 1.6; stroke-linecap: round; }

/* --- Buyer personas: the committee, the chain through it, the ranking. --- */
.pe-ava { fill: var(--surface-hover); stroke: var(--line); stroke-width: 1; }
.pe-head { fill: var(--muted-2); }
.pe-body { fill: none; stroke: var(--muted-2); stroke-width: 2.4; }
.pe-role { font: 600 9px Inter; fill: var(--foreground); }
.pe-chain path { fill: none; stroke: var(--primary); stroke-width: 5; }
.pe-chain polygon { fill: var(--primary); }
.pe-entry rect, .pe-blocked rect { fill: var(--surface); stroke-width: 1.2; }
.pe-entry text, .pe-blocked text { font: 600 9.5px Inter; }
.pe-entry rect { stroke: var(--primary-30); }
.pe-entry text { fill: var(--primary); }
.pe-entry .pe-drop { fill: none; stroke: var(--primary); stroke-width: 1.3; }
/* The blocked way in: the same box in red, its drop line struck by an x. */
.pe-blocked rect { stroke: color-mix(in srgb, var(--red) 35%, transparent); }
.pe-blocked text { fill: var(--red); }
.pe-blocked .pe-drop { fill: none; stroke: var(--muted-2); stroke-width: 1.3; stroke-dasharray: 3 3; }
.pe-x path { fill: none; stroke: var(--red); stroke-width: 1.8; stroke-linecap: round; }

/* --- Problem-Solution-Fit: the table, then the awareness ladder it picks
       its last column from. --- */
.psf-pill { fill: var(--primary-08); stroke: var(--primary-30); stroke-width: 1; }
.psf-pillt { font: 600 9px Inter; fill: var(--primary); }
.psf-step { fill: var(--bar-track); }
.psf-step.psf-on { fill: var(--primary); }
.psf-stept { font: 500 8.5px Inter; fill: var(--muted-2); }
.psf-stept.psf-on { fill: var(--primary); font-weight: 700; }

/* --- USPs: the competitor's downfall on the left, your USP on the right. --- */
.us-v { font: 600 10px Inter; fill: var(--foreground); }
.us-bad { font: 600 9.5px Inter; fill: var(--red); }
.us-good { font: 600 9.5px Inter; fill: var(--primary); }
.us-arrow path { fill: none; stroke: var(--muted-2); stroke-width: 1.4; }
.us-arrow polygon { fill: var(--muted-2); }

/* --- Buying window: one timeline, the trigger that opens it, the edges. --- */
.bw-axis { fill: none; stroke: var(--line-strong); stroke-width: 1.5; }
.bw-trig path { fill: none; stroke: var(--primary); stroke-width: 1.5; }
.bw-trig circle { fill: var(--primary); }
.bw-band { fill: var(--primary-12); stroke: var(--primary-30); stroke-width: 1; }
.bw-tag { fill: var(--primary); }
.bw-off { font: 500 9px Inter; fill: var(--muted-2); }

/* --- Other attributes: three numbered slots, and the cap that stops there. --- */
.at-num { fill: var(--primary); }
.at-numt { font: 700 10px Inter; fill: var(--primary-fg); }
.at-t { font: 500 10px Inter; fill: var(--foreground); }
.at-cap { fill: var(--primary-08); stroke: var(--primary-30); stroke-width: 1; }
.at-capt { font: 700 9px Inter; fill: var(--primary); }

/* --- Touchpoints: the three deciders feeding the table of funnels. --- */
.tp-feed path { fill: none; stroke: var(--muted-2); stroke-width: 1.3; }
.tp-feed polygon { fill: var(--muted-2); }

/* --- the framework Introduction's three-attribute Venn. The circles are the
       webinar's, drawn in the ink of the page (chalk on the slide, foreground
       here), each drawing itself in turn; the sliver all three share fills in
       last, because that sliver is what the whole framework is looking for.
       Circumference at r=54 is 340, which is the dasharray. --- */
.wiz-venn { max-width: 620px; }
.vn-c {
  fill: none; stroke: var(--foreground); stroke-width: 2.2;
  stroke-dasharray: 340; stroke-dashoffset: 340;
  animation: vnDraw .85s ease-out var(--d) forwards;
}
.vn-t { font: 700 12px Inter; fill: var(--foreground); opacity: 0; animation: wizIn .4s ease-out var(--d) forwards; }
.vn-hit {
  fill: var(--primary-12); stroke: var(--primary); stroke-width: 2;
  opacity: 0; transform-box: fill-box; transform-origin: center;
  animation: vnPop .55s ease-out 2.7s forwards;
}
.vn-cap { font: 700 11px Inter; fill: var(--primary); }
@keyframes vnDraw { to { stroke-dashoffset: 0; } }
@keyframes vnPop { from { opacity: 0; transform: scale(.5); } to { opacity: 1; transform: scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .ax, .axt { animation: none; stroke-dashoffset: 0; opacity: 1; }
  .vc-tri, .vct { animation: none; opacity: 1; transform: none; }
  .it-e { animation: none; stroke-dashoffset: 0; }
  .it-n, .it-legend, .it-cap, .it-boxp { animation: none; opacity: 1; }
  .it-caret, .it-ring { animation: none; opacity: 0; }
  .it-typebox { animation: none; }
  /* No crossfade to watch, so the picked node simply arrives picked. */
  .it-pick .it-box { animation: none; opacity: 0; }
  .it-pick .it-t { animation: none; fill: var(--foreground); font-weight: 600; }
  .wiz-in, .ts-menu { animation: none; opacity: 1; }
  .ue-fill, .ts-typebox { animation: none; }
  .ue-thumb { animation: none; transform: translateX(var(--t)); }
  .ts-caret { animation: none; opacity: 0; }
  .ic-dot { animation: none; opacity: .3; }
  .ic-dot.ic-pick { animation: none; opacity: 1; fill: var(--primary); }
  .ic-ring { animation: none; opacity: 1; }
  .vn-c { animation: none; stroke-dashoffset: 0; }
  .vn-t, .vn-hit { animation: none; opacity: 1; transform: none; }
}

/* --- the value-chain board: you in the middle, suppliers left, clients right,
       value flowing left to right. The canvas chrome (the dotted field, the
       zoom stack, the thinking dots, the error toast) is the industry tree's,
       reused class for class so the two boards feel like one tool - only the
       nodes and the arrows are its own. --- */
.vc-top { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.vc-flow {
  display: inline-flex; align-items: center; gap: 7px; flex: 1; min-width: 260px;
  font: 600 11px/1 Inter; letter-spacing: .07em; text-transform: uppercase; color: var(--muted-foreground);
}
.vc-flow .ico-sm { opacity: .5; }
.vc-legend { display: flex; gap: 14px; font: 500 12px/1 Inter; color: var(--muted-foreground); }
.vc-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.vc-key { width: 12px; height: 12px; border-radius: 4px; display: inline-block; border: 1.5px solid var(--primary); }
.vc-key-seat { background: color-mix(in srgb, var(--primary) 18%, transparent); }
.vc-key-target { background: none; border-style: dashed; }
.vc-canvas { height: 560px; }
.vc-edges { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
.vc-e { fill: none; stroke: var(--line-strong); stroke-width: 1.6; opacity: .75; }
.vc-edges marker path { fill: var(--line-strong); opacity: .75; }

.vc-node {
  position: absolute; box-sizing: border-box; padding: 3px 4px; border-radius: 10px;
  background: var(--card); border: 1.5px solid var(--line-strong);
}
.vc-node.is-own { border-style: dashed; }
/* Drawn by the sparring partner as you talk: GOLD, the house colour for
   knowledge offered. Dotted, because it is a suggestion on your board - it is
   not in your answer until you build on it. */
.vc-node.is-sketch { border-style: dotted; border-color: var(--gold-border); background: var(--gold-bg); }
.vc-node.is-sketch .vc-t { color: var(--gold-fg); }
.vc-node.is-dim { opacity: .34; }
.vc-node.is-dim .vc-t { text-decoration: line-through; }
.vc-node.is-open { z-index: 3; box-shadow: 0 6px 18px rgb(0 0 0 / 10%); }
.vc-row { display: flex; align-items: center; gap: 2px; min-height: 32px; }
.vc-t {
  flex: 1; min-width: 0; border: 0; background: none; padding: 3px 4px; text-align: left;
  font: 500 12.5px/1.25 Inter; color: var(--foreground); cursor: pointer; border-radius: 6px;
}
.vc-t:hover { background: var(--primary-12); }
.vc-b {
  flex: none; width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 7px; background: none; color: var(--muted-foreground); cursor: pointer;
}
.vc-b:hover { background: var(--primary-12); color: var(--primary); }
.vc-b.is-on { background: var(--primary-12); color: var(--primary); }
.vc-b[data-vc-act="remove"]:hover { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--red); }
.vc-menu { display: flex; align-items: center; gap: 2px; padding: 3px 1px 1px; border-top: 1px solid var(--line); margin-top: 3px; }
.vc-in {
  flex: 1; min-width: 0; width: 100%; border: 1px solid var(--primary); border-radius: 7px;
  padding: 4px 7px; background: var(--surface); color: var(--foreground); font: 500 12.5px/1.3 Inter;
}
.vc-in:focus { outline: none; box-shadow: 0 0 0 3px var(--primary-12); }
.vc-tag {
  position: absolute; left: 8px; top: -9px; padding: 2px 7px; border-radius: 999px;
  font: 700 9px/1.4 Inter; letter-spacing: .07em; text-transform: uppercase;
  color: var(--primary-fg); background: var(--primary); white-space: nowrap;
}
/* You, and the client right after you: the two marks the whole screen is for. */
.vc-node.vc-me { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 7%, var(--card)); }
.vc-node.is-seat { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 14%, var(--card)); }
.vc-node.is-seat .vc-t { font-weight: 700; }
.vc-node.is-target { border-color: var(--primary); border-style: dashed; box-shadow: 0 0 0 3px var(--primary-12); }
.vc-node.is-target .vc-t { font-weight: 700; }

/* The board before it is a board: one box, one question. */
.vc-start { padding: 14px 14px 12px; border-color: var(--primary); }
.vc-start-l {
  font: 700 10px/1 Inter; letter-spacing: .09em; text-transform: uppercase;
  color: var(--primary); margin-bottom: 8px;
}
.vc-start input {
  width: 100%; box-sizing: border-box; border: 1px solid var(--line); border-radius: 9px;
  padding: 9px 11px; background: var(--surface); color: var(--foreground); font: 500 13px/1.3 Inter;
}
.vc-start input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-12); }
.vc-start-h { margin-top: 8px; font: 500 11.5px/1.4 Inter; color: var(--muted-foreground); }

/* The one-time line about the controls, in the error toast's seat but in the
   quiet colours - it is news, not a problem. */
.vc-hint { color: var(--muted-foreground); max-width: 460px; line-height: 1.5; }
.vc-picked { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.vc-chip { color: var(--muted-foreground); border-style: dashed; }
.vc-chip.is-set { color: var(--foreground); border-style: solid; border-color: var(--primary); }
.vc-notes-l { margin-top: 14px; }
.vc-notes { min-height: 76px; }

/* The intro's tip boxes hold one short paragraph each, so the bulb centers on
   the box instead of hanging at a chapter callout's first-line height; the
   paragraph margins that made the icon look stranded go too. */
.wiz-welcome .cb::before { top: 50%; transform: translateY(-50%); }
.wiz-welcome .cb p { margin: 0; }
.wiz-welcome .cb p + .buzz-row { margin-top: 8px; }
/* A quote's attribution: upright and bold inside the italic paragraph. */
.wiz-prose .wiz-quote-who { font-style: normal; }
/* Error line in the confirmation-page editor - same shape as the call row's. */
.sx-err { margin: 0 20px; padding: 9px 13px; border-radius: var(--card-radius);
  background: var(--red-bg); color: var(--red); font: 500 12.5px/1.45 Inter; }

/* ---------- the confirmation page editor ----------
   A list of blocks in the order they appear on the page, so a button placed
   between two sections is edited between them too - the editor's shape is the
   page's shape rather than a form that has to be mentally re-ordered. */
.sb-block { border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 12px 14px; margin-bottom: 10px; background: var(--surface); }
.sb-top { display: flex; align-items: center; gap: 6px; margin-bottom: 9px; }
.sb-kind { flex: 1; font: 700 10.5px/1.4 Inter; color: var(--muted-foreground);
  text-transform: uppercase; letter-spacing: .05em; }
.sb-top button { display: grid; place-items: center; width: 26px; height: 26px; border: none;
  border-radius: 9999px; background: none; color: var(--muted-foreground); cursor: pointer; }
.sb-top button:hover { background: var(--surface-hover); color: var(--foreground); }
.sb-top .sb-del:hover { background: var(--red-bg); color: var(--red); }
.sb-top svg { width: 14px; height: 14px; }

/* The accent swatches. The native picker sits at the end for anything not here,
   so the common answer is one click and the uncommon one is still possible. */
.sb-swatches { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sb-sw { width: 26px; height: 26px; border-radius: 9999px; cursor: pointer;
  background: var(--sw); border: 2px solid transparent; box-shadow: 0 0 0 1px var(--line); }
.sb-sw.on { border-color: var(--background); box-shadow: 0 0 0 2px var(--primary); }
.sb-swatches input[type="color"] { width: 26px; height: 26px; padding: 0; border: 1px solid var(--line);
  border-radius: 9999px; background: none; cursor: pointer; overflow: hidden; }
.sb-swatches input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.sb-swatches input[type="color"]::-webkit-color-swatch { border: none; border-radius: 9999px; }

/* The confirmation page's logo slot. Checkerboard behind it, because these are
   transparent PNGs meant for a dark page - on a plain white swatch a white logo
   looks like an empty box. */
.sb-logo { width: 92px; height: 40px; display: grid; place-items: center; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 4px;
  background-color: var(--surface);
  background-image: linear-gradient(45deg, var(--surface-hover) 25%, transparent 25%),
                    linear-gradient(-45deg, var(--surface-hover) 25%, transparent 25%),
                    linear-gradient(45deg, transparent 75%, var(--surface-hover) 75%),
                    linear-gradient(-45deg, transparent 75%, var(--surface-hover) 75%);
  background-size: 10px 10px;
  background-position: 0 0, 0 5px, 5px -5px, -5px 0;
}
.sb-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }

/* ---------- screenshots on a CRM note ----------
   Thumbnails, not full-width images: a note is a line in a list and a pasted
   screenshot is often a tall phone capture that would push everything after it
   off the panel. Click opens the full size in a tab. */
.note-shots { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.note-shots a { display: block; line-height: 0; }
.note-shots img { width: 72px; height: 52px; object-fit: cover; border-radius: var(--radius);
  border: 1px solid var(--line); transition: border-color .12s; }
.note-shots a:hover img { border-color: var(--primary); }
.note-row .t { display: block; }

/* The tray of screenshots waiting to go up with the next note. */
.note-tray { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.note-chip { position: relative; line-height: 0; }
.note-chip img { width: 56px; height: 42px; object-fit: cover; border-radius: var(--radius);
  border: 1px solid var(--line); }
.note-chip button { position: absolute; top: -6px; right: -6px; width: 18px; height: 18px;
  display: grid; place-items: center; border-radius: 9999px; border: 1px solid var(--line);
  background: var(--card); color: var(--muted-foreground); cursor: pointer; padding: 0; }
.note-chip button:hover { background: var(--red-bg); color: var(--red); border-color: var(--red-bg); }
.note-chip button svg { width: 10px; height: 10px; }

/* ============================================================
   Projects - personal kanban boards (public/projects.js).
   The board is Matt's Action Board, as a kanban: a colour per PERSON
   (--pp, set inline per column / per row - identity, like the track
   palette), clean white rows with an emoji tile washed in that colour,
   a check-off circle, standing Team update + Done columns on the right,
   and one progress bar over the whole board. The per-person view reuses
   the same rows in a single centred list. The emoji in a tile is card
   DATA (badge-glyph carve-out), never chrome.
   ============================================================ */
/* The progress strip: one honest number for the whole board. */
.pj-progress {
  display: flex; align-items: center; gap: 16px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 12px 18px; margin-bottom: 14px;
}
.pj-progress-n { font: 500 13px/1 Inter; color: var(--muted-foreground); white-space: nowrap; }
.pj-progress-n b { font-size: 17px; color: var(--foreground); font-weight: 700; }
.pj-progress .progress-bar-wrap { flex: 1; margin: 0; }

/* A person's column header: their face, their NAME in their colour (a
   button - it opens their focused list), their open count. Base rules are
   the dark theme (as everywhere): lighten the identity colour for dark
   ground, use it raw on light. */
.pj-col-head { display: flex; align-items: center; gap: 8px; }
.pj-pbtn {
  display: inline-flex; align-items: center; gap: 8px; flex: 1; min-width: 0;
  border: none; background: transparent; padding: 0; font: inherit; text-align: left;
  color: inherit;
}
button.pj-pbtn { cursor: pointer; border-radius: var(--radius); }
button.pj-pbtn:hover .col-title { text-decoration: underline; }
.pj-col-head .col-title {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: color-mix(in srgb, var(--pp, currentColor) 60%, #fff);
}
[data-theme="light"] .pj-col-head .col-title { color: var(--pp, currentColor); }
.kcol.pj-stage .pj-col-head .col-title { color: var(--foreground); }
.pj-nobody {
  width: 20px; height: 20px; flex: none; border-radius: 50%;
  border: 1px dashed var(--muted-2); color: var(--muted-foreground);
  display: inline-grid; place-items: center;
}
.pj-nobody svg { width: 11px; height: 11px; }

/* A card as a clean row: neutral card chrome - the person's colour lives
   only in the emoji tile's wash (and the tile follows the card's OWNER in
   the stage columns, so Team update still says whose work each row was). */
.pj-row {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--card-radius);
  padding: 11px 12px; margin-bottom: 8px; cursor: pointer;
  transition: border-color .12s, box-shadow .12s, opacity .12s;
}
.pj-row:hover { box-shadow: 0 2px 8px rgba(16,24,24,.10); }
.pj-tile {
  width: 36px; height: 36px; flex: none; border-radius: 9px;
  background: color-mix(in srgb, var(--pp, #888) 20%, transparent);
  color: color-mix(in srgb, var(--pp, #888) 60%, #fff);
  display: grid; place-items: center;
}
[data-theme="light"] .pj-tile {
  background: color-mix(in srgb, var(--pp, #888) 13%, #fff);
  color: var(--pp, var(--muted-foreground));
}
.pj-tile svg { width: 17px; height: 17px; }
.pj-emoji { font-size: 19px; line-height: 1; }
.pj-row-main { flex: 1; min-width: 0; padding-top: 1px; }
.pj-row .t {
  font: 500 13px/1.4 Inter; color: var(--foreground);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.pj-row-notes {
  margin-top: 2px; font: 400 11.5px/1.45 Inter; color: var(--muted-foreground);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.pj-row-meta { margin-top: 6px; display: flex; align-items: center; gap: 8px; }
.pj-src {
  display: inline-flex; align-items: center; gap: 4px;
  font: 500 11.5px/1 Inter; color: var(--muted-foreground); text-decoration: none;
}
.pj-src:hover { color: var(--primary); }
.pj-row .bcheck { flex: none; margin-top: 1px; }
.pj-row.done { opacity: .72; }
.pj-row.done .t { color: var(--muted-foreground); text-decoration: line-through; }
.pj-row.done .pj-tile { filter: saturate(.4); }

/* The recorded-call rows (launcher card + the board's From-a-call modal). */
.pj-call { display: flex; align-items: center; gap: 12px; padding: 11px 18px; }
.pj-call + .pj-call { border-top: 1px solid var(--line); }
.pj-call-ico { color: var(--muted-foreground); display: inline-flex; flex: none; }
.pj-call-ico svg { width: 17px; height: 17px; }
.pj-call-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pj-call-t { font: 500 13.5px/1.35 Inter; color: var(--foreground);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pj-call-m { font: 400 11.5px/1.4 Inter; color: var(--muted-foreground); }
.pj-call .pj-extract { flex: none; }
.pj-call .vb-act { position: static; opacity: .7; }
.pj-call .vb-act:hover { opacity: 1; }
#pj-calls .empty-cell, #pj-modal-calls .empty-cell { color: var(--muted-foreground); font-size: 13px; }
#pj-modal-calls { max-height: 55vh; overflow-y: auto; }
#pj-modal-calls .pj-call { padding: 10px 2px; }

/* The busy state on an extract button spins its refresh glyph. */
.ico.spin { animation: spin .8s linear infinite; }

/* "Added N action steps from ..." - one line, then gone. Neutral card chrome,
   not a status colour: it is a receipt, not an alert. */
.pj-toast {
  position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
  background: var(--card); color: var(--foreground); border: 1px solid var(--line);
  border-radius: 9999px; padding: 11px 18px; font: 500 13px/1 Inter;
  box-shadow: 0 6px 24px rgba(16,24,24,.18); z-index: 300; max-width: 80vw;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: opacity .4s ease;
}
.pj-toast.gone { opacity: 0; }

/* First-login logo intro. Plate white in BOTH themes on purpose - it must be
   one surface with the clip, so this is the one place a hardcoded colour is
   correct. The video's whites are near-white (253) and YUV->RGB conversion
   shifts them differently per device, which drew a faint box around the video
   on phones; brightness(1.04) clips them to pure white in the browser's own
   pipeline, so clip and plate agree everywhere (the near-black logo is not
   visibly touched). Sits above everything (highest chrome z-index is 400). */
.intro-splash {
  position: fixed; inset: 0; z-index: 500; background: #fff;
  display: grid; place-items: center;
  opacity: 1; transition: opacity .45s ease;
}
.intro-splash video { filter: brightness(1.04); }
/* Pre-paint cover for the splash: the same plate, applied by an inline script
   in app.html BEFORE the app can paint, so chrome never flashes under the
   intro. Sits just beneath .intro-splash, which mounts over it and lifts it. */
html.intro-hold::before {
  content: ''; position: fixed; inset: 0; z-index: 499; background: #fff;
}
.intro-splash.out { opacity: 0; pointer-events: none; }
.intro-splash video { width: 100%; height: 100%; object-fit: contain; }

/* Post-intro "click here first" pointer. Draws a ring around the first card a
   new member should open, arrow + pill beneath. The accent at FULL strength is
   deliberate here - it is the one moment the UI physically points, and it is
   gone on the first click. pointer-events: none throughout: the circled card
   itself stays one click away. Below every modal/palette (z 100+), above the
   board it annotates. */
.intro-nudge { position: fixed; inset: 0; z-index: 90; pointer-events: none;
  opacity: 1; transition: opacity .35s ease; }
.intro-nudge.out { opacity: 0; }
.intro-nudge svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.intro-nudge .nudge-box { fill: none; stroke: var(--primary); stroke-width: 3;
  stroke-linecap: round; stroke-dasharray: 12 9;
  opacity: 0; animation: nudge-in .4s ease .2s forwards; }
.intro-nudge .nudge-arrow { fill: none; stroke: var(--primary); stroke-width: 3;
  stroke-linecap: round; stroke-linejoin: round; transform-box: fill-box;
  opacity: 0; animation: nudge-in .35s ease .5s forwards,
    nudge-bob 1.5s ease-in-out .9s infinite; }
@keyframes nudge-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }
/* The label is a card chip - the same chrome as the board's cards and toasts
   (Matt picked this over accent-tinted, dark-tooltip and bare-text variants),
   so it reads as part of the product, not a sticker on top of it. */
.intro-nudge .nudge-label { position: absolute; transform: translateX(-50%);
  background: var(--card); color: var(--foreground);
  border: 1px solid var(--line);
  border-radius: 9999px; padding: 8px 13px; font: 600 12.5px/1 Inter, sans-serif;
  white-space: nowrap; box-shadow: 0 4px 14px rgba(16,24,24,.12);
  opacity: 0; animation: nudge-in .35s ease .5s forwards; }
@keyframes nudge-in { to { opacity: 1; } }

/* One person's focused list (the Action Board view): a centred column of the
   same rows, their colour on the header, their own little progress chip. */
.pj-solo { max-width: 760px; margin: 0 auto; }
.pj-solo-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.pj-solo-name {
  font: 700 20px/1.2 Inter; letter-spacing: -.01em;
  color: color-mix(in srgb, var(--pp, currentColor) 60%, #fff);
}
[data-theme="light"] .pj-solo-name { color: var(--pp, var(--foreground)); }
.pj-solo-chip {
  font: 700 11px/1 Inter; letter-spacing: .08em; padding: 4px 10px; border-radius: 9999px;
  background: color-mix(in srgb, var(--pp, #888) 18%, transparent);
  color: color-mix(in srgb, var(--pp, #888) 65%, #fff);
}
[data-theme="light"] .pj-solo-chip {
  background: color-mix(in srgb, var(--pp, #888) 14%, #fff);
  color: var(--pp, var(--muted-foreground));
}
.pj-solo .pj-row { padding: 13px 14px; }

/* The new-board dialog's people picker + the Add-person rows. */
.pj-pick {
  display: flex; align-items: center; gap: 10px; padding: 7px 2px;
  font: 500 13.5px/1 Inter; color: var(--foreground); cursor: pointer;
}
.pj-pick input { accent-color: var(--primary); width: 16px; height: 16px; }
.pj-addrow {
  width: 100%; justify-content: flex-start; margin-bottom: 8px;
  display: inline-flex; align-items: center; gap: 10px;
}
/* A document on a note has nothing to preview, so it names itself and its size -
   a grey rectangle says nothing about whether it is worth opening. */
.note-file { display: inline-flex; align-items: center; gap: 6px; max-width: 220px;
  padding: 5px 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); font: 500 11.5px/1.4 Inter; color: var(--foreground);
  text-decoration: none; }
.note-file:hover { border-color: var(--primary); }
.note-file svg { width: 13px; height: 13px; flex: none; color: var(--muted-foreground); }
.note-file > span:first-of-type { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-file .muted { flex: none; font-size: 10.5px; }

.note-chip.doc { display: inline-flex; align-items: center; }
.note-chip-doc { display: inline-flex; align-items: center; gap: 5px; max-width: 150px;
  height: 42px; padding: 0 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); font: 500 11px/1.3 Inter; color: var(--foreground); }
.note-chip-doc span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-chip-doc svg { width: 12px; height: 12px; flex: none; color: var(--muted-foreground); }

/* ============ Admin > Changelog ============ */
/* Rows, not boxes - the Users table's rhythm: a divider under each entry, the
   row tints on hover, and the click path itself is the link (kept muted). */
.chlog { max-width: none; }
.chlog-day { margin: 22px 0 4px; padding: 0 12px; }
.chlog-entry {
  padding: 11px 12px;
  border-bottom: 1px solid var(--td-border);
  transition: background .1s;
}
.chlog-entry:hover { background: var(--surface); }
.chlog-note { font: 400 20px/1.6 Inter; color: var(--foreground); }
.chlog-note strong { font-weight: 650; }
.chlog-where {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 4px;
  font: 400 13.5px/1.4 Inter; color: var(--muted-foreground);
}
.chlog-where .ico-sm { flex-shrink: 0; opacity: .6; }
a.chlog-where { text-decoration: none; cursor: pointer; }
a.chlog-where:hover span { text-decoration: underline; }
/* A condensed entry: the click path and the "N updates" count share a line,
   and the raw notes unfold underneath, indented and slightly smaller. */
.chlog-foot { display: flex; align-items: center; gap: 14px; }
.chlog-more {
  background: none; border: none; padding: 0; margin-top: 6px;
  font: 400 13.5px/1.4 Inter; color: var(--muted-foreground); cursor: pointer;
}
.chlog-more:hover, .chlog-more.on { text-decoration: underline; }
.chlog-subs { margin: 8px 0 2px 14px; padding-left: 12px; border-left: 2px solid var(--line); }
.chlog-sub { padding: 5px 0; }
.chlog-sub .chlog-note { font-size: 15.5px; }
/* The little square numbering each entry - #1 is the oldest ever shipped. */
.chlog-num {
  flex: none; min-width: 40px; height: 40px; padding: 0 6px; margin-top: 2px;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  font: 600 14px/1 Inter; color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}
/* The entry's screenshot: a fixed-width thumbnail on the LEFT (the important
   control boxed and arrowed in the image itself), the click path and update
   count right under it. Zooms in a lightbox on click. */
.chlog-entry { display: flex; align-items: flex-start; gap: 20px; }
/* Staging only: an entry live has not shipped. Blue = information, the one
   status colour for "this is not the real thing yet". */
.chlog-entry.is-pending .chlog-num { background: rgba(13,127,158,.12); color: var(--blue); border-color: rgba(13,127,158,.35); }
.chlog-pending { margin-left: 8px; vertical-align: middle; font-weight: 600; }
.chlog-body { flex: 1; min-width: 0; }
.chlog-media { flex: none; width: 48%; display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.chlog-media .chlog-foot { flex-direction: column; align-items: flex-start; gap: 0; }
.chlog-shot {
  width: 100%; border-radius: 8px;
  border: 1px solid var(--line); cursor: zoom-in; margin: 2px 0 4px;
}
@media (max-width: 700px) {
  .chlog-entry { flex-direction: column; }
  .chlog-media { width: 100%; max-width: none; }
}
.modal.chlog-lightbox { padding: 0; background: none; border: none; box-shadow: none; max-width: none; }
.chlog-zoom {
  max-width: min(92vw, 1400px); max-height: 88vh; border-radius: 10px;
  border: 1px solid var(--line); box-shadow: var(--shadow-pop); cursor: zoom-out;
}

/* ============================================================ admin user map */
/* The dots and their popups sit ON the always-light Positron basemap, so like
   the positioning map's pins they use fixed light-mode colors rather than the
   theme tokens - mint-on-white is exactly what the design guide forbids. */
.umap-pin {
  position: relative; width: 34px; height: 34px; border-radius: 50%;
  border: 2px solid #fff; background: #e6f2ef; color: #0a7d68;
  font: 600 14px/1 Inter; display: flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0; box-shadow: 0 2px 8px rgba(16, 24, 24, .3);
}
.umap-pin img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
.umap-pin b {
  position: absolute; top: -6px; right: -9px; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9999px; background: #0a7d68; color: #fff; font: 600 11px/18px Inter;
}
/* A dot shared by several people opens this list - app chrome, not map paint,
   so it dresses like the member hover card (tokens, both themes) and each row
   is a member chip: hovering it floats the full hover card, clicking opens
   the member page. */
.umap-list {
  position: fixed; z-index: 310; min-width: 200px; max-width: 260px;
  background: var(--card-bg); border: 1px solid var(--line);
  border-radius: var(--card-radius); box-shadow: var(--shadow-pop);
  padding: 10px; display: flex; flex-direction: column; gap: 2px;
}
.umap-list-t { font: 600 12.5px/1.3 Inter; color: var(--muted-foreground); margin: 0 3px 6px; }
.umap-list-row {
  display: flex; align-items: center; gap: 9px; padding: 5px 6px; border-radius: 9999px;
  background: none; border: none; cursor: pointer; text-align: left; width: 100%;
  color: var(--foreground); font: 500 13px/1.3 Inter;
}
.umap-list-row:hover { background: var(--surface-hover); }
.umap-list-row .n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.umap-list-row .ua {
  width: 26px; height: 26px; border-radius: 50%; overflow: hidden; flex: none;
  background: var(--primary-12); color: var(--primary); display: inline-flex;
  align-items: center; justify-content: center; font: 600 11px/1 Inter;
}
.umap-list-row .ua img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The user map is the whole screen: viewport height minus the top bar, the
   page padding and the title row - .pos-map's clamp() is for a map that
   shares a page with a form, which this one does not. */
.pos-map.umap { height: calc(100dvh - 146px); min-height: 320px; }
.umap-count { margin-left: auto; font: 400 12px/1.4 Inter; color: var(--muted-foreground); }

/* ============================================================
   Market Signals - the feed page (Tools > Market Signals), and the
   coach's citation card.

   Cards on the page background, the accent only on links and the saved
   rule; gold for the worked example, because gold is for knowledge
   offered, never a verdict. It used to be a ticker under the positioning
   wizard - Matt moved it out (Sep 2026).
   ============================================================ */
.sig-page-sub { max-width: 860px; margin: 8px 0 0; }
.sig-toolbar { margin-bottom: 16px; }
.sig-filter {
  height: 32px; padding: 0 12px; border: 1px solid var(--line-strong); border-radius: 9999px;
  background: var(--card); color: var(--foreground); font: 500 12.5px/1 Inter; cursor: pointer;
}
.sig-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); gap: 14px; align-items: start; }
.sig-empty { grid-column: 1 / -1; }
.sig-card {
  border: 1px solid var(--line); border-radius: var(--card-radius); background: var(--card);
  padding: 15px 16px; transition: border-color .15s, box-shadow .15s;
}
.sig-card.is-saved { border-color: var(--primary-30); }
.sig-card.is-focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring); }
.sig-card-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.sig-card-src { display: inline-flex; align-items: center; gap: 4px; font: 600 11.5px/1 Inter; color: var(--foreground); text-decoration: none; }
.sig-card-src:hover { text-decoration: underline; text-underline-offset: 2px; }
.sig-card-date { font: 500 11px/1 Inter; color: var(--muted-2); }
.sig-card-h { margin: 0 0 9px; font: 600 14.5px/1.4 Inter; color: var(--foreground); }
.sig-card-row { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 8px; font: 400 12.5px/1.5 Inter; color: var(--foreground); margin-bottom: 5px; }
.sig-card-k { font: 600 11px/1.6 Inter; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: .04em; }
.sig-more { margin-top: 4px; }
.sig-more > summary { cursor: pointer; font: 500 12px/1 Inter; color: var(--muted-foreground); list-style: none; padding: 4px 0; }
.sig-more > summary::-webkit-details-marker { display: none; }
.sig-more > summary::before { content: '+ '; }
.sig-more[open] > summary::before { content: '- '; }
.sig-more[open] > summary { margin-bottom: 6px; }
.sig-example {
  display: flex; gap: 8px; align-items: flex-start; margin: 6px 0 8px; padding: 9px 11px;
  background: var(--gold-bg); border: 1px solid var(--gold-border); border-radius: var(--radius);
  font: 400 12.5px/1.5 Inter; color: var(--gold-fg);
}
.sig-example .ico { flex: none; margin-top: 2px; }
.sig-example em { display: block; margin-top: 4px; font-style: normal; color: var(--muted-foreground); }
.sig-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 4px; }
.sig-tag { font: 500 10.5px/1 Inter; color: var(--muted-foreground); padding: 3px 7px; border: 1px solid var(--line); border-radius: 9999px; }
.sig-card-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
@media (max-width: 900px) { .sig-grid { grid-template-columns: 1fr; } }

/* ---------- the patterns strip: what is forming across the cards ---------- */
.sig-themes { margin: 0 0 22px; }
.sig-theme-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; align-items: start; }
.sig-theme {
  border: 1px solid var(--gold-border); border-radius: var(--card-radius); background: var(--gold-bg);
  padding: 15px 16px;
}
.sig-theme-h { margin: 0 0 8px; font: 600 15px/1.35 Inter; color: var(--foreground); }
.sig-theme-p { margin: 0 0 10px; font: 400 12.5px/1.55 Inter; color: var(--foreground); }
.sig-theme-ev { display: flex; flex-wrap: wrap; gap: 6px; }
.sig-ev {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 9px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 9999px; background: var(--card);
  font: 500 11px/1 Inter; color: var(--foreground); text-decoration: none;
}
.sig-ev:hover { border-color: var(--line-strong); }
.sig-ev.is-hiring { color: var(--gold-fg); border-color: var(--gold-border); }

/* ---------- Admin > Coach Chats: a transcript in the side panel ---------- */
.cc-idbar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
.cc-idno { font: 600 13px/1 Inter; color: var(--foreground); font-variant-numeric: tabular-nums; margin-right: auto; }
.cc-copy {
  display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 9px;
  border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--card);
  color: var(--muted-foreground); font: 500 12px/1 Inter; cursor: pointer;
}
.cc-copy:hover { background: var(--surface-hover); color: var(--foreground); }
.cc-copy.ok { color: var(--primary); border-color: var(--primary); }
.cc-log { display: flex; flex-direction: column; gap: 10px; }
.cc-msg { max-width: 92%; font: 400 13px/1.5 Inter; color: var(--foreground); }
.cc-msg.is-me { align-self: flex-end; }
.cc-msg.is-me .cc-t { background: var(--primary-12); border-radius: 12px 12px 3px 12px; padding: 8px 11px; }
.cc-msg.is-coach .cc-t { background: var(--surface); border: 1px solid var(--line); border-radius: 12px 12px 12px 3px; padding: 8px 11px; }
.cc-at { font: 500 10.5px/1 Inter; color: var(--muted-2); margin-top: 4px; }
.cc-msg.is-me .cc-at { text-align: right; }
.cc-card { margin-top: 6px; padding: 7px 10px; border: 1px dashed var(--gold-border); border-radius: var(--radius); font: 400 12px/1.45 Inter; color: var(--foreground); }
.cc-card .ico { color: var(--gold-fg); vertical-align: -2px; }
.cc-note { align-self: center; max-width: 92%; font: 400 11px/1.45 Inter; color: var(--muted-2); text-align: center; padding: 2px 8px; }

/* ---------- the coach's citation card ---------- */
.coach-signal {
  border: 1px solid var(--line); border-left: 2px solid var(--primary);
  border-radius: var(--card-radius); background: var(--surface); padding: 11px 12px;
}
.coach-signal-t { font: 600 13px/1.4 Inter; color: var(--foreground); margin-bottom: 6px; }
.coach-signal .btn-sec { text-decoration: none; }

/* ---------- Admin > Market Signals ---------- */
.sig-status-ok { color: var(--primary); }
.sig-status-err { color: var(--red); }
.dtable td.sig-err { max-width: 260px; white-space: normal; font-size: 12px; color: var(--muted-foreground); }
