/*
 * AIMS Charter . Dashboard v2 Shell . Stylesheet
 *
 * Companion to dashboard-v2-shell.js. Together they overlay the v2
 * sidebar-first navigation on any existing /aimscharter/ page without
 * touching that page's own markup.
 *
 * Loading pattern (add to any dashboard, in the <head> after
 * setup-preview.css):
 *   <link rel="stylesheet" href="dashboard-v2-shell.css">
 *   <script src="dashboard-v2-shell.js" defer></script>
 *
 * The palette variables it consumes (--bg, --bg-elev, --border, etc.) all
 * live in setup-preview.css. This file only adds shape.
 */

/* FOUC prevention (Rob 2026-08-12): dashboard-v2-shell.js is deferred
   and runs after DOMContentLoaded, then synchronously restructures the
   DOM into a sidebar-first grid via wrapMainColumn(). Between initial
   paint and that restructure, users saw the raw main content laid out
   at the left edge of the viewport for a beat - visible on any
   redirect INTO a dashboard page (e.g., after verify -> Go to Charter).
   We hide the body until the shell script flips .v2-shell-on. Safety
   net: a fallback class .v2-shell-fallback added by the same script's
   setTimeout after 800ms so the body always eventually shows even if
   mount() throws or the JS never loads at all. */
html.v2-shell-preload body:not(.v2-shell-on):not(.v2-shell-fallback) {
  visibility: hidden;
}
html.v2-shell-preload body.v2-shell-on,
html.v2-shell-preload body.v2-shell-fallback {
  visibility: visible;
}

body.v2-shell-on {
  display: grid;
  grid-template-columns: 244px 1fr;
  min-height: 100vh;
  margin: 0;
}

/* SIDEBAR shell */
.v2-sidebar {
  background: var(--bg);
  border-right: 1px solid var(--border-soft);
  display: flex;
  flex-direction: column;
  padding: 16px 12px;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  z-index: 20;
}
.v2-sidebar::-webkit-scrollbar { width: 6px; }
.v2-sidebar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

/* WORKSPACE switcher */
.v2-workspace {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 120ms;
  color: var(--text);
  text-decoration: none;
  border: none;
  background: transparent;
  width: 100%;
  font-family: inherit;
  text-align: left;
}
.v2-workspace:hover { background: var(--bg-hover); }
.v2-workspace-avatar {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-deep) 100%);
  color: #111827;
  font-weight: 800;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  position: relative;
}
/* With a customer logo in it, this box matches .topbar-mark: transparent,
   clipped, no plate. The amber gradient stays ONLY for the initial-letter
   fallback, where it is the contrast behind the letter. Rob 2026-09-28:
   reported against light mode, applied to both because the amber sits behind
   the logo identically in dark. */
.v2-workspace-avatar.v2-has-logo {
  background: transparent;
  box-shadow: none;
  overflow: hidden;
}
.v2-workspace-avatar.v2-has-logo img { display: block; }

.v2-workspace-avatar::after {
  content: '';
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: 10px;
  height: 10px;
  background: var(--ok);
  border: 2px solid var(--bg);
  border-radius: 50%;
}
.v2-workspace-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.v2-workspace-name {
  color: var(--text-strong);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v2-workspace-email {
  color: var(--text-muted);
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v2-workspace-chevrons {
  display: flex;
  flex-direction: column;
  color: var(--text-muted);
  font-size: 9px;
  line-height: 1.1;
  flex-shrink: 0;
}

/* INTEGRATIONS mini-row */
.v2-integrations {
  margin-top: 24px;
  padding: 0 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.v2-integrations-label {
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
}
.v2-integration-chips {
  display: flex;
  gap: 4px;
  padding: 4px 6px;
  background: var(--bg-elev);
  border-radius: 6px;
  border: 1px solid var(--border-soft);
}
.v2-integration-chip {
  width: 16px;
  height: 16px;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-weight: 700;
}
.v2-chip-g { background: linear-gradient(135deg, #4285F4, #34A853); color: #fff; }
.v2-chip-m { background: #0078D4; color: #fff; }
.v2-chip-s { background: #4A154B; color: #fff; }

/* NAV sections */
.v2-nav-section { margin-top: 20px; }
.v2-nav-section-label {
  color: var(--text-dim);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0 8px;
  margin-bottom: 6px;
}
.v2-nav-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background 120ms, color 120ms;
  margin-bottom: 1px;
  text-decoration: none;
  border: none;
  background: transparent;
  width: 100%;
  font-family: inherit;
  text-align: left;
}
.v2-nav-link:hover { background: var(--bg-hover); color: var(--text-strong); }
.v2-nav-link.v2-active {
  background: var(--bg-elev-2);
  color: var(--text-strong);
  box-shadow: inset 0 0 0 1px var(--border);
}
.v2-nav-icon {
  width: 16px;
  height: 16px;
  color: var(--text-muted);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.v2-nav-link.v2-active .v2-nav-icon { color: var(--accent-deep, #7a5a00); }
.v2-nav-label { flex: 1; }
.v2-nav-badge {
  background: var(--bg-elev);
  color: var(--text-muted);
  font-size: 10px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 4px;
  letter-spacing: 0.02em;
  border: 1px solid var(--border);
  min-width: 20px;
  text-align: center;
}
.v2-nav-badge.v2-count-danger {
  color: var(--danger);
  border-color: transparent;
  background: var(--danger-dim);
}
.v2-nav-badge.v2-new {
  background: var(--new-dim);
  color: var(--new);
  border-color: transparent;
  font-size: 9px;
  padding: 2px 5px;
}
/* Light / dark theme toggle (Rob 2026-08-24). Segmented pill at the
   bottom of the sidebar. Mirrors the pattern on the AIMS Admin panel
   (aims_theme localStorage key). Active button is amber-filled. */
.v2-theme-toggle {
  display: flex;
  gap: 4px;
  padding: 4px;
  margin: 6px 12px 8px;
  background: var(--bg-elev-2, var(--bg-elev));
  border: 1px solid var(--border);
  border-radius: 999px;
}
.v2-theme-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 6px 8px;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 600;
  font-family: inherit;
  letter-spacing: 0.02em;
  border-radius: 999px;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
  white-space: nowrap;
}
.v2-theme-btn svg { width: 12px; height: 12px; flex-shrink: 0; }
.v2-theme-btn:hover { color: var(--text-strong); }
.v2-theme-btn.active {
  background: var(--accent);
  color: #ffffff;
}

/* SHARED MODAL SHELL (Rob 2026-08-24 refactor Phase 1)
   Moved out of dashboard-core.html so ANY shell-hosted page can host
   the shared modals (roster, incident, alerts, attestation, reporting,
   datagov, rhythm, framework, onboard). Modal-specific styling ships
   alongside each modal's own JS/HTML in dashboard-shared-modals.*. */

/* --- MODULE-CARD STATUS DOT (canonical, shared) ---
   Rob 2026-08-27: previously each dashboard host page (core, extended,
   max) defined its own inline .module-card-status-dot at 8px, so a
   size tweak needed three edits and inevitably drifted. Consolidated
   here at 14px + 5px halo to match the attention-row / completed-row
   dots Rob bumped on 2026-08-24 ("so red vs amber is unmistakable
   at a glance"). Single source of truth: any future size or color
   change lives here and applies to every dashboard.
*/
.module-card-status-dot {
  width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0;
}
.module-card-status-dot.green { background: var(--success); box-shadow: 0 0 0 5px rgba(16, 185, 129, 0.16); }
.module-card-status-dot.amber { background: var(--accent);  box-shadow: 0 0 0 5px rgba(238, 175, 17, 0.16); }
.module-card-status-dot.red   { background: #dc2626;         box-shadow: 0 0 0 5px rgba(220, 38, 38, 0.18); }
.module-card-status-dot.grey  { background: var(--text-dim); box-shadow: 0 0 0 5px rgba(107, 114, 128, 0.14); }

/* --- V3 ATTENTION SECTION (Rob 2026-08-27, canonical shared) ---
   Consolidated from dashboard-core.html so Extended + Max dashboards
   can render the same "Needs your attention" rows for mentioned +
   routed incidents. Follows the cross-dashboard consolidation rule
   (feedback_aimscharter_shared_dashboard_css): if the same visual
   element renders on 2+ dashboards, it lives here once. */
.v3-section { margin-top: 44px; }
.v3-section:first-of-type { margin-top: 8px; }
.v3-section-eyebrow {
  display: flex; align-items: baseline; gap: 12px;
  color: var(--text-muted); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  margin: 0 0 20px;
}
.v3-section-eyebrow .count {
  color: var(--text-dim); font-size: 11.5px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: none;
}
.v3-attention-rows { display: flex; flex-direction: column; gap: 6px; }
.v3-att-row {
  display: grid;
  grid-template-columns: 24px 1fr auto;
  gap: 16px;
  padding: 18px 20px;
  background: var(--bg);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  align-items: center;
  transition: border-color 0.15s ease, background 0.15s ease;
  cursor: pointer;
}
.v3-att-row:hover { border-color: var(--amber-quiet-border); background: var(--amber-quiet-hover); }
.v3-att-row .dot {
  width: 14px; height: 14px; border-radius: 50%;
  align-self: center; justify-self: center;
}
.v3-att-row .dot.amber { background: var(--accent); box-shadow: 0 0 0 5px rgba(238,175,17,0.16); }
.v3-att-row .dot.red   { background: #dc2626;       box-shadow: 0 0 0 5px rgba(220,38,38,0.18); }
.v3-att-row .dot.grey  { background: var(--text-dim); box-shadow: 0 0 0 5px rgba(107,114,128,0.14); }
.v3-att-row .body { min-width: 0; }
.v3-att-row .row-title {
  color: var(--text-strong); font-size: 15px; font-weight: 600;
  letter-spacing: -0.005em; display: flex; align-items: baseline; gap: 10px;
  flex-wrap: wrap;
}
.v3-att-row .row-count {
  color: #7a5a00; font-size: 12px; font-weight: 600;
  padding: 2px 8px; background: var(--amber-quiet-bg); border-radius: 999px;
}
.v3-att-row .row-summary {
  color: var(--text-muted); font-size: 12.5px; margin-top: 4px; line-height: 1.5;
}
.v3-att-row .row-actions { display: flex; align-items: center; gap: 12px; }
/* The incident row's primary action.
   Rob 2026-10-27: "when there is a nag related to an incident on the
   dashboard, make the 'Open incident' link a bit more prominent and easy
   to see." It was bare #7a5a00 text sitting on the cream mention ground
   (#FFF7ED) next to a Dismiss button, so the thing to do and the thing to
   ignore looked alike.
   Built as the twin of .doc-nag-btn-primary rather than as a new
   treatment: that is the established primary for a nag, and the comment
   above it records that #111827 on amber is the readable pairing in
   either theme, where white would be 1.95:1. The two nag types are meant
   to read as siblings. */
.v3-att-row .row-cta {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 14px; border-radius: 8px;
  background: var(--accent); color: #111827;
  border: 1px solid var(--accent);
  font-size: 13px; font-weight: 700; letter-spacing: 0.01em;
  line-height: 1; white-space: nowrap;
  transition: background 120ms ease, border-color 120ms ease;
}
/* The whole row is the click target, so the hover belongs to the row. */
.v3-att-row:hover .row-cta { background: #D99A0B; border-color: #D99A0B; }
.v3-att-row.v3-att-row-mentioned {
  background: #FFF7ED;
  border-left: 3px solid #EEAF11;
}
.v3-att-row.v3-att-row-mentioned .row-title .mentioned-badge {
  display: inline-block; padding: 1px 8px;
  border-radius: 999px; background: #EEAF11; color: #111827;
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.06em;
  text-transform: uppercase; vertical-align: 1px;
  margin-right: 8px;
}
.row-mention-snippet {
  margin-top: 8px; padding: 8px 10px;
  background: #ffffff; border: 1px solid #FED7AA;
  border-radius: 6px;
  color: #7C2D12; font-size: 12.5px; line-height: 1.5;
}
.row-mention-snippet strong { color: #7C2D12; font-weight: 700; }

/* --- DOC NAG CARD (Rob 2026-08-27, canonical shared) ---
   Refined callout for "Actions required from you" doc assignments.
   Approved mockup: https://claude.ai/code/artifact/7e87bd81-...
   Amber-tinted hairline card with icon slot, eyebrow label, due-date
   chip, primary CTA + kebab menu. Sits in the same visual language
   as .v3-att-row-mentioned so doc nag reads as sibling to incident
   mentions. Used on Core (via existing renderMyAssignments) + Extended
   + Max (via shared renderDocActionsAttention in shared-modals.js). */
.doc-nag-card {
  position: relative;
  display: grid;
  grid-template-columns: 44px 1fr auto;
  gap: 16px;
  align-items: flex-start;
  padding: 18px 20px;
  background: #FFF7ED;
  border: 1px solid #FED7AA;
  border-radius: 12px;
  box-shadow: 0 2px 6px rgba(238, 175, 17, 0.10);
  transition: box-shadow 150ms ease, border-color 150ms ease;
}
.doc-nag-card + .doc-nag-card { margin-top: 8px; }
.doc-nag-card::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--accent);
  border-radius: 12px 0 0 12px;
}
.doc-nag-card:hover {
  border-color: var(--accent);
  box-shadow: 0 4px 14px rgba(238,175,17,0.18);
}
.doc-nag-icon {
  width: 44px; height: 44px; border-radius: 10px;
  background: rgba(238,175,17,0.16);
  color: var(--accent-deep);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.doc-nag-icon svg { width: 22px; height: 22px; }
.doc-nag-body { min-width: 0; }
.doc-nag-eyebrow {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--accent-deep);
  font-size: 10px; font-weight: 800;
  letter-spacing: 0.14em; text-transform: uppercase;
  margin-bottom: 4px;
}
.doc-nag-title {
  color: var(--text-strong);
  font-size: 15px; font-weight: 700;
  letter-spacing: -0.005em; line-height: 1.35;
  margin-bottom: 4px;
  word-break: break-word;
}
.doc-nag-meta {
  color: var(--text-muted);
  font-size: 12.5px; line-height: 1.5;
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap;
}
.doc-nag-meta .sep { color: var(--text-dim); }
.doc-nag-due-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  background: rgba(238,175,17,0.16);
  color: var(--accent-deep);
  border: 1px solid rgba(238,175,17,0.4);
}
.doc-nag-due-chip.overdue {
  background: rgba(220,38,38,0.10);
  color: #DC2626;
  border-color: rgba(220,38,38,0.28);
}
.doc-nag-due-chip .clock {
  width: 8px; height: 8px; border-radius: 50%;
  background: currentColor; opacity: 0.7;
  display: inline-block;
}
.doc-nag-resign {
  margin-top: 8px; padding: 8px 10px;
  background: #ffffff; border: 1px solid #FED7AA;
  border-radius: 6px;
  color: #7C2D12; font-size: 12px; font-weight: 500;
  line-height: 1.5;
}
.doc-nag-resign .what-changed {
  margin-top: 4px; font-weight: 500;
}
.doc-nag-resign .what-changed .lead { opacity: 0.8; }
.doc-nag-actions {
  display: flex; align-items: center; gap: 8px;
  flex-shrink: 0;
}
@media (max-width: 720px) {
  .doc-nag-card { grid-template-columns: 44px 1fr; }
  .doc-nag-actions {
    grid-column: 1 / -1;
    justify-content: flex-start;
    flex-wrap: wrap;
    margin-top: 4px;
  }
}
.doc-nag-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border-radius: 8px;
  font-family: inherit;
  font-size: 13px; font-weight: 700; letter-spacing: 0.01em;
  cursor: pointer; line-height: 1;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
  text-decoration: none; white-space: nowrap;
}
/* Rob 2026-09-25: white on #EEAF11 is 1.95:1, in either theme. The amber
   fill is the same on both grounds, so its ink is too: #111827 gives
   9.10:1. Hovering used to swap to --accent-deep, which is now
   theme-dependent, so it gave no visible change in dark and dark-on-dark
   in light. A fixed darker amber keeps the same ink readable. */
.doc-nag-btn-primary {
  background: var(--accent); color: #111827;
  border: 1px solid var(--accent);
  padding: 9px 14px;
}
.doc-nag-btn-primary:hover {
  background: #D99A0B; border-color: #D99A0B; color: #111827;
}
.doc-nag-btn-outline {
  background: transparent; color: var(--text-strong);
  border: 1px solid var(--border);
}
.doc-nag-btn-outline:hover {
  border-color: var(--accent); color: var(--accent-deep);
  background: #ffffff;
}
/* Kebab menu — native <details> so no JS toggle needed. */
.doc-nag-kebab {
  position: relative;
  padding: 0;
}
.doc-nag-kebab summary {
  list-style: none;
  width: 32px; height: 32px;
  border-radius: 8px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.doc-nag-kebab summary::-webkit-details-marker { display: none; }
.doc-nag-kebab summary:hover { background: rgba(0,0,0,0.04); color: var(--text-strong); }
.doc-nag-kebab summary svg { width: 16px; height: 16px; }
.doc-nag-kebab .kebab-menu {
  position: absolute;
  top: 100%; right: 0;
  margin-top: 4px;
  min-width: 180px;
  padding: 6px;
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(15,23,42,0.10);
  z-index: 20;
}
.doc-nag-kebab .kebab-menu button {
  display: block; width: 100%;
  padding: 8px 10px;
  background: transparent; border: none;
  border-radius: 6px;
  font-family: inherit;
  font-size: 13px; font-weight: 500;
  color: var(--text-strong);
  text-align: left;
  cursor: pointer;
}
.doc-nag-kebab .kebab-menu button:hover {
  background: var(--bg-hover, #efe9dc);
}

/* --- ROLE-BASED PRIORITIZATION (Rob 2026-08-27) ---
   Persona chips now DO something: reorder the module grid so the
   modules a given persona actually touches sit at top with a
   "PRIORITY" pill, and minimize the rest into a chip strip below.
   Approved mockup: artifact 5ad3311b. Persona choice persists via
   localStorage ('aims_persona'). Four personas map to jobs, not
   titles: Executive · Governance owner · Finance & attribution ·
   Admin. Wiring lives in dashboard-shared-modals.js
   (window.applyPersona). */

.persona-focus {
  margin-top: 14px;
  color: var(--text);
  font-size: 12.5px;
  background: #FFF7ED;
  border: 1px solid #FED7AA;
  border-radius: 10px;
  padding: 10px 14px;
  line-height: 1.5;
}
.persona-focus strong { color: var(--text-strong); font-weight: 700; }

/* Persona KPI strip — sits above the module grid. Four cells. */
.persona-kpi-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin: 20px 0 8px;
}
@media (max-width: 860px) {
  .persona-kpi-strip { grid-template-columns: repeat(2, 1fr); }
}
.persona-kpi-cell {
  padding: 14px 16px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 12px;
  display: flex; flex-direction: column; gap: 4px;
}
.persona-kpi-label {
  color: var(--text-muted);
  font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.10em; text-transform: uppercase;
}
.persona-kpi-num {
  color: var(--text-strong);
  font-size: 24px; font-weight: 800;
  letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
  display: flex; align-items: baseline; gap: 6px;
}
.persona-kpi-num .unit {
  color: var(--text-muted); font-size: 12px; font-weight: 500;
}
.persona-kpi-num.attn { color: #DC2626; }
.persona-kpi-num.warn { color: var(--accent-deep); }
.persona-kpi-num.ok   { color: var(--success); }
.persona-kpi-sub { color: var(--text-muted); font-size: 11.5px; }

/* Modules the selected role leads with. Rob 2026-09-19: the old
   PRIORITY pill shouted on every card (the governance persona covers 13
   of 18 modules); a slightly lifted card carries the distinction
   quietly. Re-applied on every persona switch. */
.module-card { position: relative; transition: box-shadow 160ms ease, border-color 160ms ease; }
.module-card.priority-for-persona {
  border-color: rgba(238, 175, 17, 0.35);
  box-shadow: 0 3px 12px rgba(15, 23, 42, 0.10);
}
.module-card.priority-for-persona:hover {
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.14);
}
/* "Actions required from you" cards in dark mode. Rob 2026-09-25:
   Heather switched to dark and this box stayed light while its text
   followed the theme, so near-white text sat on a cream card at 1.02:1.
   Effectively invisible.

   .doc-nag-card hardcoded background #FFF7ED and border #FED7AA while
   every piece of text inside used var(--text-strong), var(--text-muted)
   and var(--accent-deep). Half the component was themed and half was not.

   Keeping the amber "this needs you" signal, but as a tint over the dark
   ground rather than a light slab. Fixing the surface also repairs the
   eyebrow and the due chip, which were light-on-light for the same
   reason. */
/* The same half-themed pattern, found by scanning this file for rules
   with a hardcoded light background and no dark counterpart. 13 had
   none. These are the ones that carry text, so they were the ones that
   became unreadable rather than merely pale.

   .v3-att-row-mentioned matters most: it is the row that highlights an
   incident where you were @-mentioned, which is exactly what Rob was
   testing when he found the nag card. */
:root[data-theme="dark"] .v3-att-row.v3-att-row-mentioned {
  background: rgba(238, 175, 17, 0.10);
}
:root[data-theme="dark"] .row-mention-snippet {
  background: rgba(238, 175, 17, 0.07);
  border-color: rgba(238, 175, 17, 0.30);
  color: var(--text);
}
:root[data-theme="dark"] .row-mention-snippet strong { color: var(--text-strong); }
:root[data-theme="dark"] .persona-focus {
  background: rgba(238, 175, 17, 0.09);
  border-color: rgba(238, 175, 17, 0.28);
}
:root[data-theme="dark"] .doc-nag-kebab .kebab-menu {
  background: var(--bg-elev);
  border-color: var(--border);
}
:root[data-theme="dark"] .doc-nag-resign {
  background: var(--bg-elev);
  border-color: var(--border);
}
:root[data-theme="dark"] .doc-nag-btn-outline:hover {
  background: rgba(255, 255, 255, 0.06);
}

:root[data-theme="dark"] .doc-nag-card {
  background: rgba(238, 175, 17, 0.10);
  border-color: rgba(238, 175, 17, 0.32);
}
:root[data-theme="dark"] .doc-nag-card:hover {
  border-color: var(--accent);
}
/* The overdue chip's red was picked for a light card. */
:root[data-theme="dark"] .doc-nag-due-chip.overdue {
  background: rgba(220, 38, 38, 0.18);
  color: #FCA5A5;
  border-color: rgba(220, 38, 38, 0.45);
}

:root[data-theme="dark"] .module-card.priority-for-persona {
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.45);
}

/* "Also on your plan" — minimized module chip strip */
.module-min-section {
  margin-top: 22px;
}
.module-min-eyebrow {
  display: flex; justify-content: space-between; align-items: baseline;
  color: var(--text-muted);
  font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  margin: 0 0 10px;
}
.module-min-eyebrow .link {
  color: var(--accent-deep);
  font-size: 11.5px; font-weight: 700;
  letter-spacing: 0.02em; text-transform: none;
  cursor: pointer;
  border: none; background: transparent;
  font-family: inherit;
  padding: 0;
}
.module-min-eyebrow .link:hover { text-decoration: underline; }
.module-min-strip {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 14px 16px;
  background: rgba(238,175,17,0.05);
  border: 1px dashed var(--border);
  border-radius: 12px;
}
.module-min-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px;
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  color: var(--text-muted);
  font-size: 12px; font-weight: 500;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}
.module-min-chip:hover {
  border-color: rgba(238,175,17,0.4);
  background: var(--bg-elev);
  color: var(--text);
}
.module-min-chip .mini-dot {
  width: 8px; height: 8px; border-radius: 50%;
  flex-shrink: 0;
}
.module-min-chip .mini-dot.green { background: var(--success); }
.module-min-chip .mini-dot.amber { background: var(--accent); }
.module-min-chip .mini-dot.red   { background: #DC2626; }
.module-min-chip .mini-dot.grey  { background: var(--text-dim); }

/* Persona-focus section header — placed above the priority grid */
.persona-priority-eyebrow {
  color: var(--text-muted);
  font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  margin: 22px 0 12px;
}

.module-modal-backdrop {
  display: none;
  position: fixed; inset: 0; z-index: 200;
  background: rgba(10, 15, 26, 0.75);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  align-items: center; justify-content: center;
  padding: 24px;
}
.module-modal-backdrop.show { display: flex; }
.module-modal {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 16px;
  max-width: 760px; width: 100%; max-height: 90vh;
  display: flex; flex-direction: column;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

/* The dark counterpart, canonical here for the same reason the light one
   is. Rob 2026-10-28: "in dark mode let's have them all match Core's
   value and look."

   These lived inline in dashboard-core.html, so Core ran at 0.12 / 0.35 /
   0.06 while Extended and Max fell through to the setup-preview.css base
   at 0.14 / 0.35 / 0.22. The visible one was the hover: an attention row
   on Core lifted by a sixth of what it lifted on the other two.

   Core's are the chosen values. The comment they carried says they were
   picked to sit with incident-severity.medium and
   rhythm-status-pill.due-soon, and the quieter hover is the better call
   on a dark ground, where a 22% amber wash reads as a selection rather
   than a hover.

   :root:not([data-theme="light"]) is (0,2,0), above the (0,1,0) base in
   setup-preview.css, and it cannot collide with the light block below:
   the two selectors are mutually exclusive. */
:root:not([data-theme="light"]) {
  --amber-quiet-bg: rgba(238, 175, 17, 0.12);
  --amber-quiet-border: rgba(238, 175, 17, 0.35);
  --amber-quiet-hover: rgba(238, 175, 17, 0.06);
}

/* ===================================================================
   LIGHT THEME: the canonical palette and the shared chrome.
   Rob 2026-10-27.

   Every dashboard defined its own copy of this. The ten core tokens were
   identical in all three, and in calendar.html, account.html,
   document-repo.html and trust-settings.html too, so the duplication was
   never visible as a bug. What it hid was everything around it.

   Measured before this change, in light mode, across the three
   dashboards: THIRTEEN computed properties disagreed.

     --amber-quiet-* existed only on Core. Extended and Max resolved them
     to the dark-first values from setup-preview.css, so the count chip on
     an attention row was an opaque cream on Core and a translucent amber
     over white on the other two.

     Core overrode dashboard-v2-shell.css with its own module-modal rules.
     The same modal, injected from the same shared HTML, had a 0.35
     backdrop on Core and 0.55 everywhere else.

     .topbar-avatar, .hero-logo, .topbar-dropdown-menu and
     .activity-row.head each had two or three different answers.

   The mechanism that let it drift unseen is worth naming: the same rule
   was written as html[data-theme="light"] (0,1,1), :root[data-theme=
   "light"] (0,2,0) and [data-theme="light"] (0,1,0) in different files,
   so "the same" rule carried three different specificities and won or
   lost depending on which dashboard you were looking at.

   Everything here uses :root[data-theme="light"], one form, one
   specificity. Add a light rule for shared chrome HERE, not inline.
   scripts/light-theme-drift.test.js fails if an inline copy comes back.
   =================================================================== */
:root[data-theme="light"] {
  --bg: #ffffff;
  --bg-elev: #faf7f2;
  --bg-elev-2: #f5f1e8;
  --bg-hover: #efe9dc;
  --border: #e5e0d3;
  --border-soft: #efe9dc;
  --text: #334155;
  --text-dim: #5B6472;
  --text-muted: #6b7280;
  --text-strong: #111827;
  /* Opaque, not a tint. The dark-theme values in setup-preview.css are
     rgba over a dark ground; reused on white they read differently, which
     is exactly what Extended and Max were doing. */
  --amber-quiet-bg: #fbf3e5;
  --amber-quiet-border: #f0e3c9;
  --amber-quiet-hover: #fdfbf5;
}

/* Shared chrome. Where the three disagreed, the majority value wins:
   it is the smallest change that ends the disagreement, and no third
   answer gets invented. */
:root[data-theme="light"] .topbar {
  background: rgba(255, 255, 255, 0.85);
  border-bottom-color: #efe9dc;
}
/* Was #efe9dc / #334155 on Core, #f5f1e8 / #111827 on Extended and Max. */
:root[data-theme="light"] .topbar-avatar {
  background: #f5f1e8;
  color: #111827;
}
/* Background was white on Extended and cream on Core and Max; the border
   was #e5e0d3 on Core and #efe9dc on the other two. Majority on each. */
:root[data-theme="light"] .hero-logo {
  background: #f5f1e8;
  border-color: #efe9dc;
}
/* 0.12 on Core and Max, 0.10 on Extended. */
:root[data-theme="light"] .topbar-dropdown-menu {
  box-shadow: 0 12px 32px rgba(17, 24, 39, 0.12);
}
/* #f5f1e8 on Core, a 3% ink wash on Extended and Max. The wash holds up
   on both the page ground and an elevated card; the opaque cream does
   not. */
:root[data-theme="light"] .activity-row.head,
:root[data-theme="light"] .attest-cadence-badge.onetime {
  background: rgba(17, 24, 39, 0.03);
}

[data-theme="light"] .module-modal-backdrop { background: rgba(17, 24, 39, 0.55); }
[data-theme="light"] .module-modal { box-shadow: 0 20px 60px rgba(15, 23, 42, 0.18); }
.module-modal.report-modal-wide { max-width: 960px; }
.module-modal-header {
  padding: 22px 26px 18px;
  border-bottom: 1px solid var(--border-soft, var(--border));
  display: flex; justify-content: space-between; align-items: flex-start; gap: 20px;
}
.module-modal-eyebrow {
  color: #60A5FA; font-size: 10px; letter-spacing: 0.16em;
  text-transform: uppercase; font-weight: 700; margin-bottom: 6px;
}
[data-theme="light"] .module-modal-eyebrow { color: #2563EB; }
.module-modal-header h2 {
  color: var(--text-strong); font-size: 22px; font-weight: 600;
  letter-spacing: -0.015em; margin: 0 0 8px;
}
.module-modal-status {
  display: flex; align-items: center; gap: 8px;
  color: var(--text-muted); font-size: 11.5px;
}
.module-modal-status .status-dot { width: 6px; height: 6px; border-radius: 50%; background: #10B981; box-shadow: 0 0 6px rgba(16, 185, 129, 0.5); }
.module-modal-close {
  background: transparent; border: none; color: var(--text-muted);
  font-size: 26px; line-height: 1; cursor: pointer;
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  transition: background 120ms, color 120ms;
  flex-shrink: 0;
}
.module-modal-close:hover { background: rgba(255, 255, 255, 0.05); color: var(--text-strong); }
[data-theme="light"] .module-modal-close:hover { background: rgba(15, 23, 42, 0.05); }
.module-modal-body { padding: 22px 26px; overflow-y: auto; flex: 1; }
.module-modal-section { margin-bottom: 28px; }
.module-modal-section:last-child { margin-bottom: 0; }
.module-modal-section-head {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: 12px;
}
.module-modal-section-head h3 {
  color: var(--text-strong); font-size: 13.5px; font-weight: 600;
  letter-spacing: 0.02em; margin: 0;
}
.module-modal-section-count { color: var(--text-muted); font-size: 11.5px; }
.module-modal-primary {
  padding: 10px 16px;
  background: var(--accent); color: #ffffff;
  font-size: 12.5px; font-weight: 600;
  border: none; border-radius: 6px; cursor: pointer;
  align-self: flex-start; font-family: inherit;
}
.module-modal-primary:hover { background: var(--accent-deep, #B08000); }
.module-modal-secondary {
  padding: 10px 16px;
  background: transparent; color: var(--text);
  font-size: 12.5px; font-weight: 600;
  border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
  font-family: inherit;
}
.module-modal-secondary:hover { border-color: var(--accent); color: var(--accent-deep, #7a5a00); }
.module-modal-footer {
  padding: 16px 26px;
  border-top: 1px solid var(--border-soft, var(--border));
  display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap;
}
.module-modal-footer-note { color: var(--text-muted); font-size: 11.5px; }
.module-modal-done {
  padding: 8px 16px;
  background: transparent; color: var(--text-muted);
  font-size: 12.5px; font-weight: 600;
  border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
  font-family: inherit;
}
.module-modal-done:hover { border-color: var(--accent); color: var(--accent-deep, #7a5a00); }

/* HID admin quick-jump link (Rob 2026-08-24). Sits at the bottom of
   the sidebar for anyone on HID_ADMIN_EMAILS; subtle blue tint so it
   reads as an internal tool link, not a customer nav item. */
.v2-hid-admin-link {
  color: #1E40AF;
  margin-top: 4px;
  border-top: 1px dashed var(--border-soft, var(--border));
  padding-top: 10px;
}
.v2-hid-admin-link .v2-nav-icon { color: #1E40AF; }
.v2-hid-admin-link:hover { background: #EFF6FF; color: #1E3A8A; }
.v2-hid-admin-link:hover .v2-nav-icon { color: #1E3A8A; }
.v2-hid-admin-link .v2-nav-badge.v2-new {
  background: #DBEAFE; color: #1E40AF;
}
.v2-nav-chev {
  color: var(--text-dim);
  font-size: 10px;
}

/* PROMO card at bottom */
.v2-promo-spacer { flex: 1; min-height: 20px; }
.v2-promo-card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  position: relative;
  margin-bottom: 12px;
}
.v2-promo-close {
  position: absolute;
  top: 6px;
  right: 6px;
  color: var(--text-dim);
  font-size: 14px;
  cursor: pointer;
  background: none;
  border: none;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.v2-promo-close:hover { color: var(--text); }
.v2-promo-badge {
  display: inline-block;
  background: var(--new-dim);
  color: var(--new);
  font-size: 9px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 3px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 8px;
}
.v2-promo-title {
  color: var(--text-strong);
  font-size: 12px;
  font-weight: 600;
  margin-bottom: 4px;
  line-height: 1.3;
}
.v2-promo-desc {
  color: var(--text-muted);
  font-size: 11px;
  line-height: 1.5;
  margin-bottom: 10px;
}
.v2-promo-cta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 9px;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text-strong);
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.v2-promo-cta:hover { background: var(--bg-hover); }

/* Bottom fixed nav */
.v2-sidebar-bottom {
  padding-top: 12px;
  border-top: 1px solid var(--border-soft);
}

/* MAIN COLUMN . Wraps the existing topbar + main content of the host page.
   `min-width: 0` is what actually prevents grid overflow; explicit
   overflow-x: hidden would silently break sticky positioning on the
   host page's topbar (browsers can promote overflow-x to overflow: auto),
   so we do not set it. */
.v2-main-column {
  min-width: 0;
  min-height: 100vh;
}

/* Neutralize the host page's sticky topbar backdrop since it now sits inside
   a column, not viewport-wide. Keep the sticky behavior. */
body.v2-shell-on .topbar {
  position: sticky;
  top: 0;
  z-index: 15;
}

/* Legacy WORKSPACE DROPDOWN MENU styles — retired 2026-08-24 when the
   workspace button was promoted to a plain home link and its contents
   moved into the sidebar's Company expandable group (.v2-nav-group).
   Rules kept here only in case a third-party page still references the
   classes; nothing in the shell emits them anymore. */
.v2-workspace-menu { display: none !important; }
.v2-workspace-menu-item, .v2-workspace-menu-divider, .v2-workspace-chevrons { display: none !important; }

/* COMPANY NAV GROUP (Rob 2026-08-24)
   Expandable group in the main sidebar nav holding Account settings,
   Company profile, Branding & personalization, and Log out. The group
   toggle looks like a normal nav link with a chevron on the right that
   rotates 180deg when the group is open. Sublinks sit indented under
   the toggle so the parent/child relationship is unambiguous. */
.v2-nav-group { margin-bottom: 1px; }
.v2-nav-group-toggle .v2-nav-chevron {
  margin-left: auto;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  transition: transform 160ms ease, color 120ms;
}
.v2-nav-group.v2-open .v2-nav-group-toggle .v2-nav-chevron {
  transform: rotate(180deg);
  color: var(--text);
}
.v2-nav-submenu {
  display: none;
  padding: 2px 0 4px 30px;
  margin-top: 2px;
}
.v2-nav-group.v2-open .v2-nav-submenu { display: block; }
.v2-nav-sublink {
  display: block;
  padding: 6px 10px;
  border-radius: 6px;
  color: var(--text-muted);
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  transition: background 120ms, color 120ms;
  margin-bottom: 1px;
}
.v2-nav-sublink:hover { background: var(--bg-hover); color: var(--text-strong); }
.v2-nav-sublink-danger { color: #B91C1C; }
.v2-nav-sublink-danger:hover { background: #FEF2F2; color: #7F1D1D; }
.v2-nav-sublink-divider {
  height: 1px;
  background: var(--border-soft);
  margin: 4px 6px;
}

/* WORKSPACE LINK (Rob 2026-08-24): upper-left company block was a
   dropdown toggle; now it's a home-button link. Hover treatment lifts
   slightly + a subtle right arrow appears on hover as a visual cue
   that it navigates. */
.v2-workspace-link { position: relative; }
.v2-workspace-link::after {
  content: '';
  position: absolute;
  right: 12px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--text-muted);
  border-top: 1.5px solid var(--text-muted);
  transform: translateY(-50%) rotate(45deg);
  opacity: 0;
  transition: opacity 140ms ease, right 140ms ease;
}
.v2-workspace-link:hover::after { opacity: 0.7; right: 10px; }

/* WHITE-LABEL MODE.
   When applyBranding() detects a customized instance (customer set logo OR
   primaryColor OR accentColor), body gets .v2-branded. Under that class:
     - The host page's topbar-brand block is hidden (customer's tenant identity
       already lives in the sidebar workspace switcher; no need to also badge
       the platform up top)
     - A v2-powered-by footer is injected at the bottom of .v2-main-column
       to preserve the AIMS Charter attribution in a subtle, out-of-the-way
       location so the customer's brand carries the visual weight up top.
*/
body.v2-branded .topbar-brand,
body.v2-branded .topbar-mark,
body.v2-branded .topbar-brand-text,
body.v2-branded header > .brand,
body.v2-branded header > a.brand {
  display: none !important;
}
.v2-powered-lines { display: flex; flex-direction: column; gap: 3px; align-items: center; }
.v2-powered-acronym { color: var(--text-dim, var(--text-muted)); font-size: 10.5px; font-weight: 500; }
.v2-powered-by {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 20px 24px;
  margin-top: 40px;
  border-top: 1px solid var(--border-soft);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-align: center;
  background: transparent;
}
.v2-powered-by .v2-powered-mark {
  width: 18px;
  height: 18px;
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.v2-powered-by .v2-powered-mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.v2-powered-by a {
  color: var(--text);
  text-decoration: none;
  font-weight: 600;
}
.v2-powered-by a:hover { color: var(--accent-deep, #7a5a00); }

/* Small top-right "Powered by AIMS Charter" pill. Only rendered on
   branded (white-label) instances. Non-branded instances still show the
   original topbar-brand mark which normalizeExistingBrandLinks() rewrites
   to point at the same absolute AIMS Charter URL. */
.v2-powered-top {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-muted);
  text-decoration: none;
  letter-spacing: 0.02em;
  border: 1px solid var(--border);
  border-radius: 4px;
  margin-left: 8px;
  background: transparent;
}
.v2-powered-top:hover {
  color: var(--text-strong);
  border-color: var(--text-muted);
}
.v2-powered-top-mark {
  width: 14px;
  height: 14px;
  border-radius: 3px;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.v2-powered-top-mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Mobile hamburger toggle button — hidden ≥960px, visible below */
.v2-mobile-toggle {
  display: none;
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 40;
  width: 44px;
  height: 44px;
  border-radius: 8px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  color: var(--text-strong);
  cursor: pointer;
  align-items: center;
  justify-content: center;
  padding: 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.v2-mobile-toggle:hover { background: var(--bg-hover); }
.v2-mobile-toggle svg { width: 20px; height: 20px; }

/* Backdrop shown behind off-canvas sidebar */
.v2-sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 25;
  animation: v2-fade-in 180ms ease-out;
}
@keyframes v2-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* Responsive: off-canvas sidebar under 960px (touch devices + phones) */
@media (max-width: 960px) {
  body.v2-shell-on {
    grid-template-columns: 1fr;
  }
  .v2-mobile-toggle { display: inline-flex; }
  .v2-sidebar {
    position: fixed;
    top: 0; left: 0;
    height: 100vh;
    width: 280px;
    max-width: 85vw;
    transform: translateX(-100%);
    transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1);
    border-right: 1px solid var(--border-soft);
    border-bottom: none;
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.25);
  }
  body.v2-sidebar-open .v2-sidebar {
    transform: translateX(0);
  }
  /* Above the backdrop, or the backdrop paints over the drawer and eats
     every tap. The sidebar's base z-index is 20 and the backdrop is 25,
     which on a phone meant the menu slid out looking dark grey with
     nothing clickable in it. Rob 2026-10-26. Still below the hamburger
     at 40, so the toggle stays reachable to close it. */
  .v2-sidebar { z-index: 30; }
  body.v2-sidebar-open .v2-sidebar-backdrop {
    display: block;
  }
  /* Main content needs top padding for the fixed hamburger to not overlap */
  .v2-main-column {
    padding-top: 56px;
  }
  .v2-promo-spacer { display: none; }
}

/* Phone-size polish (≤480px): tighten padding, allow full-width cards */
@media (max-width: 480px) {
  .v2-sidebar { width: 88vw; max-width: 320px; padding: 12px 10px; }
  .v2-main-column { padding-top: 52px; }
}

/* ─── Demo-account upsell suppression (Rob 2026-09-12) ─────────────
   body.is-demo-account is set by dashboard-v2-shell.js loadContext()
   when the account has isDemo=true (provisioned via
   admin-create-demo-account). Any element tagged data-hide-on-demo
   disappears — Extended upgrade bands, add-on marketplace CTAs, the
   sidebar's promo card, and any future upsell surface that opts in.
   Prospect walkthroughs render clean, without "Growing beyond Core?"
   or "Upgrade to Extended" nags competing with the demo story. */
body.is-demo-account [data-hide-on-demo] { display: none !important; }

/* ─── Commercial prompts off (Rob 2026-10-06) ──────────────────────
   Two different accounts want the same thing for different reasons:
   a demo account must not pitch a prospect mid-walkthrough, and High
   Intent Digital's own internal account must not invite them to buy
   from themselves. Rob 2026-10-06: "on the High Intent Digital
   account, let's remove all of the upgrade nags and offers."

   hideUpsells is an account flag (admin-account-flags.js) and stamps
   body.hide-upsells in dashboard-v2-shell.js loadContext.

   data-commercial-prompt is the canonical marker. Every element that
   asks for money, or for a call that leads to money, carries it:
   upgrade bands, add-on marketplace CTAs, the Fully Managed callout,
   the PLUS+ availability badge, the sidebar promo card.

   [data-hide-on-demo] is the old name, still honoured so nothing
   in flight regresses, and so a tag missed in the rename still works
   for the case it was written for. */
body.hide-upsells [data-commercial-prompt],
body.hide-upsells [data-hide-on-demo],
body.is-demo-account [data-commercial-prompt] { display: none !important; }

/* ── Recent activity table ──────────────────────────────────────────
   Canonical home for .activity-row (Rob 2026-09-19). It was defined
   inline in dashboard-core / extended / max, identically, and the
   Action column was a fixed 100px, so longer event kinds
   (demo_tier_switched, reminder_timezone_set,
   tier_downgrade_warning_acknowledged) were clipped at the card edge.
   The column now flexes and long kinds wrap instead of overflowing. */
.activity-row {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr) minmax(0, 210px) minmax(150px, 210px);
  gap: 16px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border-soft);
  font-size: 13px;
  align-items: center;
}
.activity-row > * { min-width: 0; }           /* let long text wrap, not overflow */
.activity-row:last-child { border-bottom: none; }
.activity-row.head {
  background: rgba(255, 255, 255, 0.02);
  color: var(--text-muted);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  padding-top: 12px;
  padding-bottom: 12px;
}
.activity-row .mono {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
}
.activity-row .user {
  color: var(--text-strong);
  font-weight: 500;
  overflow-wrap: anywhere;                    /* long emails break rather than spill */
}
.activity-row .doc { color: var(--text); overflow-wrap: anywhere; }
.activity-row .action {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.35;
}
.activity-row .action.opened     { background: rgba(16, 185, 129, 0.12); color: var(--success); }
.activity-row .action.downloaded { background: rgba(238, 175, 17, 0.12); color: var(--accent-deep, #7a5a00); }
.activity-row .action.uploaded   { background: rgba(96, 165, 250, 0.12); color: var(--info); }
.activity-row .action.signed     { background: rgba(16, 185, 129, 0.12); color: var(--success); }
.activity-row .action.assigned   { background: rgba(96, 165, 250, 0.12); color: var(--info); }
.activity-row .action.pull       { background: rgba(139, 92, 246, 0.14); color: #A78BFA; }
@media (max-width: 900px) {
  .activity-row { grid-template-columns: 120px minmax(0, 1fr) minmax(0, 170px) minmax(130px, 170px); gap: 12px; padding: 12px 14px; }
}
@media (max-width: 720px) {
  .activity-row { grid-template-columns: 1fr; gap: 4px; }
  .activity-row.head { display: none; }
}

/* ---------------------------------------------------------------------
   Account switcher. Rob 2026-10-21: "a 'you are in Client B' banner, and
   a dropdown ... to select any other active accounts that the email
   address is associated with as a user."

   Only rendered when the signed-in email is on more than one account, so
   the overwhelming majority of users never see it. It sits directly under
   the workspace block, which already names the current account, so this
   offers the choice without repeating the answer.

   A native <select>: keyboard accessible, correct on a phone, and with no
   open/close state of its own to get wedged.
   --------------------------------------------------------------------- */
.v2-acct-switch {
  padding: 10px 14px 12px;
  margin: 0 0 4px;
  border-bottom: 1px solid var(--v2-border, rgba(255, 255, 255, 0.08));
}
.v2-acct-switch-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--v2-muted, #9aa3af);
  margin-bottom: 6px;
}
.v2-acct-select {
  width: 100%;
  box-sizing: border-box;
  padding: 7px 9px;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--v2-text, #e5e7eb);
  background: var(--v2-surface-2, rgba(255, 255, 255, 0.05));
  border: 1px solid var(--v2-border, rgba(255, 255, 255, 0.14));
  border-radius: 6px;
  cursor: pointer;
}
.v2-acct-select:focus {
  outline: none;
  border-color: var(--v2-accent, #EEAF11);
  box-shadow: 0 0 0 3px rgba(238, 175, 17, 0.18);
}
.v2-acct-select:disabled { opacity: 0.6; cursor: wait; }
.v2-acct-switch-status {
  margin-top: 6px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--v2-muted, #9aa3af);
}
.v2-acct-switch-error { color: #F87171; }

/* Light theme: the sidebar inverts, so the control has to follow it. */
:root[data-theme="light"] .v2-acct-switch,
body.v2-light .v2-acct-switch { border-bottom-color: #e5e0d3; }
:root[data-theme="light"] .v2-acct-select,
body.v2-light .v2-acct-select {
  color: #111827;
  background: #ffffff;
  border-color: #e5e0d3;
}
:root[data-theme="light"] .v2-acct-switch-label,
body.v2-light .v2-acct-switch-label { color: #6b7280; }
