/* ASGT Command Center V2 — Navigation Shell tokens + layout
   Loaded after asgt-design-system.css via loader.js so these win the cascade.
   Uses !important where necessary to beat legacy design-system.css. */

:root {
  /* Backgrounds & surfaces */
  --bg-primary: #0C0B0F;
  --bg-outer-rail: #08070B;
  --bg-inner-sidebar: #0F0E12;
  --bg-surface: #161519;
  --bg-surface-2: #1E1D22;

  /* Borders */
  --border-v2: rgba(255,255,255,0.06);
  --border-hover-v2: rgba(255,255,255,0.12);

  /* Text */
  --text-v2: #E8E6ED;
  --text-muted-v2: #8A8693;
  --text-dim: #5C5869;

  /* Accents */
  --accent: #840F68;
  --accent-light: #A83D8C;

  /* Role colours */
  --role-chief: #D4537E;
  --role-analyst: #378ADD;
  --role-executor: #1D9E75;
  --role-scanner: #BA7517;

  /* Status */
  --status-active: #5DCAA5;

  /* Severity colours for alert cards */
  --severity-critical: #E85A6F;
  --severity-critical-bg: rgba(232,90,111,0.10);
  --severity-high:     #E8945A;
  --severity-high-bg:  rgba(232,148,90,0.10);
  --severity-warning:  #E8C55A;
  --severity-warning-bg: rgba(232,197,90,0.10);
  --severity-info:     #5A9EE8;
  --severity-info-bg:  rgba(90,158,232,0.10);

  /* Radii */
  --radius-sm-v2: 8px;
  --radius-md-v2: 10px;
  --radius-lg-v2: 14px;

  /* Fonts */
  --font-sans-v2: 'Inter', 'DM Sans', system-ui, sans-serif;
  --font-mono-v2: 'JetBrains Mono', 'DM Mono', monospace;

  /* Spacing scale */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-8: 48px;

  /* Typography scale */
  --text-xs:  11px;
  --text-sm:  12px;
  --text-base: 13px;
  --text-md:  14px;
  --text-lg:  16px;
  --text-xl:  20px;
  --text-2xl: 24px;
  --text-3xl: 28px;

  /* Font weights */
  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;

  /* Shell dimensions.
     Both of these were stale duplicates of sizes the design system owns, and
     both were wrong by the time anyone measured them:

       --outer-rail-w was 64px; .asgt-rail is 68px and wins, so the sidebar's
       sticky offset sat 4px inside the rail and overlapped its border.

       --inner-sidebar-w was 220px; .asgt-sidebar is var(--asgt-sidebar-w),
       which is 248px normally and 224px at data-density="compact". A fixed
       number here can never track a token that moves with density.

     They now mirror the system rather than restate it. */
  --outer-rail-w: 68px;                      /* = .asgt-rail inline-size */
  --inner-sidebar-w: var(--asgt-sidebar-w);  /* follows density */
}

/* When the v2 shell is mounted, hide the legacy #app entirely and promote
   .shell-root to a fixed full-viewport container. This implicitly hides
   nav.sidebar + nav.mobile-nav (they live inside #app) and prevents the
   double-stacking of #app and .shell-root as siblings of <body>.
   The MutationObserver in shell-nav.js still reads #app's .active class
   for login-gating, so hiding #app via display:none does not break auth. */
/* The old Command Center sidebar is still in index.html and the v2 shell hides
 * #app only once it has booted. Between sign-in and that moment the legacy nav
 * paints -- "Command Center / AI Agent System v1.0", Dashboard, Activity Feed --
 * and flashes on every refresh.
 *
 * visibility rather than display, and scoped to the legacy nav rather than to
 * #app: the shell reads from these nodes while it builds, and hiding the whole
 * app would leave a blank screen for anyone whose shell never sets the class.
 * Hidden it occupies its space silently; shown it never was there.
 */
body:not(.v2-shell-active) #app > .sidebar,
body:not(.v2-shell-active) #app > .mobile-nav { visibility: hidden; }

body.v2-shell-active #app {
  display: none !important;
}
body.v2-shell-active .shell-root {
  position: fixed;
  inset: 0;
  display: flex;
  background: var(--bg-primary);
}

/* Root shell layout */
.shell-root {
  display: flex;
  min-height: 100vh;
  background: var(--bg-primary);
  color: var(--text-v2);
  font-family: var(--font-sans-v2);
}

/* Outer rail */
.outer-rail {
  width: var(--outer-rail-w);
  background: var(--bg-outer-rail);
  border-right: 1px solid var(--border-v2);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 12px 0;
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  z-index: 100;
}
.outer-rail-slot.active {
  background: var(--bg-surface-2);
  color: var(--text-v2);
}
.outer-rail-slot.active::before {
  content: "";
  position: absolute;
  left: -12px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: var(--accent);
  border-radius: 2px;
}
/* Tooltip */
.outer-rail-slot[data-tooltip]:hover::after {
  content: attr(data-tooltip);
  position: absolute;
  left: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%);
  background: var(--bg-surface-2);
  color: var(--text-v2);
  padding: 6px 10px;
  border-radius: var(--radius-sm-v2);
  border: 1px solid var(--border-v2);
  font-size: 12px;
  white-space: nowrap;
  z-index: 1000;
  pointer-events: none;
}

/* Inner sidebar */
.inner-sidebar {
  width: var(--inner-sidebar-w);
  background: var(--bg-inner-sidebar);
  border-right: 1px solid var(--border-v2);
  position: fixed;
  top: 0;
  left: var(--outer-rail-w);
  height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 16px 0;
  overflow-y: auto;
}
.inner-sidebar.hidden {
  display: none;
}
.dept-item.active {
  background: var(--bg-surface-2);
  color: var(--text-v2);
  font-weight: 500;
}

/* Workspace */
/* The margin clears the RAIL ONLY, not the rail plus the sidebar.
   It used to be calc(rail + sidebar), which was correct while .inner-sidebar
   was position:fixed and therefore out of flow. The design system's
   .asgt-sidebar sets position:sticky and loads last, so it wins — and sticky
   elements stay IN flow. The sidebar was taking its 224px of the flex row and
   the margin was then reserving that same 224px a second time, pushing this
   column to x=508 when it should start at 292. That was the dead band down the
   left of every screen, and it cost the content 216px of width. */
.shell-workspace {
  flex: 1;
  margin-left: var(--outer-rail-w);
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--bg-primary);
}
.shell-workspace.fullwidth {
  margin-left: var(--outer-rail-w);
}
.workspace-tabs.hidden {
  display: none;
}
.workspace-tab.active {
  color: var(--text-v2);
  border-bottom-color: var(--accent);
  font-weight: 500;
}
.workspace-body {
  flex: 1;
  position: relative;
  overflow: auto;
}

/* Legacy .main override: when inside the new shell, remove its own height/overflow
   and let the workspace-body handle scrolling. Must beat design-system.css !important. */
/* padding is 0, not 24px 32px.
   .main holds the legacy pages and stays in the layout even when every one of
   them is hidden. With padding it rendered as a 48px-tall empty box above the
   real content, which is the gap that sat between the tabs and every
   shell-drawn screen. An empty container must occupy nothing.

   Removing it costs the legacy pages nothing: Dashboard and Activity were
   migrated in c8ff83e and now carry their own .asgt-container gutter, so this
   padding was doubling theirs. Measured after the change — the Activity page
   keeps its 24px inline padding from .asgt-container. */
.shell-workspace .workspace-body .main {
  margin-left: 0 !important;
  width: 100% !important;
  height: auto !important;
  min-height: 0 !important;
  padding: 0 !important;
  overflow: visible !important;
}

/* Placeholder card for departments without a real overlay (Session 2 legacy — prefer .placeholder-card-v2) */
/* Department overview header (used for non-populated departments) */
/* MIGRATED — 2026-08-11. .dept-overview-header, .dept-subtitle and
   .placeholder-footer-link went with buildDeptHeaderMinimal(): the header
   printed the department name and division directly under a shell header
   already showing both, and the footer link is now .asgt-btn--quiet. */

/* Session 7 M-4 — shared modal backdrop (WhatsApp + Import). Inline style
   controls display; this class provides the flex layout + positioning. */
.wa-modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1000;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, 0.6);
}

/* (Session 7 M-7 session-expired banner was reverted — deferred to Session 8) */

/* MIGRATED TO THE DESIGN SYSTEM — 2026-08-11
   The runtime shell's component styling now comes from asgt-ui.v2.css:
     .outer-rail*      -> .asgt-rail / .asgt-rail__item / .asgt-divider
     .inner-sidebar-*  -> .asgt-sidebar__group / __group-label / __brand
     .dept-item        -> .asgt-navitem
     .workspace-header -> .asgt-pagehead
     .workspace-tab*   -> .asgt-tabs / .asgt-tab
     .workspace-title  -> .asgt-pagehead__text, status dot -> .asgt-presence
     .workspace-breadcrumb -> .asgt-breadcrumb
   What stayed: .hidden and .fullwidth (JS toggles them, the system has no
   equivalent), .workspace-body (scroll container), and every
   body.v2-shell-active / body.role-* rule, which is behaviour not styling.
   =========================================================================== */

/* ============================================================
 * Session 4C — WhatsApp leads (Sales → WhatsApp)
 * ============================================================ */
/* MIGRATED TO THE DESIGN SYSTEM — 2026-08-11 (Sales -> WhatsApp, Pipeline)
     .wa-page-v2        -> .asgt-container--wide + .asgt-stack
     .wa-header-v2      -> .asgt-pagehead / __text / __desc / __actions
     .wa-add-btn        -> .asgt-btn--primary
     .wa-import-btn     -> .asgt-btn--outline
     .wa-filters-v2     -> .asgt-filterbar / .asgt-search / __end
     .wa-filters-v2 input|select -> .asgt-input--sm / .asgt-select--sm
     .wa-table-wrap     -> .asgt-table-wrap inside .asgt-panel
     .wa-table          -> .asgt-table (+ .num, .sku, .asgt-cell-main)
     .wa-cell-empty     -> .asgt-faint
     .wa-expand-row td  -> .asgt-spec chips + .asgt-feed__text prose
     .wa-status-pill    -> .asgt-badge--stock|low|out|info|plain
     .wa-icon-btn       -> .asgt-btn--sm.asgt-btn--quiet in .asgt-table__actions
     #wa-*-modal panels -> .asgt-modal / __head / __body / __foot
     #wa-*-modal fields -> .asgt-field / .asgt-label / .asgt-input / .asgt-select
                           / .asgt-textarea / .asgt-input-group
     .wa-form-row       -> .asgt-grid--2
     .wa-import-hint    -> .asgt-help-panel
     .wa-import-preview -> .asgt-report__raw on a <pre>

   The modal input rules had to go rather than merely stop being used:
   `#wa-modal input` is (1,0,1) and beat .asgt-input (0,1,0) outright, so the
   system's controls would have rendered with the old padding and radius inside
   the one form in this tool that edits customer records.

   Still legacy below this point: the Tasks tab modal, which is why
   .wa-form-field and .wa-modal-actions survive further down. */

/* MIGRATED TO THE DESIGN SYSTEM — 2026-08-11 (Finance overview)
     .card-v2 / -title / -meta      -> .asgt-card / .asgt-entity__title /
                                       .asgt-feed__text
     .finance-alerts-host           -> .asgt-container--wide (it existed only to
                                       carry page padding, and its 0 top value
                                       is what left this screen touching the tabs)
     .alerts-stack                  -> .asgt-stack
     .alert-card-v2 + .severity-*   -> .asgt-card--edged + .asgt-badge--out|low|
                                       info. Severity is a badge now rather than
                                       a tinted card body and a coloured bar, so
                                       one alert no longer floods a third of the
                                       screen with red wash.
     .alert-header / -left / -title -> .asgt-entity__head / __title / __badges
     .alert-body-text               -> .asgt-feed__text
     .alert-solver + -icon + -text  -> .asgt-help-panel, the one alert-family
                                       component the system documents as
                                       "instructional, never a status colour" —
                                       which is exactly what a solver action is
     .alert-meta-row / .meta-*      -> .asgt-spec chips. Current value, target,
                                       date and source are four facts, and the
                                       system puts facts in pressed wells rather
                                       than running them together with middots.
     .alert-actions / -action-btn   -> .asgt-btn--sm.asgt-btn--quiet
     .alert-expanded-label          -> .asgt-report__heading
     .alert-expanded-grid / -key /
       -value                       -> .asgt-entity__meta, which already renders
                                       a label with its figure in mono beneath
     .alert-expanded-footer         -> .asgt-feed__time + .asgt-data
     .alert-expanded-empty          -> .asgt-faint
     .placeholder-card-v2 stays — the "coming soon" screens still use it.

   What stays below: the open/closed state only. The system has no disclosure
   component, and the button toggles .expanded on the card. */
.alert-card .alert-expanded { display: none; }
.alert-card.expanded .alert-expanded {
  display: block;
  margin-block-start: var(--space-4);
  padding-block-start: var(--space-4);
  border-block-start: 1px dashed var(--border-v2);
}

/* ============================================================
 * Session 4B step 2 — Knowledge Base polish
 * ============================================================ */
.kb-page-v2 {
  padding: var(--space-5) var(--space-6);
  font-family: var(--font-sans-v2);
}

.kb-header-v2 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-5);
  gap: var(--space-4);
}
.kb-header-v2 h2 {
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  color: var(--text-v2);
  margin: 0;
}
.kb-header-v2 .kb-header-meta {
  font-size: var(--text-sm);
  color: var(--text-muted-v2);
  font-family: var(--font-mono-v2);
}
.kb-header-v2 .kb-add-btn {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: #fff;
  font-family: var(--font-sans-v2);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm-v2);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  transition: background 0.15s ease;
}
.kb-header-v2 .kb-add-btn:hover { background: var(--accent-light); border-color: var(--accent-light); }
.kb-header-v2 .kb-add-btn svg { width: 14px; height: 14px; }

.kb-filters-v2 {
  display: flex;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
  flex-wrap: wrap;
}
.kb-filters-v2 input,
.kb-filters-v2 select {
  background: var(--bg-surface);
  border: 1px solid var(--border-v2);
  border-radius: var(--radius-sm-v2);
  color: var(--text-v2);
  font-family: var(--font-sans-v2);
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  outline: none;
  transition: border-color 0.15s ease;
}
.kb-filters-v2 input:focus,
.kb-filters-v2 select:focus { border-color: var(--border-hover-v2); }
.kb-filters-v2 input { flex: 1; min-width: 220px; }
.kb-filters-v2 select { min-width: 140px; }

.kb-section-count {
  font-family: var(--font-mono-v2);
  font-size: var(--text-xs);
  color: var(--text-muted-v2);
}

.kb-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.kb-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-v2);
  border-left: 3px solid var(--border-v2);
  border-radius: var(--radius-md-v2);
  padding: var(--space-4);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.kb-card:hover { border-color: var(--border-hover-v2); background: var(--bg-surface-2); }
.kb-card.priority-critical { border-left-color: var(--severity-critical); }
.kb-card.priority-high     { border-left-color: var(--severity-warning); }
.kb-card.priority-normal   { border-left-color: var(--border-v2); }
.kb-card.priority-low      { border-left-color: var(--border-v2); }

.kb-card .kb-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}
.kb-card .kb-card-title {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--text-v2);
  line-height: 1.35;
  flex: 1;
  min-width: 0;
}
.kb-card .kb-card-actions {
  display: flex;
  gap: var(--space-1);
  flex-shrink: 0;
}
.kb-card .kb-icon-btn {
  background: transparent;
  border: 1px solid var(--border-v2);
  color: var(--text-muted-v2);
  padding: 4px 6px;
  border-radius: var(--radius-sm-v2);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s ease, color 0.15s ease;
}
.kb-card .kb-icon-btn:hover { background: var(--bg-surface-2); color: var(--text-v2); }
.kb-card .kb-icon-btn svg { width: 12px; height: 12px; }

.kb-card .kb-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  font-size: var(--text-xs);
  color: var(--text-muted-v2);
}
.kb-card .kb-meta-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--bg-surface-2);
  color: var(--text-muted-v2);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}
.kb-card .kb-meta-chip.brand { color: var(--accent-light); background: rgba(168,61,140,0.10); }
.kb-card .kb-meta-sep { color: var(--text-dim); }

.kb-card .kb-priority-pill {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: 2px 8px;
  border-radius: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.kb-card .kb-priority-pill.priority-critical { background: rgba(232,90,111,0.12); color: var(--severity-critical); }
.kb-card .kb-priority-pill.priority-high     { background: rgba(232,197,90,0.12); color: var(--severity-warning); }

.kb-card .kb-card-preview {
  font-size: var(--text-sm);
  color: var(--text-muted-v2);
  line-height: 1.5;
}
.kb-card.expanded .kb-card-preview { display: none; }

.kb-card .kb-card-full {
  display: none;
  font-size: var(--text-sm);
  color: var(--text-v2);
  line-height: 1.6;
  white-space: pre-wrap;
  padding-top: var(--space-3);
  border-top: 1px dashed var(--border-v2);
}
.kb-card.expanded .kb-card-full { display: block; }

/* Modal override — target legacy #kb-modal without rebuilding markup */
#kb-modal { background: rgba(0,0,0,0.6) !important; }
#kb-modal > div {
  background: var(--bg-surface) !important;
  border: 1px solid var(--border-v2) !important;
  border-radius: var(--radius-lg-v2) !important;
  color: var(--text-v2) !important;
  font-family: var(--font-sans-v2) !important;
}
#kb-modal h3 { color: var(--text-v2) !important; font-weight: var(--weight-semibold) !important; }
#kb-modal label { color: var(--text-muted-v2) !important; font-size: var(--text-xs) !important; text-transform: uppercase !important; letter-spacing: 0.06em !important; font-weight: var(--weight-medium) !important; }
#kb-modal input,
#kb-modal select,
#kb-modal textarea {
  background: var(--bg-surface-2) !important;
  border: 1px solid var(--border-v2) !important;
  color: var(--text-v2) !important;
  border-radius: var(--radius-sm-v2) !important;
  font-family: var(--font-sans-v2) !important;
  font-size: var(--text-sm) !important;
}
#kb-modal input:focus,
#kb-modal select:focus,
#kb-modal textarea:focus { border-color: var(--border-hover-v2) !important; outline: none !important; }
#kb-modal button {
  background: var(--bg-surface-2) !important;
  border: 1px solid var(--border-v2) !important;
  color: var(--text-v2) !important;
  border-radius: var(--radius-sm-v2) !important;
  font-family: var(--font-sans-v2) !important;
  font-size: var(--text-sm) !important;
  font-weight: var(--weight-medium) !important;
  padding: var(--space-2) var(--space-4) !important;
  cursor: pointer !important;
}
#kb-modal button:hover { background: var(--bg-surface) !important; }
#kb-modal button[onclick*="saveKBEntry"], #kb-modal button#kb-save-btn { background: var(--accent) !important; border-color: var(--accent) !important; color: #fff !important; }
#kb-modal button[onclick*="saveKBEntry"]:hover, #kb-modal button#kb-save-btn:hover { background: var(--accent-light) !important; border-color: var(--accent-light) !important; }

/* Coming-soon placeholder — the canonical empty state */
/* MIGRATED — 2026-08-11. .placeholder-card-v2 and its icon/title/subtitle are
   gone with showPlaceholder(), which now renders .asgt-placeholder. The old
   card looked like an ordinary card, so an unwired tab read as a real screen
   that happened to be empty; the system's dashed hatched surface cannot be
   mistaken for one. */

/* Section header used above grouped content on custom Overview tabs */
.section-header-v2 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-6);
  margin: var(--space-5) 0 var(--space-3);
}
.section-header-v2 h3 {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  font-weight: var(--weight-semibold);
}
.section-header-v2 .section-count {
  font-size: var(--text-xs);
  color: var(--text-muted-v2);
  font-family: var(--font-mono-v2);
}

/* Finance Overview host — where the embedded Issues table lands */
/* A .inner-sidebar-counts rule stood here to pull the "1 department" line into
   line with the rest of the sidebar. The line itself is gone now, so the patch
   that aligned it goes too rather than sitting here matching nothing. */

/* Block padding for the migrated Executive Office screen.
   .asgt-container gives inline padding only — the design system leaves block
   spacing to the page, because a container is used both as a full page and as
   a section inside one. --space-5 matches .wa-page-v2 and .finance-alerts-host
   so all three screens that mount in .workspace-body start at the same height.

   The better end state is one block-padding rule on .workspace-body with the
   per-screen ones deleted, so a new screen cannot forget it the way Finance
   did. That touches screens outside this one and is left for their migration. */
.exec-page { padding-block: var(--space-5); }

/* Top padding was 0, so this card sat flush against the tab underline with
   nothing between them. It only looked acceptable while the empty legacy .main
   sat above it contributing 48px of accidental space; collapsing that box
   exposed the missing value. --space-5 matches .exec-alert-wrapper and
   .wa-page-v2, which are the other two screens that mount here. */
.finance-alerts-host {
  padding: var(--space-5) var(--space-6);
}
.finance-alerts-host .main {
  padding: 0 !important;
  margin: 0 !important;
}

/* ============================================================
 * Session 4A — Executive Overview utility classes
 * Reused in Sessions 4B and 4C across all pages
 * ============================================================ */

/* Icon circle — colored container for KPI/alert icons */
.icon-circle {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  flex-shrink: 0;
}
.icon-circle.sm { width: 28px; height: 28px; }
.icon-circle.sm svg { width: 14px; height: 14px; }
.icon-circle.md { width: 36px; height: 36px; }
.icon-circle.md svg { width: 18px; height: 18px; }
.icon-circle.lg { width: 40px; height: 40px; }
.icon-circle.lg svg { width: 20px; height: 20px; }

/* Semantic color variants — tinted bg + solid icon */
.icon-circle.accent-green    { background: rgba(29,158,117,0.12);  color: var(--role-executor); }
.icon-circle.accent-blue     { background: rgba(55,138,221,0.12);  color: var(--role-analyst); }
.icon-circle.accent-purple   { background: rgba(168,61,140,0.12);  color: var(--accent-light); }
.icon-circle.accent-red      { background: rgba(232,90,111,0.12);  color: var(--severity-critical); }
.icon-circle.accent-amber    { background: rgba(186,117,23,0.14);  color: var(--role-scanner); }
.icon-circle.accent-pink     { background: rgba(212,83,126,0.12);  color: var(--role-chief); }

/* Hero KPI card — the 4 big cards in the top row */
.hero-kpi-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-v2);
  border-radius: var(--radius-lg-v2);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  position: relative;
  overflow: hidden;
  transition: border-color 0.15s ease;
}
.hero-kpi-card:hover { border-color: var(--border-hover-v2); }

/* Critical variant — red top strip */
.hero-kpi-card.critical::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--severity-critical);
}

.hero-kpi-card .kpi-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.hero-kpi-card .kpi-label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted-v2);
  font-weight: var(--weight-medium);
}
.hero-kpi-card .kpi-value {
  font-family: var(--font-mono-v2);
  font-size: var(--text-2xl);
  font-weight: var(--weight-semibold);
  color: var(--text-v2);
  line-height: 1.1;
}
.hero-kpi-card .kpi-value.critical { color: var(--severity-critical); }
.hero-kpi-card .kpi-context {
  font-size: var(--text-sm);
  color: var(--text-muted-v2);
}
.hero-kpi-card .kpi-context.muted-red { color: var(--severity-critical); }
.hero-kpi-card .kpi-context.muted-amber { color: var(--role-scanner); }
.hero-kpi-card .kpi-context.muted-green { color: var(--role-executor); }

/* PIN-protected hero KPI (Repayable Capital). Click to unlock for session.
   Uses visibility:hidden on content + a centered full-card overlay, NOT blur. */
.hero-kpi-card.kpi-blurred { position: relative; cursor: pointer; }
.hero-kpi-card.kpi-blurred.unlocked { cursor: default; }
.hero-kpi-card.kpi-blurred .kpi-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  visibility: hidden;
}
.hero-kpi-card.kpi-blurred.unlocked .kpi-content {
  visibility: visible;
}
.hero-kpi-card.kpi-blurred .kpi-blur-overlay {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: rgba(22, 21, 25, 0.85);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  border-radius: inherit;
  cursor: pointer;
  z-index: 2;
}
.hero-kpi-card.kpi-blurred .kpi-blur-overlay .lock-icon {
  width: 24px;
  height: 24px;
  color: var(--text-muted-v2);
  display: flex;
  align-items: center;
  justify-content: center;
}
.hero-kpi-card.kpi-blurred .kpi-blur-overlay .lock-icon svg { width: 24px; height: 24px; }
.hero-kpi-card.kpi-blurred .kpi-blur-overlay .lock-label {
  font-size: var(--text-xs);
  font-family: var(--font-mono-v2);
  color: var(--text-muted-v2);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.hero-kpi-card.kpi-blurred.unlocked .kpi-blur-overlay { display: none; }

/* Progress bar (for Debt KPI) */
.kpi-progress {
  width: 100%;
  height: 6px;
  background: var(--bg-surface-2);
  border-radius: 3px;
  overflow: hidden;
  margin-top: var(--space-2);
}
.kpi-progress .kpi-progress-fill {
  height: 100%;
  background: var(--accent-light);
  transition: width 0.3s ease;
}

/* Compact KPI card — smaller 4-across row below the heroes */
.compact-kpi-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-v2);
  border-radius: var(--radius-md-v2);
  padding: var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  transition: border-color 0.15s ease;
}
.compact-kpi-card:hover { border-color: var(--border-hover-v2); }
.compact-kpi-card .compact-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.compact-kpi-card .compact-label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted-v2);
  font-weight: var(--weight-medium);
}
.compact-kpi-card .compact-value {
  font-family: var(--font-mono-v2);
  font-size: 18px;
  font-weight: var(--weight-semibold);
  color: var(--text-v2);
  line-height: 1.1;
}
.compact-kpi-card .compact-value.critical { color: var(--severity-critical); }
.compact-kpi-card .compact-sub {
  font-size: var(--text-xs);
  color: var(--text-dim);
}
.compact-kpi-card .compact-sub.muted-green { color: var(--role-executor); }
.compact-kpi-card .compact-sub.muted-amber { color: var(--role-scanner); }
.compact-kpi-card .compact-sub.muted-red { color: var(--severity-critical); }

/* Trend pill */
.trend-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: 2px 8px;
  border-radius: 10px;
  line-height: 1.4;
}
.trend-pill.up       { background: rgba(29,158,117,0.15);  color: var(--role-executor); }
.trend-pill.down     { background: rgba(232,90,111,0.15);  color: var(--severity-critical); }
.trend-pill.neutral  { background: rgba(138,134,147,0.15); color: var(--text-muted-v2); }
.trend-pill.critical { background: rgba(232,90,111,0.15);  color: var(--severity-critical); }

/* Alert banner — full-width gradient-tinted banner at the top of Executive Overview */
.alert-banner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg-v2);
  border: 1px solid var(--border-v2);
  background: linear-gradient(90deg, rgba(232,90,111,0.12), rgba(232,90,111,0.04));
  border-left: 3px solid var(--severity-critical);
}
.alert-banner .alert-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.alert-banner .alert-title {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--text-v2);
}
.alert-banner .alert-summary {
  font-size: var(--text-sm);
  color: var(--text-muted-v2);
}
.alert-banner .alert-action {
  background: transparent;
  border: 1px solid var(--border-hover-v2);
  color: var(--text-v2);
  font-size: var(--text-sm);
  font-family: var(--font-sans-v2);
  font-weight: var(--weight-medium);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm-v2);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease;
}
.alert-banner .alert-action:hover { background: var(--bg-surface-2); }
.alert-banner .alert-right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}
.alert-banner .alert-timestamp {
  font-size: var(--text-xs);
  font-family: var(--font-mono-v2);
  color: var(--text-dim);
  white-space: nowrap;
}

/* Avatar initials */
.avatar-initials {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-family: var(--font-sans-v2);
  font-weight: var(--weight-semibold);
  color: #fff;
  flex-shrink: 0;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.avatar-initials.sm { width: 28px; height: 28px; font-size: 11px; }
.avatar-initials.md { width: 36px; height: 36px; font-size: 13px; }
.avatar-initials.lg { width: 44px; height: 44px; font-size: 15px; }
/* Deterministic palette — renderer picks one based on hash of name */
.avatar-initials.color-1 { background: #7B4FA6; }
.avatar-initials.color-2 { background: #3E7CB8; }
.avatar-initials.color-3 { background: #2E8B6F; }
.avatar-initials.color-4 { background: #B86B3E; }
.avatar-initials.color-5 { background: #A64F7B; }
.avatar-initials.color-6 { background: #4F6BA6; }

/* Team Status row */
.team-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm-v2);
  transition: background 0.15s ease;
}
.team-row:hover { background: var(--bg-surface-2); }
.team-row .team-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.team-row .team-name {
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--text-v2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.team-row .team-role {
  font-size: var(--text-xs);
  color: var(--text-muted-v2);
}
.team-row .team-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  color: var(--text-muted-v2);
  white-space: nowrap;
}
.team-row .team-status .status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-dim);
}
.team-row .team-status.online .status-dot { background: var(--status-active); box-shadow: 0 0 6px rgba(93,202,165,0.5); }
.team-row .team-status.busy   .status-dot { background: var(--role-scanner); }
.team-row .team-status.offline .status-dot { background: var(--text-dim); }

/* Activity row */
.activity-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm-v2);
  transition: background 0.15s ease;
}
.activity-row:hover { background: var(--bg-surface-2); }
.activity-row .activity-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.activity-row .activity-text {
  font-size: var(--text-base);
  color: var(--text-v2);
  line-height: 1.4;
}
.activity-row .activity-text strong {
  font-weight: var(--weight-semibold);
}
.activity-row .activity-meta {
  font-size: var(--text-xs);
  color: var(--text-muted-v2);
  font-family: var(--font-mono-v2);
}

/* Split layout for Team Status + Today's Activity bottom row.
   align-items: start so each card only grows to content height (no stretch). */
.exec-bottom-grid {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: var(--space-4);
  padding: 0 var(--space-6) var(--space-6);
  align-items: start;
}
.exec-bottom-grid .card-v2 {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.exec-bottom-grid .card-v2 .card-v2-title {
  padding: 0 var(--space-2);
  margin-bottom: var(--space-2);
}

/* KPI grid rows — hero (4 across) and compact (4 across) */
.exec-kpi-row-hero {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
  padding: 0 var(--space-6);
  margin-bottom: var(--space-4);
}
.exec-kpi-row-compact {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
  padding: 0 var(--space-6);
  margin-bottom: var(--space-5);
}

/* Top-level alert banner wrapper (full-width padding) */
.exec-alert-wrapper {
  padding: var(--space-5) var(--space-6) var(--space-4);
}

/* Executive Overview last-updated stamp */

/* ============================================================
   Session 9 — Role-based access hook
   ============================================================ */
.admin-only { display: none; }
body.role-admin .admin-only { display: initial; }

/* ============================================================
   Session 9B — Single-purpose layout (whatsapp_only)
   ============================================================ */
.single-purpose-topbar { display: none; }

body.role-single-purpose .outer-rail,
body.role-single-purpose .inner-sidebar {
  display: none !important;
}
body.role-single-purpose.v2-shell-active .shell-root,
body.role-single-purpose .shell-root {
  flex-direction: column;
}
body.role-single-purpose .shell-workspace,
body.role-single-purpose .shell-workspace.fullwidth {
  margin-left: 0 !important;
  width: 100% !important;
  max-width: 100% !important;
}
body.role-single-purpose .workspace-body {
  margin-left: 0 !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
}

body.role-single-purpose .single-purpose-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-v2);
  height: 56px;
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: 100;
}
body.role-single-purpose .single-purpose-topbar .sp-brand {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
}
body.role-single-purpose .single-purpose-topbar .sp-logo {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: linear-gradient(135deg, #a61f8f 0%, #7B1A6E 50%, #5B0D50 100%);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 16px;
  flex-shrink: 0;
  box-shadow: 0 4px 12px rgba(123, 26, 110, 0.35), inset 0 1px 0 rgba(255,255,255,0.18);
}
body.role-single-purpose .single-purpose-topbar .sp-title {
  margin-left: 12px;
  font-size: 15px;
  font-weight: 600;
  color: var(--text-v2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.role-single-purpose .single-purpose-topbar .sp-logout {
  background: transparent;
  border: 1px solid var(--border-v2);
  border-radius: 8px;
  padding: 8px 14px;
  color: var(--text-muted-v2);
  font-size: 13px;
  cursor: pointer;
  transition: all 0.15s ease;
  flex-shrink: 0;
}
body.role-single-purpose .single-purpose-topbar .sp-logout:hover,
body.role-single-purpose .single-purpose-topbar .sp-logout:active {
  background: var(--bg-surface-2);
  color: var(--text-v2);
  border-color: var(--text-muted-v2);
}

@media (max-width: 768px) {
  /* .wa-header-v2 and .wa-filters-v2 were listed here too; both were deleted
     in the WhatsApp migration and are now .asgt-pagehead / .asgt-filterbar,
     which take their gutter from .asgt-container. */
  body.role-single-purpose .wa-card-list {
    padding-left: 12px !important;
    padding-right: 12px !important;
    width: 100%;
    max-width: 100%;
  }
  body.role-single-purpose .single-purpose-topbar {
    padding: 10px 12px;
  }
  body.role-single-purpose .single-purpose-topbar .sp-title {
    font-size: 14px;
  }
}

/* ============================================================
   Session 8 Phase 2 — WhatsApp mobile cards (≤768px)
   ============================================================ */
.wa-card-list { display: none; }

/* The modal is a desktop dialog by default and full-screen on a phone. Both
   live here rather than on a style attribute so the media query can win by
   source order — an inline max-block-size would have outranked it. */
.wa-modal-backdrop > .asgt-modal { max-block-size: 85vh; overflow-y: auto; }

@media (max-width: 768px) {
  /* Swap the table for the card list. Scoped to .wa-pipeline so this never
     reaches another screen's .asgt-table-wrap. */
  .wa-pipeline .asgt-table-wrap { display: none; }
  .wa-card-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  .wa-modal-backdrop > .asgt-modal {
    inline-size: 100%;
    max-inline-size: 100%;
    block-size: 100vh;
    max-block-size: 100vh;
    border-radius: 0;
  }
}

/* Disclosure state for the mobile cards. The card surface is .asgt-card and its
   contents are .asgt-entity; these three rules are only open-vs-closed, which
   the design system has no component for.

   The mobile overrides that used to sit here for .wa-header-v2, .wa-actions,
   .wa-add-btn and .wa-filters-v2 are gone with those classes: .asgt-pagehead
   and .asgt-filterbar are already flex-wrap containers, so they fold on a
   narrow viewport without being told to. */
.wa-card { cursor: pointer; }
.wa-card-detail { display: none; }
.wa-card.expanded .wa-card-detail { display: block; }

/* Click affordance. A lead row opens its detail row, but .asgt-table is a
   surface rather than a control, so the system correctly leaves the cursor
   alone and this screen says so itself. */
.wa-pipeline .asgt-table tbody tr { cursor: pointer; }
.wa-pipeline .asgt-table tbody tr.wa-expand-row { cursor: default; }

/* MIGRATED TO THE DESIGN SYSTEM — 2026-08-11 (WhatsApp Overview, Activity, Tasks)
     .wa-kpi-grid / .wa-kpi-card.hero -> .asgt-metrics / .asgt-metrics__cell
     .wa-kpi-label|value|context      -> .asgt-figure__label|__value|
                                         .asgt-metrics__sub
     .wa-kpi-currency                 -> .asgt-figure__unit (trailing, per the
                                         library: value first, then unit)
     .wa-kpi-section-header           -> .asgt-panel__head + .asgt-panel__label
     .wa-kpi-card-small               -> .asgt-well in an .asgt-grid--4
     .wa-kpi-pill                     -> .asgt-badge
     .wa-kpi-card.alert               -> an .asgt-badge--out in that cell's sub
                                         line, shown only when overdue > 0
     .wa-activity-wrap|filters        -> .asgt-panel + .asgt-filterbar
     .wa-timeline / .wa-activity-row  -> .asgt-feed / .asgt-feed__item (a <ul>)
     .wa-activity-avatar              -> .asgt-avatar--sm
     .wa-activity-line|user|lead      -> .asgt-feed__text and its <b>
     .wa-activity-line code           -> .asgt-feed__detail, the second line
     .wa-activity-time                -> .asgt-feed__time + .asgt-data
     .wa-loading / .wa-empty          -> .asgt-empty--sm
     .wa-tasks-wrap|header|title      -> .asgt-pagehead
     .wa-tasks-board                  -> .asgt-grid--3
     .wa-tasks-col*                   -> .asgt-panel / __head / __label / __body
     .wa-task-card                    -> .asgt-card
     .wa-task-type|due                -> .asgt-badge
     .wa-task-assignee                -> .asgt-avatar--sm
     .wa-task-complete-btn            -> .asgt-btn--sm.asgt-btn--outline
     .wa-modal-actions / .wa-btn-*    -> .asgt-modal__foot / .asgt-btn variants
     .wa-filter                       -> .asgt-select--sm
     .wa-refresh-btn                  -> .asgt-btn--sm.asgt-btn--outline
     .wa-empty-col                    -> .asgt-faint

   .wa-filter is the one to note: it never had a rule of its own inside the
   Tasks header. It was inheriting everything from the bare `input, textarea,
   select` selector in asgt-design-system.css, so deleting that rule without
   this markup would have left those filters as unstyled browser controls on a
   near-black canvas.

   .asgt-feed__time is mono but does not set font-variant-numeric: tabular-nums,
   so the activity timestamps compose it with .asgt-data. Raised with the Design
   System chat; drop the extra class once the component carries it.

   What stays: the click affordance on a task card, which the system leaves
   alone because .asgt-card is a surface, not a control. */
.wa-task-card { cursor: pointer; }


/* ============================================================
   Mobile bug fixes — tab overflow + Safari safe area
   ============================================================ */
@media (max-width: 768px) {

  .wa-card-list,
  .wa-activity-wrap,
  .wa-tasks-wrap,
  .wa-kpi-grid,
  .workspace-body {
    padding-bottom: calc(80px + env(safe-area-inset-bottom, 20px));
  }
}

/* ============================================================
   Session 11A / 11A.6 — pipeline row urgency
   ============================================================
   What the design system does not have: a row tinted by how late its next
   action is. Everything else that lived here migrated with the markup —
   .wa-queue-btn and .wa-columns-btn are .asgt-toggle (pressed state now comes
   from aria-pressed, not a second class), .wa-priority-badge is .asgt-badge,
   and .wa-detail-panel / .wa-detail-label are .asgt-spec chips.

   Colours are the system's status tokens rather than the raw hexes that were
   here: they follow the surface, so these bars stay legible when this screen
   moves from data-surface="dark" to "crisp". */
.wa-row-overdue      { border-left: 3px solid var(--asgt-out); }
.wa-row-due-today    { border-left: 3px solid var(--asgt-low); }
.wa-row-due-tomorrow { border-left: 3px solid var(--asgt-low-bg); }
.wa-row-faded        { opacity: 0.5; }

/* The age cell is a .num cell now, so these no longer compound with a
   .wa-col-age class that the row builder stopped emitting. */
.wa-age-aging { color: var(--asgt-low); }
.wa-age-old   { color: var(--asgt-out); font-weight: 600; }

.wa-col-hidden { display: none !important; }

/* Van sales daily log — click affordance on a row, same reason as the WhatsApp
   pipeline: .asgt-table is a surface, not a control, so the system leaves the
   cursor alone and the screen says so itself. */
.vs-daily .asgt-table tbody tr { cursor: pointer; }

/* Sign out sits at the foot of the rail. .asgt-rail is a flex column, so an
   auto top margin pushes this item — and only this item — to the bottom. */
.outer-rail .rail-foot { margin-block-start: auto; }

/* The rail's brand mark is the product logo, not a letter tile.
   .asgt-sidebar__mark--image is the design system's own modifier for this:
   it drops the gradient and the padding so artwork fills the tile. */
.outer-rail-logo img { inline-size: 100%; block-size: 100%; object-fit: contain; display: block; }

/* The chat thread scrolls inside the panel rather than growing the page, so the
   message box stays put while the history moves. */
#ai-thread { max-block-size: 52vh; overflow-y: auto; }

/* ========================================================= KNOWLEDGE UNIVERSE
 * The notes drawn as a star field over a galaxy.
 *
 * This screen deliberately steps outside the token palette for its BACKDROP
 * only -- a photograph has no tokens, and neither does the light coming off it.
 * Every control, every label and every piece of chrome on top still uses the
 * design system. Nothing here redefines an .asgt-* class; the prefix is os-.
 *
 * The galaxy is a file at assets/img/galaxy.jpg. If it is absent the layered
 * gradients below still draw a credible core and the screen works -- a missing
 * photograph must not leave a broken page.
 */
.os-uni {
  position: relative;
  min-block-size: 620px;
  border-radius: var(--asgt-r-lg);
  overflow: hidden;
  background-color: #05060d;
  background-image:
    radial-gradient(ellipse 42% 34% at 50% 50%,
      rgba(255, 214, 158, 0.30), transparent 70%),
    radial-gradient(ellipse 70% 55% at 50% 50%,
      rgba(126, 106, 214, 0.22), transparent 72%),
    radial-gradient(ellipse 95% 80% at 50% 50%,
      rgba(58, 44, 122, 0.16), transparent 78%);
  isolation: isolate;
}
.os-uni__photo {
  position: absolute; inset: -26%;
  background: center / cover no-repeat;
  opacity: 0;
  transition: opacity 240ms linear;
  will-change: transform, opacity;
  z-index: 0;
}
.os-uni__photo.is-on { opacity: 0.88; }

/* The photograph is the backdrop, not the subject. Without this the bulge is
 * brighter than anything drawn on top of it and the notes disappear into it --
 * which is exactly what the first render did. */
.os-uni::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    /* a light knock-down over the bulge only, so a star in front of it still
       reads -- not the heavy scrim that used to flatten the whole photograph */
    radial-gradient(ellipse 20% 26% at 40% 50%, rgba(4,5,12,0.42), transparent 76%),
    /* and darkness at the edges, which is where the chrome sits */
    radial-gradient(ellipse 82% 86% at 42% 50%, transparent 42%, rgba(4,5,12,0.62) 100%);
}

/* One canvas draws everything that moves: three particle depths, the nebula,
 * the links, the stars and the pulses along them. It is completely inert --
 * the DOM layer above owns every pointer and every focus ring, so a decorative
 * star can never swallow a click. */
.os-uni__gl { position: absolute; inset: 0; z-index: 2; pointer-events: none; }

/* Resolves a design-system token to a real colour for the canvas, which cannot
 * read a custom property. Never seen. */
.os-uni__probe { position: absolute; inline-size: 0; block-size: 0; opacity: 0; }

/* The interactive sky. One real button per note, placed from the camera: these
 * are the tab stops, the accessible names and the click targets, and they are
 * the only things here that take a pointer.
 *
 * direction: ltr is deliberate. This layer is a coordinate space, not text --
 * a star at x=120 is 120px from the left of the map in every language. The
 * chrome around it still uses logical properties and still flips. */
.os-uni__hits {
  position: absolute; inset: 0; z-index: 3; direction: ltr;
}
/* The sky can be dragged and the wheel zooms it, so it says so. */
.os-uni { cursor: grab; }
.os-uni.is-dragging { cursor: grabbing; }
.os-uni.is-dragging .os-uni__label { opacity: 0.55; }
.os-uni__hit {
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  padding: 0; border: 0; background: none; cursor: pointer; border-radius: 50%;
}
.os-uni__hit:focus-visible { outline: 2px solid var(--asgt-accent); outline-offset: 3px; }

.os-uni__labels { position: absolute; inset: 0; pointer-events: none; }
.os-uni__label {
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  font-family: var(--asgt-font-ui); font-size: 13px; font-weight: 600;
  line-height: 18px; white-space: nowrap;
  color: rgba(255,255,255,0.88);
  text-shadow: 0 1px 3px rgba(0,0,0,0.95), 0 0 12px rgba(0,0,0,0.85);
  transition: color 180ms var(--asgt-ease);
}
.os-uni__label.is-hot { color: #fff; }
.os-uni__label.is-on  { color: #fff; font-weight: 700; }

.os-uni__blank {
  position: absolute; inset: 0; display: grid; place-items: center;
  padding: var(--asgt-s-6); z-index: 3;
}

.os-uni__core {
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  transform: translate(-50%, -50%);
  text-align: center; z-index: 2; pointer-events: none;
  direction: ltr;   /* placed in map coordinates, like the stars */
}
.os-uni__core {
  padding: var(--asgt-s-4) var(--asgt-s-8);
  border-radius: 50%;
  /* The caption sits ON the bulge, the brightest part of the photograph. The
     contrast is carried by the type's own shadow, not by a scrim -- a scrim
     dark enough to do the job reads as a hole burnt in the galaxy. */
  background: radial-gradient(ellipse at center,
    rgba(0,0,0,0.40), rgba(0,0,0,0.16) 55%, transparent 78%);
}
.os-uni__core b {
  display: block; font-family: var(--asgt-font-ui);
  font-size: 26px; font-weight: 300; letter-spacing: 0.42em;
  color: #fff; text-indent: 0.42em;
  text-shadow: 0 1px 2px rgba(0,0,0,0.95), 0 0 10px rgba(0,0,0,0.9),
               0 0 26px rgba(0,0,0,0.75);
}
.os-uni__core span {
  font-size: var(--asgt-t-xs); letter-spacing: 0.16em;
  color: rgba(255,255,255,0.86);
  text-shadow: 0 1px 2px rgba(0,0,0,0.95), 0 0 10px rgba(0,0,0,0.9),
               0 0 22px rgba(0,0,0,0.7);
}

/* The chrome floating over the photograph. Glass rather than a solid panel, so
 * the galaxy still reads behind it. */
.os-uni__panel {
  position: absolute; inset-inline-end: var(--asgt-s-5);
  /* Below the floating header, not underneath it. The header owns the top of
     the frame and is drawn above everything; a panel starting at the top edge
     came out with the counts printed through its title. */
  inset-block-start: calc(var(--os-kn-head, 212px) + var(--asgt-s-3));
  inline-size: 330px; max-inline-size: calc(100% - var(--asgt-s-8));
  z-index: 4;
  background: linear-gradient(rgba(255,255,255,0.09), rgba(255,255,255,0.045)),
              rgba(10, 12, 24, 0.55);
  backdrop-filter: blur(26px) saturate(160%);
  -webkit-backdrop-filter: blur(26px) saturate(160%);
  border: 1px solid rgba(255,255,255,0.16);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.22), 0 18px 50px rgba(0,0,0,0.5);
  border-radius: var(--asgt-r-lg);
  padding: var(--asgt-s-5);
  /* Bounded, and it scrolls: a long note on a short window must not run off
     the bottom of the galaxy. */
  max-block-size: calc(100% - var(--os-kn-head, 212px) - var(--asgt-s-10));
  overflow: auto;
  animation: os-uni-panel 320ms var(--asgt-ease) both;
}
@keyframes os-uni-panel {
  from { opacity: 0; transform: translateX(10px); }
  to   { opacity: 1; transform: none; }
}
.os-uni__panel h3 { margin: 0; font-size: var(--asgt-t-lg); color: #fff; }
.os-uni__eyebrow {
  display: flex; align-items: center; gap: var(--asgt-s-2);
  font-size: var(--asgt-t-2xs); font-weight: 800; letter-spacing: 0.12em;
  text-transform: uppercase; color: rgba(255,255,255,0.55);
  margin-block-end: var(--asgt-s-3);
}
.os-uni__link-row {
  display: flex; align-items: center; gap: var(--asgt-s-2);
  inline-size: 100%; padding: var(--asgt-s-2) 0;
  background: none; border: 0; cursor: pointer;
  color: rgba(255,255,255,0.86); font-size: var(--asgt-t-sm);
  font-family: var(--asgt-font-ui); text-align: start;
  border-block-end: 1px solid rgba(255,255,255,0.06);
}
.os-uni__link-row:hover { color: #fff; }
.os-uni__link-row i {
  inline-size: 8px; block-size: 8px; border-radius: 50%;
  background: currentColor; flex: none;
}
.os-uni__link-row svg { margin-inline-start: auto; opacity: 0.5; }

/* Floating, not a band. The gradient block along the bottom was most of what
   made the screen look boxed in. */
.os-uni__foot {
  position: absolute; inset-inline: 0; inset-block-end: 0; z-index: 4;
  display: flex; align-items: center; gap: var(--asgt-s-4);
  flex-wrap: wrap;
  padding: var(--asgt-s-4) var(--asgt-s-8) var(--asgt-s-5);
  font-size: var(--asgt-t-2xs); color: rgba(255,255,255,0.62);
  letter-spacing: 0.05em;
  pointer-events: none;
}
.os-uni__foot > * { pointer-events: auto; }
.os-uni__foot:hover { color: rgba(255,255,255,0.92); }
.os-uni__key { display: inline-flex; align-items: center; gap: var(--asgt-s-2); }
.os-uni__key i {
  inline-size: 7px; block-size: 7px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 6px currentColor;
}
.os-uni__hint { margin-inline-start: auto; }
/* Motion is a control, not a preference buried in the operating system. It
   starts off for anyone who has asked their machine to reduce motion. */
.os-uni__motion {
  block-size: 26px; padding-inline: var(--asgt-s-3);
  border: 1px solid var(--os-glass-edge); border-radius: var(--asgt-r-pill);
  background: var(--os-glass-bg);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  box-shadow: var(--os-glass-lip), var(--os-glass-cast);
  color: rgba(255,255,255,0.85); cursor: pointer;
  font-family: var(--asgt-font-ui); font-size: var(--asgt-t-2xs);
}
.os-uni__motion:hover { background: rgba(255,255,255,0.14); color: #fff; }
.os-uni__motion[aria-pressed="false"] { opacity: 0.6; }
.os-uni__zoom {
  display: inline-flex; align-items: center; gap: var(--asgt-s-1);
  background: var(--os-glass-bg);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  border: 1px solid var(--os-glass-edge);
  box-shadow: var(--os-glass-lip), var(--os-glass-cast);
  border-radius: var(--asgt-r-pill); padding: 3px;
}
.os-uni__zoom button {
  min-inline-size: 30px; block-size: 26px; border: 0; cursor: pointer;
  background: none; color: rgba(255,255,255,0.8);
  border-radius: var(--asgt-r-pill); font-family: var(--asgt-font-data);
  font-size: var(--asgt-t-2xs);
}
.os-uni__zoom button:hover { background: rgba(255,255,255,0.12); color: #fff; }

@media (prefers-reduced-motion: reduce) {
  .os-uni__panel, .os-uni__photo { animation: none; transition: none; }
  .os-uni__label { transition: none; }
}

.os-uni__close {
  margin-inline-start: auto; inline-size: 26px; block-size: 26px;
  border: 1px solid rgba(255,255,255,0.14); border-radius: 50%;
  background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.85);
  font-size: 17px; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
  transition: background var(--asgt-dur) var(--asgt-ease);
}
.os-uni__close:hover { background: rgba(255,255,255,0.18); color: #fff; }

/* ================================================= KNOWLEDGE — page composition
 * Full bleed. The galaxy is the page: it fills the workspace edge to edge and
 * the header floats on top of it. A card with margins around a photograph of
 * space reads as a widget, not as a view.
 */
.os-kn { position: absolute; inset: 0; overflow: hidden; }

/* The universe fills it, with no radius and no border of its own. */
.os-kn > .os-uni, #os-mem-body.os-uni {
  position: absolute; inset: 0;
  block-size: auto; min-block-size: 0;
  border-radius: 0;
}
/* List view still scrolls, inside the same frame. */
.os-kn > #os-mem-body:not(.os-uni) {
  position: absolute; inset: 168px 0 0; overflow: auto;
  padding: var(--asgt-s-5) var(--asgt-s-8);
}

.os-kn__head {
  position: absolute; inset-inline: 0; inset-block-start: 0; z-index: 6;
  padding: var(--asgt-s-5) var(--asgt-s-8) var(--asgt-s-8);
  display: flex; flex-direction: column; gap: var(--asgt-s-4);
  /* The header needs a ground to be read against, and the galaxy behind it
     must still be visible. A gradient does both; a panel does neither. */
  background: linear-gradient(to bottom,
    rgba(4,5,12,0.92) 12%, rgba(4,5,12,0.62) 52%, transparent);
  /* Only the controls take the pointer -- the rest of this band is sky. */
  pointer-events: none;
}
.os-kn__head > * { pointer-events: auto; }

/* One glass recipe for everything that floats over the galaxy, so the search,
   the zoom, the motion switch and the reader all read as the same material.
   Not an .asgt-* class: the prefix is os-, and nothing in the design system is
   redefined. */
.os-kn__head, .os-uni__foot {
  --os-glass-bg: rgba(255,255,255,0.07);
  --os-glass-edge: rgba(255,255,255,0.16);
  --os-glass-lip: inset 0 1px 0 rgba(255,255,255,0.20);
  --os-glass-cast: 0 10px 34px rgba(0,0,0,0.42);
}

.os-kn__bar { display: flex; align-items: center; gap: var(--asgt-s-6); flex-wrap: wrap; }
.os-kn__crumb {
  display: block;
  font-size: var(--asgt-t-2xs); font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: rgba(255,255,255,0.46);
  margin-block-end: var(--asgt-s-1);
}
.os-kn__title h1 {
  margin: 0; font-size: var(--asgt-t-2xl); font-weight: 800; line-height: 1;
  color: #fff;
}
.os-kn__title > span:last-child {
  font-size: var(--asgt-t-sm); color: rgba(255,255,255,0.58);
}

/* Counts read as figures, not as a sentence: the number carries, the word
   labels it. Same shape as the .asgt-figure well, over a photograph. */
.os-kn__counts {
  display: flex; align-items: baseline; gap: var(--asgt-s-8);
  margin-inline-start: auto;
  /* Baseline-aligned figures, and the button beside them centred on the block
     rather than sitting on the labels' baseline. */
  padding-inline-end: var(--asgt-s-2);
}
.os-kn__count { display: flex; flex-direction: column; line-height: 1.1; }
.os-kn__count b {
  font-family: var(--asgt-font-data); font-size: var(--asgt-t-2xl);
  font-weight: 300; color: #fff; font-variant-numeric: tabular-nums;
}
.os-kn__count i {
  font-style: normal; font-size: var(--asgt-t-2xs); letter-spacing: 0.1em;
  color: rgba(255,255,255,0.52);
}
/* The two that need doing something about are coloured; the rest are facts. */
.os-kn__count.is-bad b     { color: var(--asgt-out); }
.os-kn__count.is-pending b { color: var(--asgt-low); }

.os-kn__tools { display: flex; align-items: center; gap: var(--asgt-s-4); }
.os-kn__tools .asgt-segmented { margin-inline-start: auto; }

/* The search field. Glass pill with the shortcut on the end, the way the
   mockup has it. */
.os-kn__search {
  display: flex; align-items: center; gap: var(--asgt-s-3);
  flex: 0 1 620px; min-inline-size: 0;
  block-size: 42px; padding-inline: var(--asgt-s-4);
  border-radius: var(--asgt-r-pill);
  background: var(--os-glass-bg);
  border: 1px solid var(--os-glass-edge);
  box-shadow: var(--os-glass-lip), var(--os-glass-cast);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  color: rgba(255,255,255,0.62);
  transition: border-color var(--asgt-dur) var(--asgt-ease),
              background var(--asgt-dur) var(--asgt-ease);
}
.os-kn__search:focus-within {
  border-color: rgba(255,255,255,0.34);
  background: rgba(255,255,255,0.11);
}
.os-kn__search svg { flex: none; opacity: 0.7; }
.os-kn__search-input {
  flex: 1; min-inline-size: 0;
  background: none; border: 0; outline: none;
  font-family: var(--asgt-font-ui); font-size: var(--asgt-t-sm);
  color: #fff;
}
.os-kn__search-input::placeholder { color: rgba(255,255,255,0.45); }
.os-kn__kbd {
  flex: none; padding: 2px 7px; border-radius: var(--asgt-r-sm);
  background: rgba(255,255,255,0.10);
  border: 1px solid rgba(255,255,255,0.14);
  font-family: var(--asgt-font-data); font-size: var(--asgt-t-2xs);
  color: rgba(255,255,255,0.62);
}

#os-mem-queue:not(:empty) {
  max-inline-size: 460px;
  max-block-size: 46vh; overflow: auto;
}

.os-uni__panel { inline-size: 340px; }

@media (max-width: 720px) {
  /* Bottom sheet on a small screen: a 320px panel over a phone-width galaxy
     leaves no galaxy. */
  .os-uni__panel {
    inset-inline: 0; inset-block: auto 0; inline-size: auto;
    border-radius: var(--asgt-r-lg) var(--asgt-r-lg) 0 0;
    max-block-size: 62%; overflow: auto;
  }
  .os-kn__counts { inline-size: 100%; margin-inline-start: 0; }
}

/* ================================================= THE SHELL BELOW 900px
 * asgt-ui.v2.css turns .asgt-rail and .asgt-sidebar into horizontal bars at
 * this width, but the shell's own .outer-rail stayed position:fixed and 100vh
 * tall -- so on a phone the rail covered the whole screen and nothing under
 * it could be reached. Measured at 400px on 11 Sep 2026: the rail was the
 * page. The rail becomes a bar across the top, in flow, and the workspace
 * takes the rest. Found building the AI organisation screen, which is the
 * first workspace anyone has opened on a phone.
 */
@media (max-width: 900px) {
  body.v2-shell-active .shell-root { flex-direction: column; }
  .outer-rail {
    position: sticky; top: 0; left: auto; height: auto; width: 100%;
    flex-direction: row; align-items: center;
    padding: 8px 12px; gap: 4px;
    border-right: 0; border-bottom: 1px solid var(--border-v2);
    overflow-x: auto; z-index: 100;
  }
  .outer-rail .rail-foot { margin-block-start: 0; margin-inline-start: auto; }
  .outer-rail-slot.active::before { display: none; }
  .outer-rail-divider { display: none; }
  .inner-sidebar { position: static; width: 100%; height: auto; left: auto; }
  .shell-workspace, .shell-workspace.fullwidth {
    margin-left: 0; min-height: 0; flex: 1; min-block-size: 0;
  }
  .workspace-body { min-height: 0; }
}
