/* ==========================================================================
   Quanta CRM — light theme, matched to the Quanta portal's light mode
   (portal.quantainnovation.ai/templates/_base.html :root + components).

   Same surface ramp (#ffffff / #f8fafc / #f1f5f9 / #e2e8f0), same slate text
   ramp, same navy nav, same 16px card radius and soft two-stage shadows. The
   one deliberate divergence is IDENTITY: the portal's sky primary is replaced
   by a red one. Portal token names are canonical here; the board's original
   short names (--panel, --accent, --muted…) survive as aliases so the
   templates did not need rewriting.

   The login page keeps its own dark aurora treatment, scoped under
   body.page-aurora at the bottom of this file — it takes the same red.
   ========================================================================== */
:root {
  /* Identity — red where the portal is sky. --danger is deliberately a
     DEEPER red than the brand: with a red accent, a destructive button in
     the same hue reads as the primary action, so Delete is separated by
     tone + outline treatment rather than by hue alone. */
  --primary: #dc2626;
  --primary-dark: #b91c1c;
  --primary-light: #ef4444;
  --primary-rgb: 220, 38, 38;
  --primary-light-rgb: 239, 68, 68;
  --primary-glow: rgba(220, 38, 38, 0.08);
  --button-text: #ffffff;

  /* Navigation — the portal's navy banner */
  --nav-bg: #0f172a;
  --nav-text: #ffffff;
  --nav-text-rgb: 255, 255, 255;
  --nav-border: rgba(255, 255, 255, 0.08);

  /* Surfaces */
  --secondary: #0f172a;
  --secondary-rgb: 15, 23, 42;
  --bg-primary: #ffffff;
  --bg-secondary: #f8fafc;
  --bg-tertiary: #f1f5f9;
  --bg-elevated: #e2e8f0;
  --bg-page: var(--bg-secondary);

  /* Text */
  --text-primary: #0f172a;
  --text-secondary: #475569;
  --text-muted: #94a3b8;

  /* Lines + status */
  --border: rgba(15, 23, 42, 0.1);
  --border-light: rgba(15, 23, 42, 0.05);
  --success: #10b981;
  --warning: #d97706;
  --danger: #991b1b;

  /* Shadows, portal ramp */
  --shadow-sm: 0 1px 3px rgba(var(--secondary-rgb), 0.06);
  --shadow-md: 0 8px 24px rgba(var(--secondary-rgb), 0.10);
  --shadow-lg: 0 16px 40px rgba(var(--secondary-rgb), 0.16);
  --shadow-table: 0 1px 2px rgba(var(--secondary-rgb), 0.04),
                  0 14px 34px rgba(var(--secondary-rgb), 0.06);

  --radius: 16px;
  --radius-sm: 10px;

  /* Aliases kept for the board's original class names. The aurora tints are
     the values the portal's own _aurora_tints() derives from #dc2626 —
     "claret picks up gold". */
  --bg: var(--bg-page);
  --panel: var(--bg-primary);
  --panel-2: var(--bg-tertiary);
  --text: var(--text-primary);
  --muted: var(--text-muted);
  --accent: var(--primary);
  --accent-dim: var(--primary-dark);
  --won: var(--success);
  --warn: var(--warning);
  --shadow: var(--shadow-md);

  color-scheme: light;
}
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg-page); color: var(--text-primary);
  font: 14px/1.5 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---- header: navy banner (the tabs are in the sidebar below) ---- */
/* --appheader-h is the topbar (56), alone since the tabs moved to the
   sidebar (2026-10-16). Table headers stick directly below it, so the two
   must agree — change one, change the other. */
:root { --appheader-h: 56px; }
.appheader { position: sticky; top: 0; z-index: 20; }
.topbar {
  display: flex; align-items: center; gap: 22px; height: 56px;
  padding: 0 22px; background: var(--nav-bg); border-bottom: 1px solid var(--nav-border);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .1);
}
.brand { display: flex; align-items: center; gap: 10px;
  font-weight: 600; font-size: 16px; letter-spacing: -.3px; color: var(--nav-text); }
/* 26px, matching the design app's banner mark, so the two chromes sit at the
   same height. Flex is what puts the mark beside the wordmark — which is why
   the wordmark is wrapped in its own span: as loose text it would become a
   second flex item and take the 10px gap between "Quanta" and "CRM". */
.brand img { height: 26px; width: auto; }
.brand .q { color: var(--primary-light); }
.topbar .spacer { flex: 1; }

/* ---- topbar: bell + user menu (the portal's pair, _base.html
   .notif-bell-* / .user-menu-*, on this app's tokens) ---- */
/* Both controls take the same quiet outline on the navy — the portal fills
   its bell with --primary, which here is the badge's red, and a red count
   on a red disc is no count at all. */
.notif-bell-wrapper, .user-menu-wrapper { position: relative; }
.notif-bell-btn, .user-menu-btn {
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid rgba(var(--nav-text-rgb), .2);
  color: rgba(var(--nav-text-rgb), .85); cursor: pointer; transition: all .2s ease;
  font: inherit;
}
.notif-bell-btn:hover, .user-menu-btn:hover,
.notif-bell-wrapper.open .notif-bell-btn, .user-menu-wrapper.open .user-menu-btn {
  background: rgba(var(--nav-text-rgb), .12); border-color: rgba(var(--nav-text-rgb), .35);
  color: var(--nav-text);
}
.notif-bell-btn { position: relative; width: 36px; height: 36px; padding: 0; border-radius: 50%; font-size: 16px; line-height: 1; }
/* On the Notifications page itself the bell is the "active tab". */
.notif-bell-wrapper.current .notif-bell-btn { color: var(--nav-text); border-color: rgba(var(--nav-text-rgb), .45); }
/* The unread count. STATIC — no pulse, no glow: DECISIONS.md "Notifications"
   (an unread indicator never moves). The navy ring lifts it off the button's
   outline. Rendered only when the count is non-zero, so no visibility class. */
.notif-badge {
  position: absolute; top: -5px; right: -5px; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px; background: var(--primary); color: #fff; box-shadow: 0 0 0 2px var(--nav-bg);
  font-size: 11px; font-weight: 700; line-height: 18px; text-align: center; pointer-events: none;
}
.user-menu-btn { gap: 6px; padding: 4px 8px; border-radius: 20px; }
.user-avatar-small {
  width: 28px; height: 28px; border-radius: 50%; background: var(--primary); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600;
}
.dropdown-indicator { font-size: 10px; color: rgba(var(--nav-text-rgb), .6); transition: transform .2s ease; }
.user-menu-wrapper.open .dropdown-indicator { transform: rotate(180deg); }

/* The panels: light cards under the navy bar, hidden until .open. */
.notif-bell-dropdown, .user-menu-dropdown {
  position: absolute; top: calc(100% + 8px); right: 0;
  background: var(--bg-primary); border: 1px solid rgba(var(--secondary-rgb), .08);
  border-radius: 12px; box-shadow: 0 8px 32px rgba(var(--secondary-rgb), .12);
  opacity: 0; visibility: hidden; transform: translateY(-8px); transition: all .2s ease;
  z-index: 1001; overflow: hidden; text-align: left; font-weight: 400;
}
.notif-bell-wrapper.open .notif-bell-dropdown, .user-menu-wrapper.open .user-menu-dropdown {
  opacity: 1; visibility: visible; transform: translateY(0);
}
.user-menu-dropdown { min-width: 200px; }
.user-menu-header { padding: 12px 16px; border-bottom: 1px solid rgba(var(--secondary-rgb), .08); }
.user-menu-name { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.user-menu-email { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.user-menu-items { padding: 8px; }
.user-menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px;
  background: transparent; border: none; border-radius: 8px; color: var(--text-secondary);
  font-size: 13px; text-decoration: none; cursor: pointer; transition: all .15s ease;
}
.user-menu-item:hover { background: var(--bg-secondary); color: var(--text-primary); text-decoration: none; }
.user-menu-item.current { color: var(--text-primary); font-weight: 600; }
.user-menu-item i { width: 16px; text-align: center; font-size: 12px; }
.user-menu-item.danger { color: var(--danger); }
.user-menu-item.danger:hover { background: rgba(var(--primary-light-rgb), .1); }
.user-menu-divider { height: 1px; margin: 6px 4px; background: var(--border); }

.notif-bell-dropdown { width: 300px; max-width: calc(100vw - 24px); }
.notif-bell-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid rgba(var(--secondary-rgb), .08);
}
.notif-bell-head-title { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.notif-bell-list { max-height: min(70vh, 480px); overflow-y: auto; padding: 8px; }
/* The count sentence and the nothing-unread state share one row shape. */
.notif-bell-summary, .notif-bell-empty {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px; font-size: 13px;
}
.notif-bell-summary { color: var(--text-primary); font-weight: 600; }
.notif-bell-summary i { color: var(--primary); }
.notif-bell-empty { color: var(--text-muted); }
/* One unread row in the bell: title over kind + time. Plain, like the
   summary above it — an unread indicator never moves or glows. */
.notif-bell-item {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border-radius: 8px;
  color: var(--text-primary); font-size: 13px; transition: all .15s ease;
}
.notif-bell-item:hover { background: var(--bg-secondary); text-decoration: none; }
.notif-bell-item-title { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notif-bell-item-meta { font-size: 11px; color: var(--text-muted); }
.notif-bell-open {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 12px; border-radius: 8px; color: var(--text-secondary); font-size: 13px;
  transition: all .15s ease;
}
.notif-bell-open:hover { background: var(--bg-secondary); color: var(--text-primary); text-decoration: none; }

/* ---- the sidebar (2026-10-16) ----
   The tabs left the strip under the banner for a sidebar in named groups
   (James: "the qcrm nav is getting clustered and disorganised"). Light, like
   the strip it replaced, so the navy stays the banner's alone. It sits fixed
   under the 56px bar and the page takes a margin of its width; System is
   pushed to the foot. Hide, at its top, folds it to its icons and Show opens
   it again with every label and group heading (html.nav-collapsed,
   remembered per browser); at 900px and under it is a drawer the bar's Menu
   button opens over the page (html.nav-open). */
:root { --sidebar-w: 224px; }
html.nav-collapsed { --sidebar-w: 64px; }
/* The bar's Menu button opens the drawer, at 900px and under only. */
.nav-toggle {
  display: none; align-items: center; justify-content: center; width: 36px; height: 36px; margin-right: -8px;
  background: transparent; border: 0; border-radius: 8px; cursor: pointer;
  color: rgba(var(--nav-text-rgb), .85); font-size: 17px;
}
.nav-toggle:hover { background: rgba(var(--nav-text-rgb), .12); color: var(--nav-text); }
.sidebar {
  position: fixed; top: 56px; bottom: 0; left: 0; z-index: 19; width: var(--sidebar-w);
  display: flex; flex-direction: column;
  background: var(--bg-primary); border-right: 1px solid var(--border);
  overflow: hidden auto; overscroll-behavior: contain;
}
body.has-sidebar #page { margin-left: var(--sidebar-w); }
.subnav { flex: 1 0 auto; display: flex; flex-direction: column; gap: 14px; padding: 16px 10px 10px; }
.nav-group { display: flex; flex-direction: column; gap: 2px; }
.nav-group-system { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--border-light, var(--border)); }
.nav-group-label {
  padding: 0 10px 4px; color: var(--text-muted);
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
}
.subnav a {
  position: relative; display: flex; align-items: center; gap: 11px;
  padding: 8px 10px; border-radius: 8px;
  color: var(--text-secondary); font-weight: 500; font-size: 14px; line-height: 1.2;
  transition: background-color .15s ease, color .15s ease;
}
.subnav a i { flex: 0 0 18px; width: 18px; text-align: center; font-size: 14px; color: var(--text-muted);
  transition: color .15s ease; }
.subnav a .tab-label { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.subnav a:hover { background: var(--bg-secondary); color: var(--text-primary); text-decoration: none; }
.subnav a:hover i { color: var(--text-secondary); }
/* The open tab: the brand's glow behind it and a red bar at its edge, where
   the strip had its underline. */
.subnav a.active { background: var(--primary-glow); color: var(--text-primary); font-weight: 600; }
.subnav a.active i { color: var(--primary); }
.subnav a.active::before { content: ''; position: absolute; left: -10px; top: 7px; bottom: 7px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--primary); }
.subnav a:focus-visible, .sidebar-hide:focus-visible, .nav-toggle:focus-visible {
  outline: 2px solid rgba(var(--primary-rgb), .4); outline-offset: 1px; }
.sidebar-scrim { display: none; }
/* The one toggle, at the top of the panel (James, 2026-10-16: "have it at
   the top of the panel rather than in the top banner", then "the show
   setting should fully open the panel to make titles and categories
   visible"): Hide sidebar folds the panel to its icons, and the same button,
   now an icon, shows it again. */
.sidebar-head { padding: 10px 10px 0; }
.sidebar-hide {
  display: flex; align-items: center; gap: 11px; width: 100%; padding: 7px 10px;
  background: transparent; border: 0; border-radius: 8px; cursor: pointer;
  color: var(--text-muted); font: inherit; font-size: 13px; text-align: left;
}
.sidebar-hide i { flex: 0 0 18px; width: 18px; text-align: center; }
.sidebar-hide:hover { background: var(--bg-secondary); color: var(--text-primary); }
html.nav-collapsed .sidebar-head { padding: 10px 8px 0; }
html.nav-collapsed .sidebar-hide { justify-content: center; padding: 9px 0; }
html.nav-collapsed .sidebar-hide .tab-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Folded to icons: the labels go (kept for screen readers), each group's
   heading becomes a rule, and a tab's count sits small under its icon. */
html.nav-collapsed .subnav { padding-left: 8px; padding-right: 8px; }
html.nav-collapsed .subnav a { flex-direction: column; justify-content: center; gap: 3px; padding: 8px 0 7px; }
html.nav-collapsed .subnav a.active::before { left: -8px; }
html.nav-collapsed .subnav .tab-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
html.nav-collapsed .nav-group-label { height: 1px; padding: 0; margin: 0 8px 6px; overflow: hidden;
  background: var(--border); color: transparent; }
html.nav-collapsed .nav-group-system { border-top: 0; }
html.nav-collapsed .subnav a .tab-count { min-width: 0; padding: 0; background: none;
  font-size: 9.5px; letter-spacing: 0; }
html.nav-collapsed .subnav a.active .tab-count { background: none; }
@media (max-width: 900px) {
  html.nav-collapsed { --sidebar-w: 224px; }
  .sidebar { width: min(272px, 86vw); transform: translateX(-100%); transition: transform .2s ease;
    box-shadow: none; z-index: 30; }
  html.nav-open .sidebar { transform: none; box-shadow: 0 12px 40px rgba(var(--secondary-rgb), .25); }
  body.has-sidebar #page { margin-left: 0; }
  html.nav-open .sidebar-scrim { display: block; position: fixed; inset: 56px 0 0 0; z-index: 29;
    background: rgba(15, 23, 42, .35); }
  /* The drawer always opens with its labels: folding is a wide-window idea. */
  html.nav-collapsed .subnav { padding: 16px 10px 10px; }
  html.nav-collapsed .subnav a { flex-direction: row; justify-content: flex-start; gap: 11px; padding: 8px 10px; }
  html.nav-collapsed .subnav .tab-label { position: static; width: auto; height: auto; overflow: hidden; clip: auto; }
  html.nav-collapsed .nav-group-label { height: auto; padding: 0 10px 4px; margin: 0; background: none; color: var(--text-muted); }
  html.nav-collapsed .nav-group-system { border-top: 1px solid var(--border-light, var(--border)); }
  html.nav-collapsed .subnav a .tab-count { padding: 2px 7px; font-size: 10.5px; letter-spacing: .02em;
    background: var(--bg-tertiary); }
  html.nav-collapsed .subnav a.active .tab-count { background: rgba(var(--primary-rgb), .12); }
  .nav-toggle { display: flex; }
  .sidebar-head { display: none; }
}
@media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }
/* The count beside each tab (2026-09-16): how many items the tab's first list
   holds. A quiet pill, not a badge: muted until its tab is open, then it takes
   the brand's red on its own glow. It never animates; like the bell's badge,
   a number changing is not a thing earned. */
.subnav .tab-count, .pagetab .tab-count {
  min-width: 24px; padding: 2px 7px; border-radius: 999px;
  background: var(--bg-tertiary); color: var(--text-muted);
  font-size: 10.5px; font-weight: 600; line-height: 1.2; letter-spacing: .02em;
  text-align: center; font-variant-numeric: tabular-nums;
  transition: background-color .2s ease, color .2s ease;
}
.subnav a.active .tab-count { background: rgba(var(--primary-rgb), .12); color: var(--primary); }

/* ---- tabs inside a page (the Scout tab, 2026-09-16) ----
   One list at a time instead of a page of tables one after another (James:
   "change the layout of the scout page into tabs rather than subsequent
   tables"). Each tab is a radio inside its label, outside any form, which is
   exactly the control static/inplace.js puts back after a submit: a click in
   a list re-renders on the tab it came from without a script of its own. The
   strip's look is the nav's, one size down, with the count beside the label.

   The contract: .pagetab-panel sections are children of .pagetabs, in the
   same order as the labels in .pagetabs-strip, and the nth checked label
   shows the nth panel. Where :has() is missing every panel shows, one after
   another, the way the page read before. An empty list keeps its tab so the
   strip never reshapes under the hand; it just steps back. */
.pagetabs-strip { display: flex; flex-wrap: wrap; gap: 0 24px; margin: 26px 0 16px;
  border-bottom: 1px solid var(--border); }
.pagetab { position: relative; display: inline-flex; align-items: center; gap: 7px;
  margin: 0 0 -1px; padding: 10px 1px 10px; border-bottom: 2px solid transparent;
  color: var(--text-secondary); font-size: 14px; font-weight: 500; line-height: 1.2;
  white-space: nowrap; cursor: pointer; user-select: none;
  transition: color .2s ease, border-color .2s ease; }
.pagetab input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0;
  border: 0; opacity: 0; cursor: pointer; }
.pagetab.is-empty { color: var(--text-muted); }
.pagetab:hover { color: var(--primary); }
.pagetab:hover .tab-count { color: var(--primary); }
.pagetab:has(input:checked) { color: var(--text-primary); border-bottom-color: var(--primary); }
.pagetab:has(input:checked) .tab-count { background: var(--primary-glow); color: var(--primary); }
.pagetab:has(input:focus-visible) { outline: 2px solid rgba(var(--primary-rgb), .35);
  outline-offset: 2px; border-radius: 6px 6px 0 0; }
.tabnote { margin: 0 0 14px; }
.pagetab-panel > .tabnote:last-child { margin: 14px 0 0; }
@supports selector(:has(*)) {
  .pagetabs > .pagetab-panel { display: none; }
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(1) > input:checked) > .pagetab-panel:nth-of-type(1),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(2) > input:checked) > .pagetab-panel:nth-of-type(2),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(3) > input:checked) > .pagetab-panel:nth-of-type(3),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(4) > input:checked) > .pagetab-panel:nth-of-type(4),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(5) > input:checked) > .pagetab-panel:nth-of-type(5),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(6) > input:checked) > .pagetab-panel:nth-of-type(6),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(7) > input:checked) > .pagetab-panel:nth-of-type(7),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(8) > input:checked) > .pagetab-panel:nth-of-type(8),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(9) > input:checked) > .pagetab-panel:nth-of-type(9),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(10) > input:checked) > .pagetab-panel:nth-of-type(10),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(11) > input:checked) > .pagetab-panel:nth-of-type(11),
  .pagetabs:has(> .pagetabs-strip > .pagetab:nth-of-type(12) > input:checked) > .pagetab-panel:nth-of-type(12) {
    display: block; }
}

.wrap { padding: 32px 22px 64px; max-width: 1600px; margin: 0 auto; }
h1 { font-size: 26px; font-weight: 700; letter-spacing: -.02em; line-height: 1.2; margin: 0 0 6px; }
.sub { color: var(--text-secondary); margin: 0 0 24px; }

/* ---- metrics strip ---- */
.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px,1fr)); gap: 12px; margin-bottom: 24px; }
.metric { background: var(--bg-primary); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow-sm); }
.metric .n { font-size: 24px; font-weight: 700; letter-spacing: -.02em; }
.metric .l { color: var(--text-muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .6px; font-weight: 600; margin-top: 2px; }
.metric.accent .n { color: var(--primary); }
.metric.won .n { color: var(--success); }
/* The product page's headline cards (2026-10-16): the metrics tiles carrying
   words rather than numbers, so the value is smaller and wraps; Pricing takes
   two columns. The title row holds the edit icon that opens the dialog. */
.headline-title { display: flex; align-items: center; gap: 10px; }
.headline-title h1 { margin-bottom: 0; }
.btn.icon-only { padding: 6px 9px; line-height: 1; }
.headline-desc { margin: 6px 0 14px; max-width: 760px; }
.headline-cards { margin-bottom: 26px; }
.headline-cards .metric .n { font-size: 17px; font-weight: 650; letter-spacing: -.01em; line-height: 1.3; overflow-wrap: anywhere; }
.headline-cards .metric.wide { grid-column: span 2; }
.headline-cards .metric .note { margin-top: 6px; font-size: 12px; color: var(--text-secondary); }
@media (max-width: 600px) { .headline-cards .metric.wide { grid-column: auto; } }

/* ---- filter bar ---- */
.filterbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 18px; }
.filterbar input[type=search] { max-width: 280px; }
.filterbar select { width: auto; }
/* Default-ON checkbox filter (023): reads as a control, not as body text,
   and sits at the same height as the selects beside it. */
.filterbar .filtertoggle { display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--text-secondary); white-space: nowrap;
  cursor: pointer; user-select: none; }
.filterbar .filtertoggle input { width: auto; margin: 0; cursor: pointer; }

/* A retired row still reads, it just stops competing for attention. Opacity
   alone would fade the inputs it contains past legibility, so the muting is
   on the row's own ground and its non-editable cells. */
tr.row-dim { background: var(--bg-tertiary); }
tr.row-dim > td:not(:has(input, select, textarea)) { color: var(--text-muted); }
.filterhint { color: var(--text-muted); font-size: 12px; }
.card.hidden, tr.hidden { display: none; }

/* ---- kanban board ---- */
.board { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 10px; align-items: flex-start; }
.col { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius);
       min-width: 215px; max-width: 240px; flex: 0 0 auto; box-shadow: var(--shadow-sm); }
.col.col-dim { background: var(--bg-tertiary); min-width: 180px; }
.col h3 { margin: 0; padding: 12px 14px; font-size: 12px; font-weight: 600; text-transform: uppercase;
          letter-spacing: .6px; color: var(--text-muted); border-bottom: 1px solid var(--border-light);
          display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.col h3 a { color: inherit; }
.col h3 a:hover { color: var(--primary); text-decoration: none; }
.col h3 .cnt { background: var(--bg-elevated); color: var(--text-secondary); border-radius: 20px;
               padding: 1px 8px; font-size: 11px; letter-spacing: 0; }
.col .cards { padding: 8px; display: flex; flex-direction: column; gap: 8px; min-height: 40px; }
.card { background: var(--bg-primary); border: 1px solid var(--border); border-radius: 12px; padding: 11px;
        cursor: grab; box-shadow: var(--shadow-sm); transition: box-shadow .2s ease, border-color .2s ease; }
.card:hover { border-color: rgba(var(--primary-rgb), .35); box-shadow: var(--shadow-md); }
.card.due { border-left: 3px solid var(--warning); }
.card .nm { font-weight: 600; }
.card .meta { color: var(--text-secondary); font-size: 12px; margin-top: 3px; }
.card .chips { margin-top: 7px; display: flex; flex-wrap: wrap; gap: 4px; }
.chip { font-size: 11px; padding: 1px 8px; border-radius: 20px; background: rgba(var(--primary-rgb), .08);
        color: var(--primary-dark); border: 1px solid rgba(var(--primary-rgb), .2); font-weight: 500; }
.chip.region { background: var(--bg-tertiary); color: var(--text-secondary); border-color: var(--border); }
.chip.warn { background: rgba(217,119,6,.1); color: #b45309; border-color: rgba(217,119,6,.28); }
.chip.ok { background: rgba(16,185,129,.1); color: #047857; border-color: rgba(16,185,129,.28); }
.card.dragging { opacity: .5; }
.col.dragover { outline: 2px dashed rgba(var(--primary-rgb), .5); outline-offset: -4px; }

/* ---- phase groups (107) ----
   A board's columns sit under their sales-pipeline phase. The group is a
   wrapper only: every .col, .cnt and .cards inside is the markup the board
   scripts already query, so filters, counts and drag-and-drop are unchanged.
   A stage with no phase keeps a blank header so its column lines up. */
.board > .phase-group { flex: 0 0 auto; display: flex; flex-direction: column; gap: 8px; }
.board > .phase-group + .phase-group { margin-left: 8px; }
.phase-head { display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 26px; padding: 4px 4px 6px;
              border-bottom: 2px solid rgba(var(--primary-rgb), .45); font-size: 13px; font-weight: 600;
              color: var(--text-primary); }
.phase-head a { color: inherit; }
.phase-head a:hover { color: var(--primary); text-decoration: none; }
.phase-head .phase-cnt { background: rgba(var(--primary-rgb), .08); color: var(--primary-dark);
                         border-radius: 20px; padding: 1px 8px; font-size: 11px; font-weight: 500; }
.phase-group.phase-none .phase-head { border-bottom-color: transparent; }
.phase-group.phase-dim .phase-head { border-bottom-color: var(--border); color: var(--text-secondary); }
.phase-cols { display: flex; gap: 12px; align-items: flex-start; }

/* ---- names-only view ----
   The view selector on People and Companies collapses every record to the one
   thing that identifies it. Purely a display class on the container: the
   markup is already rendered, so switching back costs no request, and the
   filters keep working on the rows underneath. */
.board.compact .col .cards { gap: 4px; }
/* Group by company on People (2026-10-15): a heading before each company's
   run of cards in a column; links to the company page. */
.cards .co-head { display: flex; align-items: center; gap: 6px; margin: 8px 2px 4px;
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-secondary); }
.cards .co-head:first-child { margin-top: 0; }
.cards a.co-head:hover { color: var(--primary); text-decoration: none; }
.cards .co-head .co-n { font-weight: 500; color: var(--text-secondary); opacity: .8; }
.cards .co-head.hidden { display: none; }
/* A deal card's network line (109): muted when nobody there is connected. */
.card .meta.net-none { font-style: italic; }
.board.compact .card { padding: 5px 10px; cursor: grab; }
.board.compact .card .meta,
.board.compact .card .chips { display: none; }
.board.compact .card .nm { font-weight: 500; font-size: 13px; white-space: nowrap;
                           overflow: hidden; text-overflow: ellipsis; }
table.compact th:not(:first-child),
table.compact td:not(:first-child) { display: none; }

/* ---- tables ---- */
/* .tablewrap scrolls BOTH ways: it already had to be a scroll container for
   wide tables, and a scroll container is what a sticky header sticks to — so
   capping its height to the space under the nav is what keeps the header
   visible on a long table. Short tables never reach the cap and behave as
   before. Bare (unwrapped) tables scroll with the page instead, so theirs
   sticks to the viewport at --appheader-h. */
.tablewrap { overflow: auto; max-height: calc(100vh - var(--appheader-h) - 40px);
             border-radius: var(--radius); background: var(--bg-primary);
             border: 1px solid var(--border-light); box-shadow: var(--shadow-table); }
/* clip, not hidden: hidden would make every table its own scrollport and the
   sticky header would resolve against that instead of the page. */
table { width: 100%; border-collapse: separate; border-spacing: 0; background: var(--bg-primary);
        border: 1px solid var(--border-light); border-radius: var(--radius); overflow: clip;
        box-shadow: var(--shadow-table); font-size: 14px; }
.tablewrap table { border: none; border-radius: 0; box-shadow: none; }
/* A wrapper whose table fits across has no reason to be a scroll container,
   and being one is exactly what would stop its header sticking to the PAGE.
   The script in base.html tags those, so only genuinely wide tables (the
   stage editor) scroll inside their own box. clip, not visible: it still has
   to cut the table's square corners to the wrapper's radius. */
.tablewrap.fits { overflow: clip; max-height: none; }
/* The wide case keeps its own scroll box — and pins under the nav, or the
   PAGE scroll would slide the box (and the header stuck to its top) up
   behind the banner. */
.tablewrap:not(.fits) { position: sticky; top: var(--appheader-h); }
th { text-align: left; background: var(--bg-secondary); padding: 14px 20px; font-weight: 600;
     font-size: 11.5px; text-transform: uppercase; letter-spacing: .6px; color: var(--text-muted);
     border-bottom: 1px solid var(--border-light); white-space: nowrap;
     position: sticky; top: var(--appheader-h); z-index: 3; }
.tablewrap th { top: 0; }
.tablewrap.fits th { top: var(--appheader-h); }
td { text-align: left; padding: 16px 20px; vertical-align: middle; color: var(--text-primary);
     border-bottom: 1px solid var(--border-light); }
tr:last-child td { border-bottom: none; }
tbody tr { transition: background .15s ease; }
tbody tr:hover td { background: rgba(var(--primary-rgb), .045); }

/* ---- stage table (inline editing) ---- */
.wrap-wide { max-width: none; }
.crumb { color: var(--text-muted); }
.edit-table th { white-space: nowrap; padding: 12px 8px; }
.edit-table td { padding: 4px 6px; }
.edit-table input, .edit-table select {
  min-width: 110px; padding: 6px 8px; border-radius: 8px; font-size: 13px;
  background: transparent; border-color: transparent; box-shadow: none;
}
.edit-table input.narrow { min-width: 90px; }
/* An estimate in hours (101): a few digits, so the column stays narrow. */
.edit-table input.est { min-width: 0; width: 72px; }
.edit-table input:hover, .edit-table select:hover { border-color: var(--border); background: var(--bg-secondary); }
.edit-table input:focus, .edit-table select:focus {
  border-color: var(--primary); background: var(--bg-primary);
  box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .12); outline: none;
}
.edit-table td.cb { text-align: center; }
.edit-table td.cb input { min-width: 0; width: auto; }
.edit-table td.li-cell { white-space: nowrap; }
.edit-table td.li-cell input { max-width: 130px; }
.edit-table tr.saving td { opacity: .55; }
.edit-table tr.saved td { background: rgba(16,185,129,.09); }
.edit-table tr.err td { background: rgba(220,38,38,.09); }
.edit-table tr.moved td { opacity: .45; }

/* ---- forms / buttons ---- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px;
       background: var(--primary); color: var(--button-text); border: none; border-radius: var(--radius-sm);
       padding: 12px 20px; font-weight: 500; font-size: 14px; font-family: inherit;
       cursor: pointer; transition: all .2s ease; text-decoration: none; }
.btn:hover { background: var(--primary-dark); box-shadow: 0 4px 20px rgba(var(--primary-light-rgb), .30); text-decoration: none; }
.btn.ghost { background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); box-shadow: none; }
.btn.ghost:hover { background: var(--bg-elevated); border-color: var(--primary); box-shadow: none; }
/* Destructive: outline + the deeper red, so it never competes with a solid
   brand-red primary action sitting beside it. */
.btn.danger { background: transparent; color: var(--danger); border: 1px solid rgba(153,27,27,.4); box-shadow: none; }
.btn.danger:hover { background: var(--danger); color: #fff; border-color: var(--danger); box-shadow: none; }
.btn.sm { padding: 7px 14px; font-size: 13px; }
input, select, textarea {
  width: 100%; background: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 10px 12px; font: inherit; transition: all .2s ease;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .12);
}
input[type=checkbox] { accent-color: var(--primary); }
textarea { min-height: 70px; resize: vertical; }
label { display: block; color: var(--text-secondary); font-size: 12px; font-weight: 500; margin: 14px 0 5px; }
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
.actions { margin-top: 18px; display: flex; gap: 10px; }

.panel { background: var(--bg-primary); border: 1px solid var(--border); border-radius: var(--radius);
         padding: 20px; margin-bottom: 18px; box-shadow: var(--shadow-sm); }
.panel h2 { margin: 0 0 14px; font-size: 16px; font-weight: 600; letter-spacing: -.01em; }
.badge { font-size: 11px; padding: 2px 9px; border-radius: 20px; background: var(--bg-tertiary);
         color: var(--text-secondary); border: 1px solid var(--border); font-weight: 500; }
.badge.warn { background: rgba(217,119,6,.1); color: #b45309; border-color: rgba(217,119,6,.28); }
/* Green spends the same token the ok flash does, so it is used the same way:
   only on a genuinely completed good state (a screening report APPLIED — the
   end of a promote consent), never on routine confirmations. */
.badge.ok { background: rgba(16,185,129,.1); color: #047857; border-color: rgba(16,185,129,.28); }
/* Where an action came from (095), beside its category: outlined rather than
   filled, so a source never reads as a second category. */
.badge.source { background: transparent; color: var(--text-muted); }
/* Untyped: NEUTRAL, not brand red. It used to take the primary tint, which
   with a red identity meant "Saved." and "Save failed" were the same colour —
   the app could not say anything was good because it could not say anything
   was ordinary either. Typed variants live in the positive-feedback block. */
.flash { padding: 12px 16px; border-radius: var(--radius-sm); margin-bottom: 16px;
         background: var(--bg-tertiary); border: 1px solid var(--border);
         color: var(--text-secondary); }
.timeline { list-style: none; padding: 0; margin: 0; }
.timeline li { padding: 10px 0; border-bottom: 1px solid var(--border-light); }
.timeline li:last-child { border-bottom: none; }
.timeline .t { color: var(--text-muted); font-size: 12px; margin-top: 2px; }
/* An activity's text keeps the lines it was written in (103: a client
   update's subject and recipients, then the email folded beneath them). */
.activity-body { white-space: pre-line; }
.activity-more { margin-top: 4px; }
.activity-more summary { cursor: pointer; font-size: 12px; color: var(--text-muted); }
.activity-more summary:hover { color: var(--primary); }
.activity-more .activity-body { display: block; margin-top: 6px; padding-left: 10px;
                                border-left: 2px solid var(--border-light); }
.empty { color: var(--text-muted); padding: 40px; text-align: center; }

.muted-link { color: rgba(var(--nav-text-rgb), .75); font-size: 13px; }
.muted-link:hover { color: var(--nav-text); }

/* ---- actions (the to-do queue, migration 011) ---- */
/* The tick is a form submit dressed as a checkbox: no JS state to manage,
   and the POST-redirect keeps the page honest about what is saved. */
.tickform { display: inline; }
.tick { background: none; border: none; padding: 0 6px 0 0; cursor: pointer;
        font-size: 16px; color: var(--text-muted); transition: color .15s ease; vertical-align: -1px; }
.tick:hover { color: var(--primary); }
.action-row.done .action-title, tr.done .action-title { text-decoration: line-through; color: var(--text-muted); }
.action-row.done .tick, tr.done .tick { color: var(--success); }
tr.done td { opacity: .65; }
/* Hide (094): a done action taken out of view, not deleted. Quiet on purpose
   and never in the success tone — it is housekeeping, not a win. */
.hide-toggle { background: none; border: none; padding: 0 0 0 8px; cursor: pointer;
               font-size: 12px; color: var(--text-muted); white-space: nowrap;
               transition: color .15s ease; }
.hide-toggle:hover { color: var(--primary); }
td .hide-toggle { padding: 0 10px 0 0; }
/* Block time (2026-10-04): a link out to Google Calendar on an open action,
   dressed as quietly as Hide — booking time is planning, not a win. */
.block-time { padding-left: 8px; font-size: 12px; color: var(--text-muted); white-space: nowrap;
              transition: color .15s ease; }
.block-time:hover { color: var(--primary); }
/* Since 2026-10-04 Block time is a button, a submit in its own form:
   dressed as the link it replaced. */
button.block-time { background: none; border: 0; font-family: inherit; line-height: inherit;
                    cursor: pointer; }
button.block-time:disabled { opacity: .5; cursor: progress; }
td .block-time { padding: 0 10px 0 0; }
/* Update client (103): on done client work, where Block time sits on open
   work, and dressed the same: telling the client is housekeeping, not a win. */
button.client-update { background: none; border: 0; font-family: inherit; line-height: inherit;
                       padding-left: 8px; font-size: 12px; color: var(--text-muted);
                       white-space: nowrap; cursor: pointer; transition: color .15s ease; }
button.client-update:hover { color: var(--primary); }
td .client-update { padding: 0 10px 0 0; }
.card .card-foot .client-update { padding-left: 0; }
/* What is booked, read live from Google Calendar (bookings.js). Quiet like
   Block time; a booking whose time went by with the action still open takes
   the warn colour, because that is work slipping, not a fault. */
.booking { padding-left: 8px; font-size: 12px; color: var(--text-secondary); white-space: nowrap;
           transition: color .15s ease; }
.booking:hover { color: var(--primary); }
.booking.passed { color: #b45309; }
td .booking { display: inline-block; padding: 4px 0 0; }
/* Room between bookings, should a holder ever show more than one. */
.booking + .booking { margin-left: 10px; }
.hidden-actions-toggle { display: inline-block; padding: 10px 0 2px; }
/* One control per detail-panel row (094, James): Hide on a done action, Delete
   on an open one. Floated to the row's right edge at one size so the eye finds
   it on every row; flow-root keeps the float inside the row's border. Delete
   turns red only on hover, because every open row carries one and a column of
   red would shout over the work itself. */
.timeline li.action-row { display: flow-root; }
.action-row .action-ctl { float: right; margin-left: 12px; }
.ctl { background: var(--bg-primary); border: 1px solid var(--border); border-radius: var(--radius-sm);
       padding: 3px 9px; font: inherit; font-size: 12px; color: var(--text-secondary);
       cursor: pointer; white-space: nowrap; transition: color .15s ease, border-color .15s ease; }
.ctl:hover { background: var(--bg-elevated); border-color: var(--primary); color: var(--text-primary); }
.ctl.del:hover { background: transparent; border-color: rgba(153,27,27,.4); color: var(--danger); }

/* ---- projects (099) ---- */
/* Progress is a measure, not a win: a neutral slate fill on the tertiary
   track. Green stays for genuinely completed good states (the .badge.ok
   rule), and red would read a half-done project as an alarm. */
.progress { height: 6px; border-radius: 999px; background: var(--bg-elevated); overflow: hidden;
            min-width: 80px; }
.progress span { display: block; height: 100%; background: var(--text-secondary);
                 border-radius: 999px; }
.crumb.warn { color: #b45309; }
.badge.date { white-space: nowrap; }
/* The project board: three derived columns sharing the width rather than the
   pipeline boards' fixed narrow ones, because there are only ever three. */
.project-board .col { flex: 1 1 0; min-width: 240px; max-width: none; }
/* A click opens the card's editor and a drag still moves it (2026-10-04). */
.project-board .card { cursor: pointer; }
.project-board .card.dragging { cursor: grabbing; }
.card .card-open { cursor: pointer; border-radius: 4px; }
.card .card-open:hover { color: var(--primary); }
.card .card-open:focus-visible { outline: 2px solid rgba(var(--primary-rgb), .5); outline-offset: 2px; }
.project-board .card.done .action-title { text-decoration: line-through; color: var(--text-muted); }
.project-board .card.done .tick { color: var(--success); }
.card .card-head { display: flex; align-items: flex-start; gap: 2px; }
.card .card-foot { margin-top: 8px; font-size: 12px; display: flex; flex-wrap: wrap; gap: 4px 10px;
                   align-items: center; }
.card .card-foot .block-time, .card .card-foot .booking { padding-left: 0; }
/* A booking line with its hours ("· 4 h of 6 h booked") wraps inside a card. */
.card .card-foot .booking { white-space: normal; }
.col .col-foot { padding: 0 14px 12px; font-size: 12px; }
/* The Done column's Clear (2026-10-05): the count and a small pill beside it. */
.col h3 .col-tools { display: flex; align-items: center; gap: 6px; }
.col h3 .col-tools form { margin: 0; display: flex; }
.col-clear { font: inherit; font-size: 11px; letter-spacing: 0; text-transform: none;
  background: none; border: 1px solid var(--border); border-radius: 20px;
  padding: 1px 9px; color: var(--text-secondary); cursor: pointer; }
.col-clear:hover { color: var(--primary); border-color: var(--primary); }
/* Armed by the first click (2026-10-07): the second one clears. */
form.armed .col-clear { color: var(--danger); border-color: var(--danger);
  background: rgba(var(--primary-rgb), .06); font-weight: 600; }
.col-clear:focus-visible { outline: 2px solid rgba(var(--primary-rgb), .5); outline-offset: 2px; }
.badge.project { color: var(--text-primary); }
.badge.project:hover { border-color: var(--primary); text-decoration: none; }
.timeline li.project-closed { opacity: .65; }

/* ---- detail pages: the portal's AI artifact shell (2026-10-17) ----
   James: "change the layout and styling of a project detail page to match
   that of an ai artifact detail page in quanta portal (alert, segment etc.)".
   Copied from the portal's templates/_detail_shell.html and _base.html
   (.crumbs) with its numbers kept: breadcrumbs, an uppercase kind label over
   a 28px title and its status pill, a meta line, actions top right (one
   primary, the rest in a ⋯ menu), tabs, then sections of an 18px title over
   a 16px-radius card. QCRM's red is the primary, as everywhere here. The tabs
   are the .pagetabs radios, restyled, so inplace.js keeps the open one. */
.detail-wrap { max-width: 1320px; }
.crumbs { margin-bottom: 20px; font-size: 13px; font-weight: 500; color: var(--text-muted); }
.crumbs ol { display: flex; flex-wrap: wrap; align-items: center; row-gap: 4px; margin: 0; padding: 0; list-style: none; }
.crumbs li { display: flex; align-items: center; min-width: 0; max-width: 100%; }
.crumbs a, .crumbs [aria-current="page"] { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crumbs a { color: var(--text-muted); text-decoration: none; transition: color .2s ease; }
.crumbs a:hover { color: var(--primary); text-decoration: none; }
.crumbs li:not(:last-child) a { max-width: 36ch; }
.crumbs [aria-current="page"] { color: var(--text-secondary); }
.crumbs a > i { margin-right: 6px; font-size: 11px; }
.crumbs-sep { flex: none; margin: 0 9px; font-size: 9px; opacity: .6; }

.detail-identity { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px 32px;
  flex-wrap: wrap; margin-bottom: 24px; }
.detail-identity-left { display: flex; flex-direction: column; gap: 6px; min-width: 0; flex: 1 1 420px; }
.detail-kind { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--primary); }
.detail-kind i { font-size: 11px; }
.detail-title { margin: 0; font-size: 28px; font-weight: 700; letter-spacing: -.02em; line-height: 1.2;
  color: var(--text-primary); display: flex; align-items: center; gap: 12px; flex-wrap: wrap; overflow-wrap: anywhere; }
.detail-title .pill { position: relative; top: 1px; }
.detail-description { margin: 0; font-size: 14px; color: var(--text-secondary); line-height: 1.5; max-width: 64ch;
  white-space: pre-line; }
.detail-meta { display: flex; gap: 6px 16px; flex-wrap: wrap; font-size: 13px; color: var(--text-muted); margin-top: 2px; }
.detail-meta > span, .detail-meta > .meta-btn { display: inline-flex; align-items: center; gap: 6px; }
.detail-meta i { font-size: 11px; opacity: .8; }
.detail-meta a { color: var(--text-secondary); }
.detail-meta a:hover { color: var(--primary); }
.detail-meta .warn { color: #b45309; }
.detail-meta .recurring { display: block; }
.meta-btn { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
.meta-btn:hover { color: var(--primary); }

.detail-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; flex-shrink: 0; }
.action-btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: 8px;
  font-size: 13px; font-weight: 500; font-family: inherit; cursor: pointer; border: 1px solid var(--border);
  background: var(--bg-secondary); color: var(--text-primary); text-decoration: none;
  transition: all .15s ease; white-space: nowrap; line-height: 1.4; }
.action-btn:hover { border-color: var(--primary); color: var(--primary); text-decoration: none; }
.action-btn.primary { background: var(--primary); color: var(--button-text); border-color: var(--primary); }
.action-btn.primary:hover { background: var(--primary-dark); color: var(--button-text); }
.action-btn[disabled] { opacity: .5; cursor: not-allowed; }
.action-menu { position: relative; }
.action-menu-btn { padding: 8px 11px; }
.action-menu-btn i { font-size: 14px; }
.action-menu-list { position: absolute; right: 0; top: calc(100% + 6px); min-width: 220px; background: var(--bg-primary);
  border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-lg); padding: 6px;
  display: none; z-index: 60; }
.action-menu.open .action-menu-list { display: block; }
.action-menu-list form { margin: 0; }
.action-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 9px 10px;
  border-radius: 7px; border: none; background: none; font-size: 13px; font-weight: 500;
  color: var(--text-primary); cursor: pointer; text-decoration: none; font-family: inherit; line-height: 1.3; }
.action-menu-item:hover { background: var(--bg-tertiary); color: var(--text-primary); text-decoration: none; }
.action-menu-item i { width: 14px; text-align: center; font-size: 12px; color: var(--text-muted); }
.action-menu-item.danger, .action-menu-item.danger i { color: var(--danger); }
.action-menu-item.danger:hover { background: color-mix(in srgb, var(--danger) 10%, transparent); }
.action-menu-sep { height: 1px; background: var(--border-light); margin: 6px 4px; }

.pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600; line-height: 1.4; white-space: nowrap; letter-spacing: 0; }
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.pill.quiet::before { display: none; }
.pill.ok   { color: #047857; background: color-mix(in srgb, var(--success) 14%, transparent); }
.pill.warn { color: #b45309; background: color-mix(in srgb, var(--warning) 14%, transparent); }
.pill.info { color: var(--primary); background: color-mix(in srgb, var(--primary) 12%, transparent); }
.pill.mute { color: var(--text-secondary); background: var(--bg-tertiary); }
.detail-tab-count { font-size: 11px; font-weight: 600; line-height: 1.4; padding: 1px 7px; border-radius: 999px;
  background: var(--bg-tertiary); color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* The tabs: .pagetabs radios dressed as the portal's .detail-tab. */
.detail-tabs > .pagetabs-strip { gap: 2px; margin: 0 0 20px; border-bottom-color: var(--border-light);
  flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
.detail-tabs > .pagetabs-strip .pagetab { gap: 8px; padding: 10px 14px 11px; color: var(--text-muted); }
.detail-tabs > .pagetabs-strip .pagetab:hover { color: var(--text-primary); }
.detail-tabs > .pagetabs-strip .pagetab:has(input:checked) { color: var(--text-primary); }
.detail-tabs .pagetab-panel { min-width: 0; }

.detail-section + .detail-section { margin-top: 24px; }
.detail-section-title { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 0 0 14px; font-size: 18px; font-weight: 600; color: var(--text-primary); }
.detail-hint-inline { font-size: 12px; font-weight: 400; color: var(--text-muted); max-width: 70ch; text-align: right; }
.detail-card { background: var(--bg-primary); border: 1px solid var(--border-light); border-radius: 16px;
  box-shadow: var(--shadow-table); padding: 20px 22px; min-width: 0; }
.detail-card .detail-form > label:first-child { margin-top: 0; }
.fact-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px 24px; }
.fact-item { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.fact-label { display: flex; align-items: center; gap: 10px; font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted); }
.fact-value { font-size: 15px; font-weight: 600; color: var(--text-primary); overflow-wrap: anywhere; }
.fact-value.accent { color: var(--primary); }
.fact-value.warn { color: #b45309; }
.fact-value small { display: block; font-size: 12px; font-weight: 400; color: var(--text-muted); margin-top: 2px; }
.fact-progress { display: block; max-width: 220px; margin-top: 6px; }
.board-notes { margin: -6px 0 10px; display: flex; flex-wrap: wrap; gap: 4px 14px; }
.board-notes:not(:has(:not(:empty))) { display: none; }

/* The board's columns are the portal's cards; its cards sit on the
   secondary ground inside them. */
.detail-wrap .project-board .col { background: var(--bg-primary); border-color: var(--border-light);
  border-radius: 16px; box-shadow: var(--shadow-table); }
.detail-wrap .project-board .col h3 { padding: 14px 16px; font-size: 14px; font-weight: 600; text-transform: none;
  letter-spacing: 0; color: var(--text-primary); }
.detail-wrap .project-board .col h3 .cnt { background: var(--bg-tertiary); color: var(--text-muted); font-weight: 600;
  padding: 1px 7px; border-radius: 999px; }
.detail-wrap .project-board .col .cards { padding: 10px; gap: 10px; }
.detail-wrap .project-board .card { background: var(--bg-secondary); border-color: var(--border-light); box-shadow: none; }
.detail-wrap .project-board .card:hover { background: var(--bg-primary); border-color: rgba(var(--primary-rgb), .35);
  box-shadow: var(--shadow-sm); }
.detail-wrap .project-board .card.due { border-left: 3px solid var(--warning); }
.detail-wrap .project-board .col .col-foot { padding: 0 16px 14px; }
#add-dlg .add-existing { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border-light);
  display: flex; gap: 8px; align-items: end; }

@media (max-width: 1024px) { .fact-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 768px) {
  .detail-title { font-size: 22px; }
  .detail-section-title { font-size: 16px; }
  .detail-hint-inline { text-align: left; }
  .detail-card { padding: 16px; }
}
@media (max-width: 380px) { .fact-grid { grid-template-columns: 1fr; } }
@media (max-width: 480px) {
  .detail-title { font-size: 20px; }
  .detail-actions { width: 100%; }
  .detail-actions .action-btn.primary { flex: 1; justify-content: center; }
}

@media (max-width: 768px) {
  .topbar { padding: 0 14px; gap: 14px; }
  .wrap { padding: 20px 14px 48px; }
  .pagetabs-strip { gap: 0 16px; }
  .pagetab { padding: 8px 1px; }
  /* The bell sits left of the user menu, so a right-anchored 300px panel
     would run off the left of a phone: pin it to the viewport instead. */
  .notif-bell-dropdown { position: fixed; top: 56px; left: 12px; right: 12px; width: auto; max-width: none; }
  .user-avatar-small { width: 26px; height: 26px; font-size: 11px; }
  .row, .row-3 { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Login — ported from design.quantainnovation.ai (static/css/app.css), which
   is itself the portal's login treatment rule for rule. Same layout, same
   orbit ground, same gradient wordmark; the accent is this app's RED rather
   than that app's teal or the CMS's Power-BI yellow.

   The entry screen stays on the dark orbit ground while the app behind it is
   light — the same split the portal runs, where the auth pages carry the
   aurora and the workspace does not. Everything here is scoped under
   `body.page-aurora`, which only the login page carries, so the light board
   tokens above are untouched by it (and it by them).
   ========================================================================== */
body.page-aurora {
  /* Accent — the CRM red, the same ramp the boards use, so the entry screen
     and the app read as one product. */
  --primary: #dc2626;
  --primary-dark: #b91c1c;
  --primary-light: #ef4444;
  --primary-rgb: 220, 38, 38;
  --primary-light-rgb: 239, 68, 68;
  --button-text: #ffffff;

  /* The hero wordmark ramp (the marketing site's .gradient-text): dark -> mid
     -> light at 135deg, built from the accent tokens so a re-theme moves the
     wordmark with it. */
  --gradient-text: linear-gradient(135deg,
      var(--primary-dark) 0%, var(--primary) 50%, var(--primary-light) 100%);

  /* Aurora lobes: red leads, soft gold second — the complement that keeps
     the glow from reading as one wash. The gold is exactly what the portal's
     own _aurora_tints() derives from #dc2626 (primary hue rotated 50deg at a
     fixed light tone: "claret picks up gold"), so a red brand auroras here
     the same way it would in the portal. */
  --aurora-primary-rgb: var(--primary-rgb);
  --aurora-accent-rgb: 229, 209, 108;

  /* The form has NO card: it floats on the aurora, so these are transparent
     by design, not unfinished. Inputs take a pale film so the field sits
     ABOVE the glow rather than compositing into it. */
  --auth-card-bg: transparent;
  --auth-card-border: transparent;
  --auth-card-shadow: none;
  --auth-header-bg: transparent;
  --auth-header-pad-bottom: 14px;
  --auth-input-bg: rgba(255, 255, 255, 0.07);
  --auth-input-border: rgba(148, 163, 184, 0.28);
  /* Local names: the app's --border/--danger are solid board colours and are
     deliberately left alone. */
  --auth-line: rgba(148, 163, 184, 0.16);
  --auth-danger: #f87171;

  --text-primary: #f1f5f9;
  --text-secondary: #cbd5e1;
  --secondary-rgb: 15, 23, 42;

  background: #060a14;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text-primary);
  /* The app declares color-scheme: light at :root; this page is the one dark
     surface, so it re-declares its own — otherwise the UA paints a light
     scrollbar and light form-control chrome onto the orbit ground. */
  color-scheme: dark;
}
/* The orbit ground: navy gradient + static aurora + vignette. No blur, no
   animation — cheap to paint and static under prefers-reduced-motion. */
body.page-aurora::before {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background:
    radial-gradient(700px 480px at 10% 2%, rgba(var(--aurora-primary-rgb), 0.24), transparent 52%),
    radial-gradient(780px 540px at 90% 98%, rgba(var(--aurora-accent-rgb), 0.17), transparent 52%),
    radial-gradient(520px 400px at 84% 8%, rgba(var(--primary-light-rgb), 0.09), transparent 55%),
    radial-gradient(120% 120% at 50% 45%, transparent 40%, rgba(0, 0, 0, 0.38) 100%),
    linear-gradient(160deg, #060a14 0%, #0b1120 45%, #111c33 100%);
}

/* Content sits above the fixed glow. */
.auth-wrapper { position: relative; z-index: 1; }
/* Column, unlike the design app's: base.html renders the flash block inside
   this wrapper, and as a row a stray flash would sit BESIDE the form. */
.auth-wrapper { width: 100%; max-width: 100%; min-height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 40px 20px; }
.auth-container { width: 100%; max-width: 460px; background: var(--auth-card-bg);
  border-radius: 20px; box-shadow: var(--auth-card-shadow); overflow: hidden;
  border: 1px solid var(--auth-card-border); }
.auth-header { text-align: center;
  padding: 40px 40px var(--auth-header-pad-bottom);
  background: var(--auth-header-bg); border-bottom: 1px solid var(--auth-card-border); }
/* Height-budgeted rather than width-budgeted, so a squarish mark and a wide
   wordmark both land at a sensible size. */
.auth-logo { max-height: 84px; max-width: 300px; width: auto; height: auto;
  margin: 0 auto 14px; display: block; object-fit: contain;
  filter: drop-shadow(0 4px 16px rgba(var(--primary-rgb), 0.2)); }
.auth-header h1 { font-size: 30px; font-weight: 700; color: var(--text-primary);
  margin: 0 0 6px; letter-spacing: -0.6px; }
/* -webkit-* prefixes are load-bearing, not legacy: background-clip: text is
   still prefixed in Safari. */
.gradient-text {
  background: var(--gradient-text);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.auth-header p { color: var(--text-secondary); font-size: 15px; font-weight: 400;
  margin: 0; }
.auth-body { padding: 40px; }
.auth-form { display: flex; flex-direction: column; gap: 20px; }
.auth-form .form-group { display: flex; flex-direction: column; gap: 8px; }
.auth-form label { font-size: 14px; font-weight: 600; color: var(--text-primary);
  margin: 0; text-transform: none; letter-spacing: 0; }
.auth-form input { padding: 14px 16px; border: 2px solid var(--auth-input-border);
  border-radius: 12px; font-size: 15px; font-family: inherit;
  transition: all 0.2s ease; background: var(--auth-input-bg);
  color: var(--text-primary); width: 100%; }
.auth-form input:focus { outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 4px rgba(var(--primary-rgb), 0.1); }
.auth-form input::placeholder { color: var(--text-secondary); }
.auth-btn { padding: 16px 24px; background: var(--primary); color: var(--button-text);
  border: none; border-radius: 12px; font-size: 16px; font-weight: 600;
  font-family: inherit; cursor: pointer; transition: all 0.2s ease;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; }
.auth-btn:hover { background: var(--primary-dark); transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(var(--primary-rgb), 0.25); }
.auth-btn:active { transform: translateY(0); }
.auth-btn i { font-size: 14px; }
.auth-footer { text-align: center; margin-top: 28px; padding-top: 28px;
  border-top: 1px solid var(--auth-line); }
.auth-footer p { color: var(--text-secondary); font-size: 14px; margin: 0; }

.alert { padding: 14px 18px; border-radius: 12px; font-size: 14px;
  margin-bottom: 20px; display: flex; align-items: center; gap: 10px; }
.alert-error { background: rgba(248, 113, 113, 0.15);
  border: 1px solid rgba(248, 113, 113, 0.30); color: var(--auth-danger); }
.alert i { font-size: 16px; }

@media (max-width: 480px) {
  .auth-wrapper { padding: 20px 16px; }
  .auth-header { padding: 32px 24px 24px; }
  .auth-body { padding: 24px; }
  .auth-header h1 { font-size: 22px; }
  .auth-logo { max-height: 64px; max-width: 240px; }
}

/* ---- strategy cadence (027) ----
   A numbered rail: the steps ARE a sequence, so the numbering is information
   rather than decoration. Detail is collapsed behind the step title so a
   nine-step play still reads as a shape at a glance. */
ol.cadence { list-style: none; margin: 0 0 18px; padding: 0;
  display: flex; flex-direction: column; gap: 8px; }
ol.cadence li { display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 12px;
  align-items: start; background: var(--panel, var(--bg-primary));
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; box-shadow: var(--shadow-table); }
ol.cadence li.exit { background: var(--bg-tertiary); }
ol.cadence .step-n { font-variant-numeric: tabular-nums; font-weight: 600;
  font-size: 13px; color: var(--text-muted); text-align: right; padding-top: 2px; }
ol.cadence li.exit .step-n { color: var(--danger); }
ol.cadence summary { cursor: pointer; list-style: none;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px; }
ol.cadence summary::-webkit-details-marker { display: none; }
ol.cadence summary::before { content: '\25B8'; color: var(--text-muted);
  font-size: 11px; transition: transform .12s ease; }
ol.cadence details[open] > summary::before { transform: rotate(90deg); }
ol.cadence .step-label { font-weight: 600; }
ol.cadence .step-when { font-size: 12px; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .04em; }
ol.cadence .step-detail { margin: 8px 0 0; color: var(--text-secondary); font-size: 14px; }
ol.cadence .step-edit { margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--border-light); }
@media (prefers-reduced-motion: reduce) {
  ol.cadence summary::before { transition: none; }
}

/* ---- source picker + tag (028) ---- */
.srcgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 8px; margin: 4px 0 4px; }
.srcpick { display: flex; align-items: flex-start; gap: 9px; font-weight: normal;
  padding: 9px 11px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-secondary); cursor: pointer; }
.srcpick:has(input:checked) { border-color: var(--primary); background: var(--primary-glow); }
.srcpick input { width: auto; margin: 2px 0 0; cursor: pointer; }
.srcpick .crumb { display: block; }
/* The source tag reads as provenance, not as a targeting flag — so it takes
   the neutral outline rather than the filled treatment the flag chips use. */
.chip.src { background: transparent; border: 1px dashed var(--border);
  color: var(--text-secondary); }

/* ---- draw filter: the industry slice on one strategy-pool link (058) ---- */
/* Reuses the source picker's checkbox-card idiom, tightened: those cards are
   sized for a label PLUS a provenance crumb, and an industry carries only the
   label. Each pool gets a rule above it so the grids read as separate
   questions rather than one long field of ticks. */
.srcgrid.tight { grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 6px; }
.srcgrid.tight .srcpick { padding: 7px 10px; }
.drawfilter-h { margin: 20px 0 2px; }
.drawfilter { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border-light); }
.drawfilter-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }

/* ---- queue tab (053) ---- */
/* The stacked bar is the configured split made visible: one segment per
   active strategy, hue keyed to the strategy id so it stays stable as
   weights move. */
.qbar { display: flex; height: 9px; border-radius: 5px; overflow: hidden;
  background: var(--bg-tertiary); margin: 6px 0 4px; }
.qbar span { display: block; height: 100%; min-width: 2px; }
.qlegend { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 12px;
  color: var(--text-secondary); margin-bottom: 10px; }
.qlegend i { display: inline-block; width: 9px; height: 9px; border-radius: 2px;
  margin-right: 5px; }
.capstrip { display: flex; gap: 18px; flex-wrap: wrap; margin: 0 0 12px;
  font-size: 14px; color: var(--text-secondary); }
.capstrip b { color: var(--text-primary); }
.capstrip .over b { color: #b45309; }
/* When the room comes back, by day (2026-10-05): a quiet line under the strip,
   one chunk per day, wrapping whole on a phone. */
.roomback { display: flex; flex-wrap: wrap; gap: 4px 10px; margin: -6px 0 12px;
  font-size: 13px; color: var(--text-secondary); }
.roomback span { white-space: nowrap; }
.roomback span + span::before { content: "·"; margin-right: 10px; }
.roomback b { color: var(--text-primary); font-weight: 600; }
.qbatch { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 10px 0 6px; }
.weightcell input { width: 74px; }

/* ---- the send queue's line (091) ----
   Rows inside the room carry the primary bar — an inset shadow, for the reason
   the unread row gives below: a border would step the row sideways. Rows past
   it are row-dim. The line itself is a quiet full-width row rather than a
   heading, because the order carries on through it. */
tr.row-send > td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
tr.qline > td { padding: 7px 12px; font-size: 12px; color: var(--text-secondary);
  background: var(--bg-secondary); border-top: 2px dashed var(--border); }
/* Below the line the whole row is greyed — it is waiting for room — and comes
   back to full strength under the pointer, because Skip and Block still work
   there. */
tr.qwait > td { opacity: .6; }
tr.qwait:hover > td, tr.qwait:focus-within > td { opacity: 1; }

/* ---- notifications: the unread row (057) ----
   Unread is a property of the ROW, not a chip. The chip column says exactly
   one thing — the furthest-along state — so an auto-applied report nobody has
   opened chips "Applied" and, until this, read like one opened three weeks
   ago: the nav badge said three were unread and the list could not say WHICH
   three. Composed beside the chip rather than competing for its slot, and
   derived from read_at like everything else here — the first open stamps it
   and the next render simply stops drawing this. No perpetual motion: several
   rows can be unread at once, and a list that pulses is a list you stop
   reading.

   An inset shadow rather than border-left, because a border would widen the
   first cell and rows would step sideways as they were read. */
tr.row-new > td { background: rgba(var(--primary-rgb), .055); }
tr.row-new > td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
/* Hover has to stay ABOVE the unread tint (it outranks it on specificity
   either way), or moving onto a row would look like it had just gone read. */
tbody tr.row-new:hover > td { background: rgba(var(--primary-rgb), .1); }
/* The mark itself. A shape, not just a colour, and it carries its own
   tooltip — the tint and the bar are the ambient half, this is the one you
   can point at. */
.unread-dot { display: inline-block; width: 7px; height: 7px; margin-right: 9px;
  border-radius: 50%; background: var(--primary); vertical-align: 1px;
  box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .16); }

/* ---- searchable dropdowns (select.js) ---- */
/* The native select stays as the trigger — untouched, so every context
   (filterbar, edit-table, forms) keeps its existing look. Only the popup is
   ours: fixed-position so it escapes .tablewrap scroll boxes, closed on any
   scroll rather than chased. */
.qsel-panel { position: fixed; z-index: 1000; background: var(--bg-primary);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md); padding: 6px; max-width: min(420px, 92vw); }
.qsel-panel .qsel-search { width: 100%; padding: 7px 10px; font-size: 13px;
  margin-bottom: 6px; }
.qsel-list { max-height: 300px; overflow-y: auto; overscroll-behavior: contain; }
.qsel-opt { padding: 6px 10px; border-radius: 6px; font-size: 13.5px;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qsel-opt.grouped { padding-left: 20px; }
.qsel-opt.active { background: var(--bg-tertiary); }
.qsel-opt.selected { color: var(--primary-dark); font-weight: 600; }
.qsel-opt.selected::after { content: " ✓"; }
.qsel-group { padding: 8px 8px 3px; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .5px; color: var(--text-muted); }
.qsel-empty { padding: 10px; font-size: 13px; color: var(--text-muted); }
.qsel-panel .hidden { display: none; }
/* Filter box above the Actions form's multi-select list boxes */
.qsel-multisearch { margin-bottom: 6px; padding: 7px 10px; font-size: 13px; }

/* ---- actions tab: inline editing ---- */
/* The due cell is an editable date input now, so the overdue warning the
   badge used to carry has to live on the input itself. */
.edit-table td.due-over input { color: #b45309; font-weight: 600; }

/* ==========================================================================
   Positive feedback (toasts, typed flashes, the metric bump)

   The app had no vocabulary for "good": --success appeared three times in the
   whole file and every flash rendered in brand red, so "Commercial lead
   created from this connection" and "Save failed" looked identical. These
   tokens are that vocabulary — green for a real win, the existing red kept
   for genuine errors, and — the third of the three — a genuinely neutral
   ground for everything else, which the flash block did not have before: it
   took the brand tint, and the brand is red. The category is opt-in per
   flash() call:
   "Saved." is not a win, and colouring every confirmation green would spend
   the token that makes the three real ones read as different.
   ========================================================================== */
:root {
  --success-rgb: 16, 185, 129;
  --success-dark: #047857;
  --danger-rgb: 153, 27, 27;
}
.flash.ok { background: rgba(var(--success-rgb), .09);
            border-color: rgba(var(--success-rgb), .30); color: var(--success-dark); }
.flash.err { background: rgba(var(--primary-rgb), .07);
             border-color: rgba(var(--primary-rgb), .30); color: var(--danger); }

/* ---- .notice — a flash that STAYS ----
   Same three tones as .flash and deliberately so: the reader should not have
   to learn a second vocabulary for the same news. The difference is lifetime,
   and it is the whole point. A flash is a transient report of what just
   happened; a notice is DERIVED per render and states what is true NOW, so it
   survives a reload and a page left open. It exists because a screening
   report's refusal was flashed, missed, and read as success. Use it where the
   standing state answers "why can I not do this", never for confirmations —
   those are events, and events belong in a flash. */
.notice { padding: 12px 16px; border-radius: var(--radius-sm); margin: 18px 0;
          background: var(--bg-tertiary); border: 1px solid var(--border);
          color: var(--text-secondary); line-height: 1.5; }
.notice.ok { background: rgba(var(--success-rgb), .09);
             border-color: rgba(var(--success-rgb), .30); color: var(--success-dark); }
.notice.err { background: rgba(var(--primary-rgb), .07);
              border-color: rgba(var(--primary-rgb), .30); color: var(--danger); }

/* ---- toasts ----
   Bottom-right, newest at the bottom, click to dismiss. Fixed so they escape
   the .tablewrap scroll boxes, and under the .qsel-panel z-index (1000) so a
   dropdown is never covered by an acknowledgement of the edit before it. */
.toaststack {
  position: fixed; right: 22px; bottom: 22px; z-index: 900;
  display: flex; flex-direction: column; gap: 8px;
  max-width: min(340px, calc(100vw - 44px)); pointer-events: none;
}
.toast {
  pointer-events: auto; cursor: pointer;
  background: var(--bg-primary); border: 1px solid var(--border);
  border-left: 3px solid var(--text-muted);
  border-radius: var(--radius-sm); padding: 11px 14px; box-shadow: var(--shadow-md);
  animation: toast-in .22s cubic-bezier(.2, .8, .3, 1) both;
}
.toast-title { font-weight: 600; letter-spacing: -.01em; }
.toast-detail { color: var(--text-secondary); font-size: 12.5px; margin-top: 2px; }
.toast.ok { border-left-color: var(--success); }
.toast.ok .toast-title { color: var(--success-dark); }
/* A win is the only louder note in the file, and it is louder by one step —
   a tinted ground and a wider rule, not a different kind of object. It fires
   on is_won, which in a normal year is a handful of times. */
.toast.win {
  border-left-width: 5px; border-left-color: var(--success);
  background: linear-gradient(180deg, rgba(var(--success-rgb), .10), rgba(var(--success-rgb), .04));
  box-shadow: var(--shadow-lg), 0 0 0 1px rgba(var(--success-rgb), .20);
}
.toast.win .toast-title { color: var(--success-dark); font-size: 15px; }
/* The third tone, and the same one .flash and .notice already use — a refusal
   should not have to be learnt twice. Since submits stopped reloading the
   page, this is where a flashed "err" lands, and it is the tone that stays
   until it is clicked. */
.toast.err { border-left-color: var(--danger);
             background: rgba(var(--primary-rgb), .05); }
.toast.err .toast-title { color: var(--danger); }
.toast.leaving { animation: toast-out .2s ease both; }
/* An offer in a toast (103: Update client after a tick or a close). */
.toast .toast-action { margin-top: 8px; }

/* ---- in-place submit: the only thing on screen that says "working" ----
   A hairline across the top of the window while the server answers. It exists
   because the page no longer blanks and re-paints, and without SOME sign a
   slow write (building a slate, promoting a batch) reads as a dead click. */
body.inplace-busy::after {
  content: ''; position: fixed; top: 0; left: 0; right: 0; height: 2px;
  background: var(--primary); transform-origin: 0 50%; z-index: 1200;
  animation: inplace-bar 1.1s cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes inplace-bar {
  0%   { transform: scaleX(0);   opacity: 1; }
  70%  { transform: scaleX(.85); opacity: 1; }
  100% { transform: scaleX(1);   opacity: 0; }
}
@keyframes toast-in  { from { opacity: 0; transform: translateY(8px) scale(.98); } }
@keyframes toast-out { to   { opacity: 0; transform: translateY(4px); } }

/* ---- metric bump ----
   The boards patch the strip in place after a stage move instead of
   reloading, so the number that changed says so. */
.metric.bumped { animation: metric-bump .5s cubic-bezier(.2, .8, .3, 1) both; }
@keyframes metric-bump {
  0%   { transform: none; }
  35%  { transform: translateY(-3px); box-shadow: var(--shadow-md); }
  100% { transform: none; }
}
/* A card that has just landed in a new column. Colour only — the column it
   sits in has already moved it, so a transform here would fight the drop. */
.card.landed { animation: card-landed 1.1s ease both; }
@keyframes card-landed {
  0%   { background: rgba(var(--success-rgb), .16); border-color: rgba(var(--success-rgb), .45); }
  100% { background: var(--bg-primary); border-color: var(--border); }
}

/* The stylesheet already treats reduced motion as a real setting (the login
   aurora is static for it, the cadence chevron drops its transition). A
   feedback layer that ignored it would be the one thing in the file that
   does: the acknowledgement still appears and still says the same thing, it
   just stops moving. */
@media (prefers-reduced-motion: reduce) {
  .toast, .toast.leaving, .metric.bumped, .card.landed { animation: none; }
  /* Still visible, just not moving — it is a state, not a flourish. */
  body.inplace-busy::after { animation: none; opacity: .7; }
  .card.landed { background: rgba(var(--success-rgb), .12); }
}

/* The slate row's three registrations (2026-08-30). The invites are sent by
   hand on LinkedIn, so every one of them is recorded from this cell — Sent
   (carrying the slug nothing upstream can supply), Block, Skip. One wrapping
   line rather than a stack: the cell repeats fifteen times down the page, and
   a stack would make the slate scroll for no information. */
.sendrow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.sendrow form { display: flex; align-items: center; gap: 6px; margin: 0; }
.sendrow input[type=text] { width: 190px; padding: 6px 9px; font-size: 13px; }

/* The reason dialog (066). One per page for BOTH verbs, handed the row it
   belongs to: a reason PICKER cannot be a prompt(), and fifteen inline forms
   would bury the slate under chrome used a few times a week. */
.reasondlg { border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--bg-primary); color: var(--text-primary);
  padding: 22px; max-width: 460px; width: calc(100% - 32px); box-shadow: var(--shadow-md); }
.reasondlg::backdrop { background: rgba(0, 0, 0, .45); }
.reasondlg label { margin-top: 10px; }
/* The project board's card editor (2026-10-04): the reason dialog's frame,
   wider for a whole action. Each field saves on its own, so the status line
   says what the last save did, in the inline editor's colours. */
.editdlg { max-width: 640px; }
.editdlg-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
                gap: 4px 12px; }
.editdlg-head h3 { margin: 0; white-space: nowrap; }
.editdlg-status { font-size: 12px; color: var(--text-muted); text-align: right; }
.editdlg-status.saving { color: var(--text-secondary); }
.editdlg-status.saved { color: var(--success); }
.editdlg-status.err { color: var(--danger); }
.editdlg textarea { min-height: 120px; line-height: 1.5; }
.editdlg .crumb { margin: 12px 0 0; }
/* Recurring time (2026-10-06): the weekday ticks in one row, and the
   project header's read-back, quiet like a booking. */
.dayticks { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.dayticks label { display: flex; align-items: center; gap: 5px; margin: 0; font-weight: normal; }
.dayticks input { width: auto; }
/* Calendar colour (2026-10-06): the header's dot, read from Google, and the
   dialog's swatches; the current one carries a ring. */
.caldot { display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  background: var(--border); vertical-align: -1px; }
.swatches { display: grid; grid-template-columns: repeat(6, 36px); gap: 10px; margin-top: 14px; }
.swatch { width: 36px; height: 36px; border-radius: 50%; border: 0; padding: 0; cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); }
.swatch:disabled { cursor: default; opacity: .45; }
.swatch.current { box-shadow: 0 0 0 2px var(--bg-primary), 0 0 0 4px var(--text-primary); opacity: 1; }
.swatch:not(:disabled):hover { transform: scale(1.08); }
p.recurring:empty { display: none; }
/* The Recurring time dialog's list of what is booked (2026-10-06): one row a
   series, the one the form is changing marked. */
.recurring-list:empty { display: none; }
.recurring-list { margin-top: 12px; border: 1px solid var(--border); border-radius: var(--radius-md); }
.recurring-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 8px 10px;
  font-size: 13px; }
.recurring-row + .recurring-row { border-top: 1px solid var(--border); }
.recurring-row .what { flex: 1 1 200px; }
.recurring-row.editing { background: rgba(var(--primary-rgb), .06); }
.recurring-row form { margin: 0; }
/* A Google dialog waiting on Google (2026-10-06): its controls stop taking
   clicks, and the clicked one says it is working. */
form.is-waiting button, form.is-waiting input { pointer-events: none; }
form.is-waiting .btn.is-busy { opacity: .75; }
.swatch.is-busy { animation: swatch-wait 1s ease-in-out infinite; opacity: 1; }
@keyframes swatch-wait { 50% { transform: scale(.85); } }
@media (prefers-reduced-motion: reduce) { .swatch.is-busy { animation: none; } }
p.recurring .booking { padding-left: 0; white-space: normal; }
.editdlg-foot { margin-top: 16px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* View all (2026-10-15): every action in a project, the Actions tab's
   editor moved into a dialog when that tab went. Near full screen: the head,
   the list tabs with the search, and the add row stay put while the lists
   scroll between them. A row is one line of fields (title, due, estimate,
   category) over a line of chips; opened, it holds notes and steps on the
   left and the rest on the right, which stack on a narrow screen. */
.alldlg { max-width: 1120px; width: calc(100% - 32px); height: min(88vh, 900px); padding: 0;
  display: flex; flex-direction: column; overflow: hidden; }
.alldlg:not([open]) { display: none; }
.alldlg-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 22px 0; }
.alldlg-head > div:first-child { flex: 1; min-width: 0; }
.alldlg-head h3 { margin: 0 0 2px; }
.al-close { border: 0; background: none; color: var(--text-muted); font-size: 18px; cursor: pointer;
  padding: 2px 4px; line-height: 1; }
.al-close:hover { color: var(--primary); }
.alldlg-tabs { flex: 1; min-height: 0; overflow-y: auto; padding: 0 22px 12px; }
.pagetabs-strip.alldlg-bar { position: sticky; top: 0; z-index: 2; background: var(--bg-primary);
  align-items: flex-end; margin: 0 0 6px; padding-top: 6px; }
.alldlg-bar #al-q { width: 240px; max-width: 100%; margin: 0 0 8px auto; padding: 6px 10px; font-size: 13px; }
.al-row { border-bottom: 1px solid var(--border); padding: 8px 6px; border-radius: 6px;
  transition: background-color .2s ease; }
.al-row.open { background: var(--bg-secondary); }
.al-row.saving { opacity: .6; }
.al-row.saved { background: rgba(16,185,129,.08); }
.al-row.err { background: rgba(220,38,38,.08); }
.al-row.arrived { box-shadow: inset 3px 0 0 var(--primary); }
.al-line { display: flex; align-items: center; gap: 8px; }
.al-line .tickform { flex: none; }
.al-line input, .al-line select, .al-add input, .al-add select { padding: 6px 9px; font-size: 13px; }
.al-title { flex: 1; min-width: 0; font-weight: 500; }
.al-row.done .al-title { color: var(--text-secondary); text-decoration: line-through; text-decoration-color: var(--text-muted); }
.al-due { width: 140px; flex: none; }
.al-due.over { color: #b45309; border-color: rgba(217,119,6,.45); }
.al-est { width: 64px; flex: none; }
.al-cat { width: 150px; flex: none; }
.al-open { flex: none; border: 1px solid var(--border); background: var(--bg-primary); color: var(--text-secondary);
  border-radius: 8px; width: 32px; height: 32px; cursor: pointer; }
.al-open:hover { color: var(--primary); }
.al-open i { transition: transform .2s ease; }
.al-row.open .al-open i { transform: rotate(180deg); }
.al-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; margin: 5px 0 0 36px;
  font-size: 12px; cursor: pointer; }
.al-chips:empty { display: none; }
.al-links-peek, .al-peek { color: var(--text-muted); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; max-width: 100%; }
.al-peek { flex: 1 1 200px; min-width: 0; }
.al-detail { margin: 10px 0 2px 36px; }
.al-grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 4px 22px; }
.al-grid label { margin-top: 10px; }
.al-grid label:first-child { margin-top: 0; }
.al-grid textarea { min-height: 150px; line-height: 1.5; }
.al-check { display: flex; align-items: center; gap: 8px; font-weight: normal; }
.al-check input { width: auto; }
.al-links { display: flex; flex-wrap: wrap; gap: 4px 12px; }
.al-link { white-space: nowrap; display: inline-flex; align-items: center; gap: 4px; }
.al-summary { white-space: pre-wrap; margin-top: 6px; font-size: 13px; }
.al-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; }
.al-empty { padding: 28px 0; }
.al-add { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 12px 22px 16px;
  border-top: 1px solid var(--border); background: var(--bg-primary); }
.al-add input[name=title] { flex: 1 1 260px; min-width: 0; }
.al-add input[type=date] { width: 140px; }
.al-add select { width: 150px; }
.al-closed { padding: 12px 22px 16px; margin: 0; border-top: 1px solid var(--border); }
.linkish { border: 0; background: none; padding: 0; font: inherit; color: var(--primary); cursor: pointer; }
.linkish:hover { text-decoration: underline; }
@media (max-width: 760px) {
  .al-grid { grid-template-columns: 1fr; }
  .al-line { flex-wrap: wrap; }
  .al-title { flex-basis: calc(100% - 80px); }
  .al-chips, .al-detail { margin-left: 0; }
}
/* The Projects tab's Not in a project list (2026-10-15): one row per open
   action filed in no project, with its project picker on the right. */
.unfiled-list { list-style: none; margin: 10px 0 0; padding: 0; }
.unfiled-list li { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 16px; padding: 12px 0;
  border-top: 1px solid var(--border); }
.unfiled-main { flex: 1 1 320px; min-width: 0; }
.unfiled-main .nm { font-weight: 500; }
.unfiled-main .chips { margin-top: 5px; }
.unfiled-main .meta { margin-top: 4px; font-size: 12px; color: var(--text-muted); }
.unfiled-form { display: flex; gap: 8px; align-items: center; }
.unfiled-form select { width: 260px; max-width: 60vw; }
/* The Update client dialog (103): the editor's frame. Who it can go to is a
   list of ticks, each with why they are on it and their address, or a field
   to add one; the message keeps its line breaks as the email will. */
.cudlg .notice { margin: 12px 0 0; font-size: 13px; }
.cu-people { list-style: none; margin: 0; padding: 0; max-height: 220px; overflow-y: auto;
             border: 1px solid var(--border-light); border-radius: var(--radius-sm); }
.cu-people:empty { display: none; }
.cu-person { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px;
             padding: 7px 10px; border-bottom: 1px solid var(--border-light); }
.cu-person:last-child { border-bottom: none; }
.cudlg label.cu-pick { display: flex; align-items: baseline; gap: 8px; flex: 1 1 240px; min-width: 0;
                       margin: 0; font-size: 13.5px; font-weight: 400; color: var(--text-primary);
                       cursor: pointer; }
.cu-pick input { flex-shrink: 0; width: auto; margin: 0; }
.cu-who { min-width: 0; }
.cu-who .crumb { font-size: 12px; }
.cu-email { font-size: 12.5px; color: var(--text-secondary); overflow-wrap: anywhere; }
.cudlg input.cu-add { flex: 0 1 220px; width: auto; min-width: 0; padding: 5px 8px; font-size: 13px; }
.cudlg #cu-body { min-height: 220px; white-space: pre-wrap; }
/* The preview (2026-10-05): the email as the client will see it, drawn by the
   server into a sandboxed frame. Beside the form when the window is wide
   enough for the email's own 600 px plus the form; below the message
   otherwise, so it is passed on the way to Approve and send. The frame is as
   tall as the email (clientupdate.js measures it), so the dialog scrolls once. */
.cudlg { max-width: 1240px; }
.cu-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 28px; align-items: start; }
.cu-preview { margin-top: 16px; min-width: 0; }
.cu-preview-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
                   gap: 2px 12px; margin: 10px 0 6px; font-size: 13px; font-weight: 600;
                   color: var(--text-secondary); }
.cu-preview-head .crumb { margin: 0; font-weight: 400; }
.cu-preview-head .crumb.err { color: var(--danger); }
#cu-preview { display: block; width: 100%; height: 520px; border: 1px solid var(--border);
              border-radius: var(--radius-sm); background: #eef1f5; }
.cu-preview > .crumb { margin-top: 8px; }
@media (min-width: 1180px) {
  .cu-cols { grid-template-columns: minmax(360px, 1fr) 660px; }
  .cu-preview { margin-top: 0; }
}

/* Action steps (102): the checklist static/steps.js draws in the card editor
   and the Actions tab's expander. A step's title is an input that reads as
   text until it is clicked into, so the list scans like a checklist and still
   edits in place. */
.steps-list { list-style: none; margin: 0 0 6px; padding: 0; }
.steps-list .step { display: flex; align-items: center; gap: 2px; padding: 1px 0; }
.steps-list .step .tick { flex-shrink: 0; }
.steps-list .step.done .tick { color: var(--success); }
.steps-list .step-title { flex: 1; min-width: 0; border-color: transparent; background: transparent;
                          padding: 4px 6px; }
.steps-list .step-title:hover { border-color: var(--border); }
.steps-list .step-title:focus { background: var(--bg-primary); }
.steps-list .step.done .step-title { text-decoration: line-through; color: var(--text-muted); }
.steps-list .step-del { flex-shrink: 0; background: none; border: 0; cursor: pointer; padding: 2px 6px;
                        color: var(--text-muted); font-size: 16px; line-height: 1; opacity: .5; }
.steps-list .step:hover .step-del, .steps-list .step-del:focus-visible { opacity: 1; }
.steps-list .step-del:hover { color: var(--danger); }
.steps .steps-add, .edit-table .steps .steps-add { padding: 6px 9px; font-size: 13px; max-width: 520px;
  border-color: var(--border); background: var(--bg-primary); }
.steps .steps-count { margin-top: 5px; font-size: 12px; color: var(--text-muted); }
.notes-cell .steps-peek { color: var(--text-muted); font-size: 12px; white-space: nowrap; }

/* The Actions row expander (2026-08-30). Notes was a one-line input, which
   could not show a cadence step's message copy at all — the row now previews
   it and opens to the field at full size. The panel is a sibling <tr> so the
   textarea gets the table's whole width instead of one narrow cell. */
.notes-cell { cursor: pointer; user-select: none; }
.notes-cell .notes-peek { color: var(--text-secondary); font-size: 13px; }
.notes-cell .chev { margin-left: 6px; font-size: 10px; color: var(--text-muted); transition: transform .15s ease; }
tr.action-detail { display: none; }
tr.action-detail.open { display: table-row; }
tr.action-detail > td { background: var(--bg-secondary, rgba(0,0,0,.02)); padding: 14px 16px 18px; }
tr.action-detail textarea { min-height: 190px; font: inherit; line-height: 1.5; white-space: pre-wrap; }
tr.action-detail .crumb { margin-top: 8px; }

/* ---- the product roadmap (115, 2026-10-15) -------------------------------
   Levels of autonomy across, areas of the restaurant down. The status
   colours are the roadmap's own and say nothing about urgency: green has
   shipped, blue is being built, slate is planned, a dashed outline is an
   idea. A row's travelled cells (up to "you are here") are tinted, so the
   distance each area has come reads before any card does. */
#roadmap {
  --rm-shipped: #10b981; --rm-shipped-bg: rgba(16,185,129,.09);
  --rm-building: #2563eb; --rm-building-bg: rgba(37,99,235,.08);
  --rm-planned: #64748b; --rm-planned-bg: var(--bg-primary);
  --rm-idea: #94a3b8;
  --rm-travel: rgba(16,185,129,.06);
  --rm-col: 196px;
}
.rm-vision { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px;
  background: var(--nav-bg); color: #fff; border-radius: var(--radius); padding: 18px 22px;
  margin: 0 0 16px; box-shadow: var(--shadow-sm); }
.rm-vision-kicker { flex: 0 0 100%; font-size: 11px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: #6ee7b7; }
.rm-vision-text { flex: 1 1 420px; margin: 0; font-size: 16px; line-height: 1.5; max-width: 980px; }
.rm-vision .btn.ghost { background: rgba(255,255,255,.08); color: #fff; border-color: rgba(255,255,255,.2); }
.rm-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin: 0 0 4px; }
.rm-legend { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-secondary); }
.rm-legend b { color: var(--text-primary); }
.rm-swatch { width: 12px; height: 12px; border-radius: 3px; border: 2px solid var(--rm-c); background: var(--rm-bg); }
.rm-shipped { --rm-c: var(--rm-shipped); --rm-bg: var(--rm-shipped-bg); }
.rm-building { --rm-c: var(--rm-building); --rm-bg: var(--rm-building-bg); }
.rm-planned { --rm-c: var(--rm-planned); --rm-bg: var(--rm-planned-bg); }
.rm-idea { --rm-c: var(--rm-idea); --rm-bg: transparent; }
.rm-legend.rm-idea .rm-swatch { border-style: dashed; }

/* The grid scrolls sideways inside its own box on a narrow window, never the page. */
.rm-scroll { overflow-x: auto; padding-bottom: 6px; }
.rm-grid { display: grid; gap: 6px;
  grid-template-columns: minmax(170px, 210px) repeat(var(--rm-levels), minmax(var(--rm-col), 1fr));
  min-width: calc(190px + var(--rm-levels) * (var(--rm-col) + 6px)); }
.rm-corner { display: flex; flex-direction: column; justify-content: flex-end; gap: 2px; padding: 8px 4px;
  font-size: 12px; }
.rm-level { all: unset; box-sizing: border-box; cursor: pointer; display: flex; flex-direction: column; gap: 3px;
  padding: 10px 12px; border-radius: var(--radius-sm); background: var(--bg-primary);
  border: 1px solid var(--border); position: relative; }
.rm-level:hover, .rm-level:focus-visible { border-color: var(--rm-building); }
.rm-level-no { font-size: 11px; font-weight: 600; color: var(--text-muted); }
.rm-level-label { font-weight: 600; font-size: 14px; }
.rm-level-desc { font-size: 12px; color: var(--text-secondary); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.rm-level.rm-dest { background: var(--nav-bg); color: #fff; border-color: var(--nav-bg); }
.rm-level.rm-dest .rm-level-desc, .rm-level.rm-dest .rm-level-no { color: rgba(255,255,255,.7); }
/* An arrow between level headers: the route runs left to right. */
.rm-level:not(.rm-dest)::after { content: ""; position: absolute; right: -6px; top: 50%; width: 6px; height: 2px;
  background: var(--border); }

.rm-area { padding: 10px 12px; border-radius: var(--radius-sm); background: var(--bg-primary);
  border: 1px solid var(--border); display: flex; flex-direction: column; gap: 4px; }
.rm-area-name { all: unset; cursor: pointer; font-weight: 600; font-size: 14px; }
.rm-area-name:hover, .rm-area-name:focus-visible { color: var(--rm-building); }
.rm-area-here { font-size: 12px; color: var(--text-secondary); }
.rm-area-next { font-size: 12px; line-height: 1.35; color: var(--rm-building); }
.rm-cell { position: relative; display: flex; flex-direction: column; gap: 6px; padding: 6px 6px 28px;
  border-radius: var(--radius-sm); background: var(--bg-secondary); border: 1px solid var(--border-light);
  min-height: 74px; }
.rm-cell.rm-travelled { background: var(--rm-travel); border-color: rgba(16,185,129,.18); }
.rm-cell.rm-here { box-shadow: inset -3px 0 0 var(--rm-shipped); }
.rm-cell.rm-dest { background: rgba(var(--secondary-rgb), .035); border-style: dashed; border-color: var(--border); }
.rm-add { all: unset; position: absolute; left: 6px; right: 6px; bottom: 4px; height: 20px; border-radius: 6px;
  text-align: center; font-size: 15px; line-height: 20px; color: var(--text-muted); cursor: pointer; opacity: 0;
  transition: opacity .15s ease, background .15s ease; }
.rm-cell:hover .rm-add, .rm-add:focus-visible { opacity: 1; }
.rm-add:hover { background: var(--bg-elevated); color: var(--text-primary); }
@media (hover: none) { .rm-add { opacity: 1; } }

.rm-card { all: unset; box-sizing: border-box; cursor: pointer; display: flex; flex-direction: column; gap: 5px;
  width: 100%; padding: 8px 10px; border-radius: 8px; background: var(--rm-bg, var(--bg-primary));
  border: 1px solid var(--border); border-left: 3px solid var(--rm-c); box-shadow: var(--shadow-sm);
  transition: box-shadow .15s ease, transform .15s ease; }
.rm-card.rm-planned { background: var(--bg-primary); }
.rm-card.rm-idea { background: transparent; border: 1px dashed var(--rm-idea); border-left: 3px dashed var(--rm-idea);
  box-shadow: none; }
.rm-card:hover, .rm-card:focus-visible { box-shadow: var(--shadow-md); }
.rm-card-title { font-size: 13px; font-weight: 500; line-height: 1.35; color: var(--text-primary); }
.rm-card.rm-idea .rm-card-title { color: var(--text-secondary); }
.rm-card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; font-size: 11px; }
.rm-pill { padding: 1px 7px; border-radius: 20px; font-size: 11px; font-weight: 500; color: var(--rm-c);
  border: 1px solid currentColor; line-height: 1.5; }
.rm-pill.rm-q { color: var(--text-secondary); border-color: var(--border); }
.rm-card-sys { font-size: 11px; color: var(--text-muted); line-height: 1.3; }
.rm-card.rm-flash { outline: 2px solid var(--rm-building); outline-offset: 2px; }

.rm-timeline { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(230px, 1fr); gap: 10px;
  align-items: start; }
.rm-tcol { display: flex; flex-direction: column; gap: 6px; padding: 10px; border-radius: var(--radius-sm);
  background: var(--bg-secondary); border: 1px solid var(--border-light); }
.rm-tcol-head { display: flex; align-items: center; gap: 8px; padding: 0 2px 4px; }
.rm-tcol-shipped { background: var(--rm-travel); }
.rm-tcol-none { border-style: dashed; }
.rm-tcol .crumb { margin: 0; font-size: 12px; }
@media (prefers-reduced-motion: reduce) { .rm-card, .rm-add { transition: none; } }

/* Deals board (2026-10-17): Won, Stalled and Lost are closed columns that
   take drops and show their count; Show opens one. */
.col.closed { min-width: 150px; max-width: 170px; }
.col.closed .cards { display: none; }
.col.closed.open { min-width: 215px; max-width: 240px; }
.col.closed.open .cards { display: flex; }
.col-toggle { margin: 8px 8px 0; width: calc(100% - 16px); justify-content: center; }
.col.closed .col-toggle .when-open, .col.closed.open .col-toggle .when-closed { display: none; }
.col.closed.open .col-toggle .when-open { display: inline; }
.col.closed:not(.open) { padding-bottom: 8px; }
.col.closed:not(.open).dragover { background: rgba(var(--primary-rgb), .06); }

/* The deal's Current / New client flag: green for a current client, violet
   for a new one (never the brand red, which reads as an error). */
.badge.client-flag { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.badge.client-flag i { font-size: 9px; }
.badge.new-client { background: rgba(124, 58, 237, .1); color: #6d28d9; border-color: rgba(124, 58, 237, .28); }
