"""Vastian Unified App — copilot-first shell with always-visible chat.

Layout: Three-column (sidebar ~60px, context flexible, chat ~400px always-visible).
Hash routing: #/dashboard, #/boards, #/sessions, #/personas, #/scenarios, #/plans,
              #/metrics, #/meetings, #/councils, #/documents, #/settings, #/backup.

Content loaded via fetch-and-inject (no iframes). Theme from config/ui-preferences.yaml.
"""

from __future__ import annotations

from pathlib import Path


def render_shell(user_tier: str = "open", project_root: Path | None = None) -> str:
    """Render the unified app shell HTML. Theme from config (theme, theme_preset, density)."""
    from vastian.ui_theme import get_theme_css

    theme_css = get_theme_css(project_root)
    return _SHELL_HTML.replace("__THEME_CSS__", theme_css).replace("__USER_TIER__", user_tier)


_SHELL_HTML = r"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate, max-age=0">
<title>Vastian Network</title>
<link rel="icon" type="image/png" href="/static/vastian-icon.png">
<style>
  __THEME_CSS__
  :root {
    --ab-w: 44px;
    --sp-w: 240px;
    --chat-w: 400px;
    --radius: 8px;
    --green: #3fb950;
    --yellow: #d29922;
    --red: #f85149;
  }
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body { height: 100%; overflow: hidden; }
  body {
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    background: var(--bg);
    color: var(--text);
  }

  /* ── App layout ─────────────────────────────────────── */
  #app {
    display: flex;
    height: 100vh;
    overflow: hidden;
  }

  /* ── Sidebar container (Activity Bar + Side Panel) ─── */
  .sidebar-container {
    display: flex;
    flex-shrink: 0;
    height: 100%;
    z-index: 100;
  }

  /* Activity Bar — always visible icon rail */
  .activity-bar {
    width: var(--ab-w);
    min-width: var(--ab-w);
    background: var(--card);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 6px 0;
  }
  .ab-btn {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--muted);
    font-size: 1.15em;
    cursor: pointer;
    border-radius: 6px;
    margin: 2px 0;
    position: relative;
    transition: color 0.15s, background 0.15s;
  }
  .ab-btn:hover { color: var(--text); background: rgba(88,166,255,0.08); }
  .ab-btn.active { color: var(--accent); }
  .ab-btn.active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 2px;
    background: var(--accent);
    border-radius: 0 2px 2px 0;
  }
  .ab-spacer { flex: 1; }
  .ab-badge {
    position: absolute;
    top: 2px;
    right: 2px;
    background: var(--accent);
    color: #fff;
    font-size: 0.5em;
    border-radius: 50%;
    min-width: 14px;
    height: 14px;
    display: none;
    align-items: center;
    justify-content: center;
    font-weight: 700;
  }

  /* Side Panel — pinnable, content area */
  .side-panel {
    width: 0;
    min-width: 0;
    overflow: hidden;
    background: var(--card);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    transition: width 0.18s ease, min-width 0.18s ease;
  }
  .side-panel.open {
    width: var(--sp-w);
    min-width: var(--sp-w);
  }
  .side-panel.overlay {
    position: absolute;
    left: var(--ab-w);
    top: 0;
    bottom: 0;
    z-index: 99;
    box-shadow: 4px 0 16px rgba(0,0,0,0.25);
  }
  .sp-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 12px 6px;
    flex-shrink: 0;
  }
  .sp-title {
    font-weight: 700;
    font-size: 0.7em;
    color: var(--muted);
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
  .sp-pin {
    background: none;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 0.9em;
    padding: 2px 4px;
    border-radius: 3px;
    transition: color 0.15s;
  }
  .sp-pin:hover { color: var(--accent); }
  .sp-pin.pinned { color: var(--accent); }
  .sp-content {
    flex: 1;
    overflow-y: auto;
    padding: 0 4px 8px;
    font-size: 0.84em;
  }

  /* Side panel nav items (Pages view) */
  .sp-nav-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    color: var(--muted);
    text-decoration: none;
    cursor: pointer;
    border-radius: 4px;
    font-size: 0.92em;
    transition: color 0.12s, background 0.12s;
    white-space: nowrap;
  }
  .sp-nav-item:hover { color: var(--accent); background: rgba(88,166,255,0.08); }
  .sp-nav-item.active { color: var(--accent); background: rgba(88,166,255,0.12); }
  .sp-nav-item.nav-locked { opacity: 0.35; pointer-events: none; }
  .sp-nav-icon { font-size: 1.1em; min-width: 20px; text-align: center; }
  .sp-nav-badge {
    background: var(--accent);
    color: #fff;
    font-size: 0.6em;
    border-radius: 50%;
    min-width: 15px;
    height: 15px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    font-weight: 700;
  }
  .sp-sep { height: 1px; background: var(--border); margin: 4px 8px; }
  .sp-section-label {
    font-size: 0.68em;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 8px 10px 3px;
    font-weight: 600;
  }

  /* Explorer tree items */
  .sp-tree-agent {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    cursor: pointer;
    color: var(--text);
    font-weight: 600;
    font-size: 0.88em;
    border-radius: 4px;
    user-select: none;
  }
  .sp-tree-agent:hover { background: rgba(88,166,255,0.06); }
  .sp-tree-chevron {
    font-size: 0.65em;
    transition: transform 0.15s;
    color: var(--muted);
  }
  .sp-tree-agent.collapsed .sp-tree-chevron { transform: rotate(-90deg); }
  .sp-tree-files { padding-left: 18px; }
  .sp-tree-agent.collapsed + .sp-tree-files { display: none; }
  .sp-tree-file {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    cursor: pointer;
    color: var(--muted);
    border-radius: 3px;
    font-size: 0.9em;
    transition: color 0.12s, background 0.12s;
  }
  .sp-tree-file:hover { color: var(--text); background: rgba(88,166,255,0.06); }
  .sp-tree-file .sp-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  .sp-tree-file .sp-dot.exists { background: var(--green); }
  .sp-tree-file .sp-dot.missing { border: 1px solid var(--muted); }

  /* Command list items */
  .sp-cmd-group {
    font-size: 0.68em;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 8px 10px 3px;
    font-weight: 600;
  }
  .sp-cmd-item {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 5px 10px;
    cursor: pointer;
    color: var(--muted);
    border-radius: 4px;
    font-size: 0.88em;
    font-family: 'Consolas', 'Menlo', monospace;
    transition: color 0.12s, background 0.12s;
  }
  .sp-cmd-item:hover { color: var(--accent); background: rgba(88,166,255,0.06); }
  .sp-cmd-name {
    flex-shrink: 0;
    white-space: nowrap;
    color: var(--text);
  }
  .sp-cmd-desc {
    font-family: 'Segoe UI', system-ui, sans-serif;
    font-size: 0.8em;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }

  /* ── Context panel ──────────────────────────────────── */
  .context-panel {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--bg);
  }

  /* ── Tab bar (VS Code style) ────────────────────────── */
  .tab-bar {
    display: flex;
    background: var(--card);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    flex-shrink: 0;
    min-height: 34px;
    scrollbar-width: none;
  }
  .tab-bar::-webkit-scrollbar { display: none; }
  .vtab {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    font-size: 0.8em;
    cursor: pointer;
    border-right: 1px solid var(--border);
    white-space: nowrap;
    color: var(--muted);
    background: var(--card);
    flex-shrink: 0;
    position: relative;
    user-select: none;
  }
  .vtab:hover { color: var(--text); }
  .vtab.active {
    color: var(--text);
    background: var(--bg);
  }
  .vtab.active::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--accent);
  }
  .vtab-icon { font-size: 1em; flex-shrink: 0; }
  .vtab-label { max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
  .vtab-close {
    background: none;
    border: none;
    color: var(--muted);
    font-size: 0.85em;
    cursor: pointer;
    padding: 0 2px;
    border-radius: 3px;
    line-height: 1;
    opacity: 0;
    transition: opacity 0.12s;
  }
  .vtab:hover .vtab-close { opacity: 0.7; }
  .vtab-close:hover { opacity: 1; color: var(--text); background: rgba(255,255,255,0.1); }
  .vtab.pinned .vtab-close { display: none; }
  .vtab.pinned .vtab-pin-dot {
    width: 5px; height: 5px; border-radius: 50%;
    background: var(--accent); flex-shrink: 0;
  }
  .vtab .vtab-pin-dot { display: none; }
  .vtab.pinned .vtab-pin-dot { display: block; }

  /* Tab panes (hidden/shown for caching) */
  .tab-pane {
    flex: 1;
    overflow-y: auto;
    padding: 20px 24px;
    display: none;
  }
  .tab-pane.active { display: block; }
  .tab-pane.fetched-page { padding: 0; }
  .tab-pane.fetched-page .injected-body { padding: 24px; }

  /* Breadcrumb for document tabs */
  .tab-breadcrumb {
    padding: 4px 24px;
    font-size: 0.72em;
    color: var(--muted);
    border-bottom: 1px solid var(--border);
    display: none;
    flex-shrink: 0;
  }
  .tab-breadcrumb.show { display: block; }

  /* Keep page-header for backward compat but it's hidden by default */
  .page-header {
    padding: 16px 24px 12px;
    border-bottom: 1px solid var(--border);
    display: none;
  }
  .page-header h1 { font-size: 1.2em; color: var(--accent); margin-bottom: 2px; }
  .page-header p { font-size: 0.85em; color: var(--muted); }
  .page-content {
    flex: 1;
    overflow-y: auto;
    padding: 20px 24px;
  }
  .page-content.fetched-page { padding: 0; }
  .page-content.fetched-page .injected-body { padding: 24px; }

  /* ── Chat panel — Cursor-style compact layout ──────── */
  .chat-panel {
    width: var(--chat-w);
    min-width: var(--chat-w);
    background: var(--card);
    border-left: 1px solid var(--border);
    display: flex;
    flex-direction: column;
  }
  .chat-header {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .chat-header h3 { font-size: 0.88em; color: var(--accent); }
  .chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 8px 10px;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .chat-msg {
    padding: 4px 8px;
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
    font-size: 0.84em;
    width: 100%;
    border-left: 2px solid transparent;
  }
  .chat-msg.user {
    border-left-color: var(--muted);
    background: none;
  }
  .chat-msg.agent {
    border-left-color: var(--accent);
    background: none;
  }
  .chat-msg.system {
    border-left-color: var(--yellow);
    background: none;
    font-style: italic;
  }
  .chat-msg .msg-header {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin-bottom: 1px;
  }
  .chat-msg .agent-name {
    font-weight: 600;
    font-size: 0.78em;
  }
  .chat-msg .msg-route {
    font-size: 0.7em;
    color: var(--muted);
  }
  .chat-msg .msg-time {
    font-size: 0.68em;
    color: var(--muted);
    margin-left: auto;
  }
  .chat-msg .msg-body { font-size: 0.92em; }
  .chat-msg .msg-body.collapsed { max-height: 80px; overflow: hidden; }
  .chat-msg .msg-toggle {
    font-size: 0.72em;
    color: var(--accent);
    cursor: pointer;
    margin-top: 2px;
    display: none;
  }
  .chat-msg .msg-toggle.visible { display: inline-block; }
  .chat-msg code {
    background: rgba(88,166,255,0.1);
    padding: 1px 4px;
    border-radius: 3px;
    font-size: 0.9em;
  }
  .chat-msg pre {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 6px 8px;
    margin: 4px 0;
    overflow-x: auto;
    font-size: 0.85em;
  }
  .chat-typing {
    color: var(--muted);
    font-size: 0.78em;
    font-style: italic;
    padding: 2px 10px;
    display: none;
  }
  .chat-typing.show { display: block; }

  /* Chat resize handle */
  .chat-resize-handle {
    width: 4px;
    cursor: col-resize;
    background: transparent;
    transition: background 0.15s;
    flex-shrink: 0;
  }
  .chat-resize-handle:hover, .chat-resize-handle.active { background: var(--accent); }

  /* Chat input */
  .chat-input {
    padding: 8px 10px;
    border-top: 1px solid var(--border);
  }
  .chat-input textarea {
    width: 100%;
    min-height: 40px;
    max-height: 100px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 6px 10px;
    color: var(--text);
    font: inherit;
    font-size: 0.84em;
    resize: none;
    line-height: 1.35;
  }
  .chat-input textarea:focus { outline: none; border-color: var(--accent); }
  .chat-controls {
    display: flex;
    gap: 6px;
    align-items: center;
    margin-top: 4px;
  }
  .agent-selector {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1;
    min-width: 0;
  }
  .agent-chief-btn {
    background: rgba(88,166,255,0.12);
    color: var(--accent);
    border: 1px solid rgba(88,166,255,0.3);
    padding: 3px 8px;
    border-radius: 4px;
    font-size: 0.78em;
    cursor: pointer;
    font-family: inherit;
    white-space: nowrap;
    font-weight: 600;
  }
  .agent-chief-btn:hover { background: rgba(88,166,255,0.2); }
  .agent-chief-btn.active { background: rgba(88,166,255,0.25); border-color: var(--accent); }
  .chat-controls select {
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 3px 6px;
    border-radius: 4px;
    font-size: 0.78em;
    flex: 1;
    min-width: 0;
  }
  .chat-controls button#chatSend {
    background: var(--accent);
    color: #fff;
    border: none;
    padding: 4px 12px;
    border-radius: 5px;
    font-size: 0.8em;
    cursor: pointer;
    font-family: inherit;
  }
  .chat-controls button#chatSend:hover { opacity: 0.9; }
  .chat-controls button#chatSend:disabled { opacity: 0.5; cursor: not-allowed; }

  /* ── Chat mode toggle ─────────────────────────────── */
  .chat-mode-select {
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 3px 6px;
    border-radius: 4px;
    font-size: 0.74em;
    font-family: inherit;
    cursor: pointer;
    min-width: 0;
    max-width: 110px;
  }
  .chat-mode-select:focus { outline: none; border-color: var(--accent); }
  .chat-mode-indicator {
    font-size: 0.72em;
    color: var(--muted);
    padding: 2px 10px;
    font-style: italic;
    display: none;
  }
  .chat-mode-indicator.show { display: block; }

  /* ── Structured Question Card ─────────────────────── */
  .sq-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px 14px;
    margin: 6px 0 10px 0;
    max-width: 100%;
  }
  .sq-context {
    font-size: 0.78em;
    color: var(--muted);
    margin-bottom: 8px;
    line-height: 1.4;
  }
  .sq-question {
    font-size: 0.88em;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 10px;
    line-height: 1.4;
  }
  .sq-question .sq-counter {
    font-weight: 400;
    color: var(--muted);
    font-size: 0.85em;
    margin-left: 6px;
  }
  .sq-options {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 10px;
  }
  .sq-option {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
    font-size: 0.84em;
    line-height: 1.4;
  }
  .sq-option:hover {
    border-color: var(--accent);
    background: rgba(88,166,255,0.06);
  }
  .sq-option.selected {
    border-color: var(--accent);
    background: rgba(88,166,255,0.12);
  }
  .sq-option .sq-letter {
    font-weight: 700;
    color: var(--accent);
    min-width: 16px;
    flex-shrink: 0;
  }
  .sq-option .sq-label {
    color: var(--text);
    flex: 1;
  }
  .sq-other-row {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.84em;
    transition: border-color 0.15s;
  }
  .sq-other-row:hover { border-color: var(--accent); }
  .sq-other-row.expanded {
    border-color: var(--accent);
    flex-direction: column;
    gap: 6px;
  }
  .sq-other-input {
    display: none;
    width: 100%;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 6px 8px;
    color: var(--text);
    font: inherit;
    font-size: 0.92em;
    resize: none;
    min-height: 32px;
  }
  .sq-other-row.expanded .sq-other-input { display: block; }
  .sq-free-entry {
    width: 100%;
  }
  .sq-free-input {
    width: 100%;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 10px 12px;
    color: var(--text);
    font: inherit;
    font-size: 0.9em;
    resize: vertical;
    min-height: 48px;
    line-height: 1.5;
    transition: border-color 0.15s;
  }
  .sq-free-input:focus {
    border-color: var(--accent);
    outline: none;
  }
  .sq-free-input::placeholder {
    color: var(--muted);
  }
  .sq-actions {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 10px;
    margin-top: 4px;
  }
  .sq-skip {
    font-size: 0.78em;
    color: var(--muted);
    cursor: pointer;
    text-decoration: none;
  }
  .sq-skip:hover { color: var(--text); }
  .sq-continue {
    background: var(--accent);
    color: #fff;
    border: none;
    padding: 5px 16px;
    border-radius: 5px;
    font-size: 0.8em;
    cursor: pointer;
    font-family: inherit;
    font-weight: 600;
  }
  .sq-continue:hover { opacity: 0.9; }
  .sq-continue:disabled { opacity: 0.5; cursor: not-allowed; }

  /* ── Document bar (Cursor-style expandable) ───────── */
  .doc-bar {
    border-top: 1px solid var(--border);
    background: var(--bg);
    position: relative;
  }
  .doc-bar-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    font-size: 0.78em;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
  }
  .doc-bar-toggle:hover { color: var(--text); background: rgba(88,166,255,0.06); }
  .doc-bar-chevron {
    display: inline-block;
    transition: transform 0.15s;
    font-size: 0.9em;
  }
  .doc-bar.expanded .doc-bar-chevron { transform: rotate(90deg); }
  .doc-bar-list {
    display: none;
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    max-height: 220px;
    overflow-y: auto;
    background: var(--card);
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: var(--radius) var(--radius) 0 0;
    padding: 4px 0;
    z-index: 15;
  }
  .doc-bar.expanded .doc-bar-list { display: block; }
  .doc-bar-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 12px;
    font-size: 0.8em;
    color: var(--text);
    cursor: pointer;
  }
  .doc-bar-item:hover { background: rgba(88,166,255,0.1); }
  .doc-bar-item .doc-icon { color: var(--muted); font-size: 0.9em; flex-shrink: 0; }

  /* Agent chat message color tints per tier */
  .chat-msg.agent[data-tier="chief"] {
    border-left-color: var(--accent);
    background: rgba(88,166,255,0.06);
  }
  .chat-msg.agent[data-tier="specialist"] {
    background: rgba(188,140,255,0.05);
  }
  .chat-msg.agent[data-tier="background"] {
    background: rgba(139,148,158,0.04);
    opacity: 0.85;
  }

  /* Command section — collapsible */
  .cmd-section {
    margin-top: 4px;
    border-top: 1px solid var(--border);
    padding-top: 4px;
    position: relative;
  }
  .cmd-toggle-btn {
    background: none;
    border: none;
    color: var(--muted);
    font-size: 0.72em;
    cursor: pointer;
    padding: 2px 4px;
    font-family: inherit;
  }
  .cmd-toggle-btn:hover { color: var(--accent); }
  .cmd-list {
    display: none;
    padding: 4px 0;
    max-height: 200px;
    overflow-y: auto;
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    background: var(--card);
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: var(--radius) var(--radius) 0 0;
    z-index: 12;
  }
  .cmd-list.open { display: block; }
  .cmd-list-item {
    padding: 3px 8px;
    font-size: 0.72em;
    color: var(--muted);
    cursor: pointer;
    display: flex;
    gap: 6px;
    align-items: center;
    border-radius: 3px;
  }
  .cmd-list-item:hover { background: rgba(88,166,255,0.08); color: var(--accent); }
  .cmd-list-item.locked { opacity: 0.4; cursor: default; }
  .cmd-list-item .cmd-name { color: var(--accent); font-weight: 500; min-width: 90px; }
  .cmd-list-item.locked .cmd-name { color: var(--muted); }
  .cmd-list-group { font-size: 0.68em; color: var(--muted); padding: 4px 8px 2px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }

  /* Command palette (VS Code style, Ctrl+K) */
  .cmd-palette {
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    max-height: 200px;
    overflow-y: auto;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 4px;
    display: none;
    z-index: 10;
    margin-bottom: 4px;
  }
  .cmd-palette.open { display: block; }
  .cmd-palette-item {
    padding: 4px 8px;
    cursor: pointer;
    border-radius: 3px;
    font-size: 0.76em;
    color: var(--text);
  }
  .cmd-palette-item:hover { background: rgba(88,166,255,0.12); }
  .cmd-palette-item .cmd-name { color: var(--accent); }

  /* Global Command Palette Overlay */
  .vpal-overlay {
    display: none;
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0,0,0,0.5);
    z-index: 9999;
    align-items: flex-start;
    justify-content: center;
    padding-top: 18vh;
  }
  .vpal-overlay.open { display: flex; }
  .vpal-box {
    width: 520px;
    max-width: 95vw;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 16px 48px rgba(0,0,0,0.5);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    max-height: 420px;
  }
  .vpal-input-wrap {
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--border);
    padding: 0 12px;
  }
  .vpal-input-wrap .vpal-icon {
    font-size: 1em;
    color: var(--muted);
    flex-shrink: 0;
    margin-right: 8px;
  }
  .vpal-input {
    flex: 1;
    background: none;
    border: none;
    color: var(--text);
    font-size: 0.9em;
    font-family: inherit;
    padding: 12px 0;
    outline: none;
  }
  .vpal-input::placeholder { color: var(--muted); }
  .vpal-hint {
    font-size: 0.68em;
    color: var(--muted);
    padding: 0 8px;
    flex-shrink: 0;
  }
  .vpal-results {
    overflow-y: auto;
    flex: 1;
  }
  .vpal-group {
    font-size: 0.65em;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--muted);
    padding: 8px 14px 3px;
  }
  .vpal-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 14px;
    cursor: pointer;
    font-size: 0.84em;
    color: var(--text);
    transition: background 0.08s;
  }
  .vpal-item:hover, .vpal-item.active { background: rgba(88,166,255,0.1); }
  .vpal-item-icon {
    font-size: 1em;
    min-width: 18px;
    text-align: center;
    color: var(--muted);
  }
  .vpal-item-label { flex: 1; }
  .vpal-item-source {
    font-size: 0.78em;
    color: var(--muted);
    flex-shrink: 0;
  }
  .vpal-item-shortcut {
    font-size: 0.7em;
    color: var(--muted);
    background: rgba(255,255,255,0.06);
    padding: 2px 6px;
    border-radius: 3px;
    font-family: 'Consolas', 'Menlo', monospace;
  }
  .vpal-empty {
    padding: 16px;
    text-align: center;
    color: var(--muted);
    font-size: 0.82em;
  }

  /* ── Diff review panel ────────────────────────────────── */
  .diff-review-banner {
    background: linear-gradient(90deg, rgba(210,153,34,0.08), rgba(210,153,34,0.02));
    border: 1px solid rgba(210,153,34,0.25);
    border-radius: var(--radius);
    padding: 12px 16px;
    margin-bottom: 14px;
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
  }
  .diff-review-banner:hover { border-color: var(--yellow); }
  .diff-badge {
    background: var(--yellow);
    color: #000;
    font-weight: 700;
    font-size: 0.72em;
    padding: 2px 10px;
    border-radius: 10px;
  }
  .diff-viewer {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px;
    margin-bottom: 12px;
    font-family: 'Consolas','Menlo',monospace;
    font-size: 0.82em;
    max-height: 320px;
    overflow-y: auto;
    line-height: 1.5;
  }
  .diff-line-add { color: var(--green); background: rgba(63,185,80,0.08); }
  .diff-line-del { color: var(--red); background: rgba(248,81,73,0.08); text-decoration: line-through; }
  .diff-line-ctx { color: var(--muted); }
  .diff-actions { display: flex; gap: 8px; margin-top: 10px; }

  /* ── Agent profile card ─────────────────────────────── */
  .agent-profile-card {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 20px;
    max-width: 560px;
  }
  .agent-profile-header {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 14px;
  }
  .agent-profile-sigil {
    font-size: 2em;
    line-height: 1;
  }
  .agent-profile-name {
    font-size: 1.15em;
    font-weight: 700;
  }
  .agent-profile-title {
    font-size: 0.82em;
    color: var(--muted);
  }
  .agent-profile-section {
    margin-top: 12px;
  }
  .agent-profile-section h4 {
    font-size: 0.72em;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    margin-bottom: 6px;
  }
  .agent-profile-section ul {
    list-style: none;
    padding: 0;
    font-size: 0.85em;
    color: var(--text);
  }
  .agent-profile-section ul li {
    padding: 3px 0;
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .agent-profile-section ul li::before {
    content: '\2022';
    color: var(--accent);
    font-weight: 700;
  }
  .agent-tool-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: rgba(88,166,255,0.08);
    border: 1px solid rgba(88,166,255,0.15);
    border-radius: 6px;
    padding: 3px 10px;
    font-size: 0.8em;
    color: var(--text);
    margin: 2px 3px 2px 0;
  }
  .agent-quick-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 12px;
  }

  /* ── Shared components ──────────────────────────────── */
  .v-card {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px;
    margin-bottom: 12px;
  }
  .v-card h2 { font-size: 1.05em; color: var(--accent); margin-bottom: 10px; }
  .v-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
  .v-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.75em;
    font-weight: 600;
  }
  .v-badge-accent { background: rgba(88,166,255,0.15); color: var(--accent); }
  .v-badge-green { background: rgba(63,185,80,0.15); color: var(--green); }
  .v-badge-yellow { background: rgba(210,153,34,0.15); color: var(--yellow); }
  .v-badge-pro {
    background: linear-gradient(135deg, rgba(163,113,247,0.2), rgba(88,166,255,0.15));
    color: #a371f7; font-size: 0.62em; padding: 1px 6px; border-radius: 6px;
    font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;
    border: 1px solid rgba(163,113,247,0.25); flex-shrink: 0;
  }
  .v-badge-cloud {
    background: linear-gradient(135deg, rgba(88,166,255,0.2), rgba(56,211,100,0.1));
    color: #58a6ff; font-size: 0.62em; padding: 1px 6px; border-radius: 6px;
    font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;
    border: 1px solid rgba(88,166,255,0.25); flex-shrink: 0;
  }
  /* Tool approval card (Cursor-style) */
  .tool-approval-card {
    background: var(--card); border: 1px solid var(--border); border-radius: 10px;
    padding: 12px 14px; margin: 8px 0;
  }
  .tool-approval-header {
    display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  }
  .tool-approval-icon {
    width: 28px; height: 28px; border-radius: 6px;
    background: rgba(88,166,255,0.1); display: flex; align-items: center;
    justify-content: center; font-size: 0.9em; flex-shrink: 0;
  }
  .tool-approval-title {
    font-size: 0.85em; font-weight: 600; color: var(--text);
  }
  .tool-approval-subtitle {
    font-size: 0.75em; color: var(--muted); margin-top: 1px;
  }
  .tool-approval-detail {
    background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
    padding: 8px 10px; font-family: var(--mono); font-size: 0.78em;
    color: var(--muted); margin-bottom: 10px; overflow-x: auto; white-space: pre-wrap;
    word-break: break-all;
  }
  .tool-approval-actions {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  }
  .tool-approval-actions .v-btn { font-size: 0.78em; padding: 4px 12px; }
  .ta-allowlist-btn {
    background: rgba(88,166,255,0.12); color: var(--accent); border: 1px solid rgba(88,166,255,0.3);
    border-radius: 6px; padding: 4px 12px; font-size: 0.78em; cursor: pointer;
    font-weight: 600; transition: background 0.15s;
  }
  .ta-allowlist-btn:hover { background: rgba(88,166,255,0.25); }
  .ta-skip-btn {
    background: transparent; color: var(--muted); border: none;
    font-size: 0.78em; cursor: pointer; padding: 4px 8px;
  }
  .ta-skip-btn:hover { color: var(--text); }
  .ta-run-btn {
    background: var(--accent); color: #fff; border: none; border-radius: 6px;
    padding: 5px 14px; font-size: 0.78em; cursor: pointer; font-weight: 600;
    margin-left: auto;
  }
  .ta-run-btn:hover { filter: brightness(1.15); }
  /* Tool execution display (Cursor-style collapsed result) */
  .tool-exec-card {
    background: var(--card); border: 1px solid var(--border); border-radius: 8px;
    margin: 6px 0; overflow: hidden; font-size: 0.85em;
  }
  .tool-exec-header {
    display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer;
    transition: background 0.15s;
  }
  .tool-exec-header:hover { background: rgba(88,166,255,0.04); }
  .tool-exec-status {
    width: 16px; height: 16px; border-radius: 50%; display: flex;
    align-items: center; justify-content: center; font-size: 0.7em; flex-shrink: 0;
  }
  .tool-exec-status.running { background: rgba(88,166,255,0.15); color: var(--accent); }
  .tool-exec-status.done { background: rgba(63,185,80,0.15); color: var(--green); }
  .tool-exec-status.failed { background: rgba(248,81,73,0.15); color: var(--red); }
  .tool-exec-label {
    font-weight: 600; color: var(--text); flex: 1;
  }
  .tool-exec-label span { color: var(--muted); font-weight: 400; }
  .tool-exec-chevron {
    color: var(--muted); font-size: 0.7em; transition: transform 0.15s;
  }
  .tool-exec-card.expanded .tool-exec-chevron { transform: rotate(90deg); }
  .tool-exec-body {
    display: none; padding: 0 12px 10px 12px;
    border-top: 1px solid var(--border); font-family: var(--mono);
    font-size: 0.82em; color: var(--muted); white-space: pre-wrap; word-break: break-all;
  }
  .tool-exec-card.expanded .tool-exec-body { display: block; padding-top: 8px; }
  /* Upload modal */
  .upload-modal-overlay {
    position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 1100;
    display: flex; align-items: center; justify-content: center;
  }
  .upload-modal {
    background: var(--card); border: 1px solid var(--border); border-radius: 12px;
    padding: 24px; width: 420px; max-width: 90vw; box-shadow: 0 12px 40px rgba(0,0,0,0.5);
  }
  .upload-modal h3 { font-size: 1em; margin: 0 0 14px 0; color: var(--text); }
  .upload-target-btn {
    display: flex; align-items: center; gap: 12px; padding: 14px 16px;
    border: 1px solid var(--border); border-radius: 8px; background: var(--bg);
    cursor: pointer; width: 100%; text-align: left; margin-bottom: 8px;
    transition: border-color 0.15s, background 0.15s; color: var(--text); font-size: 0.88em;
  }
  .upload-target-btn:hover { border-color: var(--accent); background: rgba(88,166,255,0.05); }
  .upload-target-btn .ut-icon { font-size: 1.4em; flex-shrink: 0; }
  .upload-target-btn .ut-desc { font-size: 0.78em; color: var(--muted); margin-top: 2px; }
  .upload-dropzone {
    border: 2px dashed var(--border); border-radius: 8px; padding: 30px 20px;
    text-align: center; color: var(--muted); font-size: 0.85em; margin-top: 12px;
    transition: border-color 0.15s; cursor: pointer;
  }
  .upload-dropzone:hover, .upload-dropzone.dragover { border-color: var(--accent); color: var(--accent); }
  /* Config ellipsis menu */
  .config-menu {
    position: fixed; background: var(--card); border: 1px solid var(--border);
    border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,0.35); z-index: 1050;
    min-width: 200px; padding: 6px 0;
  }
  .config-menu-item {
    display: flex; align-items: center; gap: 10px; padding: 8px 14px;
    font-size: 0.85em; color: var(--text); cursor: pointer; transition: background 0.1s;
  }
  .config-menu-item:hover { background: rgba(88,166,255,0.08); }
  .config-menu-item .cm-icon { font-size: 1em; width: 20px; text-align: center; }
  /* Priority colors for check-in items */
  .ci-priority-critical { border-left: 3px solid #f85149; }
  .ci-priority-high { border-left: 3px solid #ffa657; }
  .ci-priority-medium { border-left: 3px solid #d29922; }
  .ci-priority-low { border-left: 3px solid #3fb950; }
  .ci-priority-none { border-left: 3px solid var(--border); }
  /* Background job toast */
  .bg-job-toast {
    position: fixed; bottom: 20px; right: 440px; background: var(--card);
    border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 6px 24px rgba(0,0,0,0.3); padding: 12px 16px;
    display: flex; align-items: center; gap: 12px; z-index: 1200;
    animation: bgToastIn 0.3s ease-out; max-width: 360px;
  }
  @keyframes bgToastIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
  .bg-job-toast .bgt-icon { font-size: 1.4em; flex-shrink: 0; }
  .bg-job-toast .bgt-body { flex: 1; min-width: 0; }
  .bg-job-toast .bgt-title { font-size: 0.85em; font-weight: 600; color: var(--text); }
  .bg-job-toast .bgt-sub { font-size: 0.75em; color: var(--muted); margin-top: 2px; }
  .bg-job-toast .bgt-actions { display: flex; gap: 6px; flex-shrink: 0; }
  .bg-job-toast .bgt-btn {
    padding: 4px 10px; border-radius: 5px; font-size: 0.78em; cursor: pointer; border: none;
  }
  .bg-job-toast .bgt-done { background: var(--border); color: var(--text); }
  .bg-job-toast .bgt-goto { background: var(--accent); color: #fff; font-weight: 600; }
  /* Suggestion frequency control */
  .freq-control {
    display: flex; gap: 4px; align-items: center;
  }
  .freq-btn {
    padding: 3px 10px; border-radius: 5px; font-size: 0.72em; font-weight: 600;
    cursor: pointer; border: 1px solid var(--border); background: transparent; color: var(--muted);
    transition: all 0.15s;
  }
  .freq-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
  .freq-btn:hover:not(.active) { border-color: var(--accent); color: var(--accent); }
  .v-empty {
    text-align: center;
    padding: 40px 20px;
    color: var(--muted);
  }
  .v-empty p { margin: 6px 0; }
  .v-btn {
    background: var(--accent);
    color: #fff;
    border: none;
    padding: 7px 18px;
    border-radius: 6px;
    cursor: pointer;
    font: inherit;
    font-size: 0.88em;
  }
  .v-btn:hover { opacity: 0.9; }
  .v-btn-outline {
    background: none;
    border: 1px solid var(--accent);
    color: var(--accent);
    padding: 6px 16px;
    border-radius: 6px;
    cursor: pointer;
    font: inherit;
    font-size: 0.85em;
  }
  .v-btn-outline:hover { background: rgba(88,166,255,0.1); }
  .v-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
  .v-tab {
    padding: 9px 18px;
    background: none;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font: inherit;
    font-size: 0.88em;
    border-bottom: 2px solid transparent;
    transition: color 0.15s;
  }
  .v-tab:hover { color: var(--text); }
  .v-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
  .v-tab-pane { display: none; }
  .v-tab-pane.active { display: block; }
  .v-form-row { margin-bottom: 12px; }
  .v-form-row label { display: block; color: var(--muted); font-size: 0.83em; margin-bottom: 4px; }
  .v-form-row input, .v-form-row select, .v-form-row textarea {
    width: 100%;
    max-width: 400px;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 8px 12px;
    border-radius: 6px;
    font: inherit;
    font-size: 0.9em;
  }
  .v-loading { text-align: center; padding: 40px; color: var(--muted); }
  .v-list-item {
    padding: 12px 16px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: border-color 0.15s;
  }
  .v-list-item:hover { border-color: var(--accent); }
  .v-list-item h3 { font-size: 0.95em; color: var(--text); margin-bottom: 4px; }
  .v-list-item p { font-size: 0.82em; color: var(--muted); }
  .v-toast {
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--green);
    color: #fff;
    padding: 8px 20px;
    border-radius: var(--radius);
    opacity: 0;
    transition: opacity 0.3s;
    z-index: 200;
    font-size: 0.9em;
  }
  .v-toast.show { opacity: 1; }
  .v-detail-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--muted);
    font-size: 0.85em;
    cursor: pointer;
    margin-bottom: 16px;
  }
  .v-detail-back:hover { color: var(--accent); }
  .v-metric-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); }
  .v-metric-row:last-child { border-bottom: none; }
  .v-metric-name { font-size: 0.9em; }
  .v-metric-val { font-size: 0.9em; color: var(--accent); font-weight: 600; }

  /* ── Collapsible chat ───────────────────────────────── */
  .chat-panel.collapsed { width: 0 !important; min-width: 0 !important; overflow: hidden; padding: 0; border: none; }
  .chat-toggle-btn {
    position: fixed; top: 12px; right: 16px; z-index: 200;
    display: none; align-items: center; gap: 6px;
    background: var(--accent); color: #fff; border: none; border-radius: 8px;
    padding: 8px 14px; font-size: 0.82em; font-weight: 600; cursor: pointer;
    box-shadow: 0 2px 8px rgba(0,0,0,0.3);
  }
  .chat-toggle-btn:hover { filter: brightness(1.1); }
  .chat-collapse-btn, .chat-history-btn {
    background: none; border: none; color: var(--muted); cursor: pointer;
    font-size: 0.85em; padding: 2px 6px; border-radius: 4px;
  }
  .chat-collapse-btn:hover, .chat-history-btn:hover { background: var(--border); color: var(--fg); }

  /* ── Thread list overlay ───────────────────────────── */
  .chat-threads {
    background: var(--card); border-bottom: 1px solid var(--border);
    max-height: 260px; overflow-y: auto;
  }
  .chat-threads-header { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--border); }
  .chat-thread-item {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--border);
    transition: background 0.1s;
  }
  .chat-thread-item:hover { background: rgba(56,189,248,0.06); }
  .chat-thread-item.active { background: rgba(56,189,248,0.1); border-left: 2px solid var(--accent); }
  .chat-thread-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
  .chat-thread-info { flex: 1; min-width: 0; }
  .chat-thread-name { font-size: 0.82em; font-weight: 600; }
  .chat-thread-preview { font-size: 0.72em; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .chat-thread-time { font-size: 0.65em; color: var(--muted); flex-shrink: 0; }

  /* ── History divider ───────────────────────────────── */
  .chat-history-divider {
    text-align: center; font-size: 0.68em; color: var(--muted);
    padding: 6px 0; border-bottom: 1px solid var(--border);
    margin-bottom: 4px;
  }

  /* ── Mobile ─────────────────────────────────────────── */
  @media (max-width: 900px) {
    .chat-panel { display: none; }
    .chat-toggle-btn { display: flex; }
    .sidebar:hover { width: var(--sidebar-w); }
    .sidebar:hover .nav-label { opacity: 0; display: none; }
  }

  /* ── Document viewer ─────────────────────────────────── */
  .doc-viewer {
    display: flex;
    height: calc(100vh - 100px);
    gap: 0;
    margin: -16px;
  }
  .doc-tree {
    width: 240px;
    min-width: 240px;
    border-right: 1px solid var(--border);
    overflow-y: auto;
    background: var(--card);
    padding-bottom: 20px;
  }
  .doc-tree-header {
    font-size: 0.72em;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    font-weight: 700;
    color: var(--muted);
    padding: 14px 16px 10px;
  }
  .doc-agent-group { border-bottom: 1px solid var(--border); }
  .doc-agent-group.collapsed .doc-file-list { display: none; }
  .doc-agent-group.collapsed .doc-agent-arrow { transform: rotate(-90deg); }
  .doc-agent-name {
    padding: 8px 14px;
    font-size: 0.85em;
    font-weight: 600;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
    user-select: none;
  }
  .doc-agent-name:hover { background: rgba(255,255,255,0.03); }
  .doc-agent-arrow {
    display: inline-block;
    font-size: 0.8em;
    transition: transform 0.15s;
  }
  .doc-agent-count {
    margin-left: auto;
    font-size: 0.72em;
    color: var(--muted);
    background: var(--bg);
    padding: 1px 6px;
    border-radius: 8px;
  }
  .doc-file-list { padding: 0 0 4px 0; }
  .doc-file {
    padding: 5px 14px 5px 32px;
    font-size: 0.82em;
    color: var(--muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
    transition: all 0.1s;
  }
  .doc-file:hover { background: rgba(88,166,255,0.06); color: var(--text); }
  .doc-file.active { background: rgba(88,166,255,0.12); color: var(--accent); font-weight: 600; }
  .doc-file.doc-missing { opacity: 0.4; }
  .doc-file-icon { font-size: 0.9em; }
  .doc-content {
    flex: 1;
    overflow-y: auto;
    padding: 0;
  }
  .doc-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    text-align: center;
    color: var(--muted);
    padding: 40px;
  }
  .doc-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 20px;
    border-bottom: 1px solid var(--border);
    background: var(--card);
    font-size: 0.85em;
    flex-wrap: wrap;
  }
  .doc-modified { margin-left: auto; color: var(--muted); font-size: 0.82em; }
  .v-btn-sm {
    padding: 4px 12px;
    font-size: 0.78em;
    border-radius: 6px;
  }
  .doc-rendered {
    padding: 24px 28px;
    max-width: 800px;
    font-size: 0.9em;
    line-height: 1.75;
    color: var(--text);
  }
  .doc-rendered h1 { font-size: 1.5em; margin: 24px 0 10px; font-weight: 700; }
  .doc-rendered h2 { font-size: 1.2em; margin: 20px 0 8px; font-weight: 700; color: var(--accent); }
  .doc-rendered h3 { font-size: 1em; margin: 16px 0 6px; font-weight: 600; }
  .doc-rendered p { margin: 6px 0; }
  .doc-rendered ul, .doc-rendered ol { padding-left: 20px; margin: 8px 0; }
  .doc-rendered li { margin-bottom: 4px; }
  .doc-rendered pre {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 12px 16px;
    font-size: 0.88em;
    overflow-x: auto;
    margin: 12px 0;
  }
  .doc-rendered code {
    background: var(--bg);
    padding: 2px 6px;
    border-radius: 3px;
    font-size: 0.9em;
    border: 1px solid var(--border);
  }
  .doc-rendered pre code { background: none; border: none; padding: 0; }
  .doc-rendered hr { border: none; border-top: 1px solid var(--border); margin: 20px 0; }
  .doc-rendered a { color: var(--accent); }
  .doc-md-table {
    width: 100%;
    border-collapse: collapse;
    margin: 12px 0;
    font-size: 0.88em;
  }
  .doc-md-table td {
    padding: 6px 10px;
    border: 1px solid var(--border);
  }
  .doc-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 200px;
    color: var(--muted);
  }
  @media (max-width: 700px) {
    .doc-viewer { flex-direction: column; height: auto; }
    .doc-tree { width: 100%; min-width: 0; max-height: 240px; border-right: none; border-bottom: 1px solid var(--border); }
  }
</style>
</head>
<body>
<div id="app">

<!-- ── Sidebar (Activity Bar + Side Panel) ─────────── -->
<div class="sidebar-container" id="sidebarContainer">
  <!-- Activity Bar (always visible, icon-only) -->
  <div class="activity-bar" id="activityBar">
    <button class="ab-btn active" data-view="explorer" title="Explorer">&#x1F4C1;</button>
    <button class="ab-btn" data-view="pages" title="Pages">&#x25A6;</button>
    <button class="ab-btn" data-view="commands" title="Commands">&#x2318;</button>
    <button class="ab-btn" data-view="activity" title="Activity Log" style="position:relative">&#x1F4CA;<span class="ab-badge" id="badge-activity"></span></button>
    <div class="ab-spacer"></div>
    <!-- Bottom nav (Zo-style: Bookmarks, Upload, Notifications, ellipsis, Settings) -->
    <button class="ab-btn" title="Bookmarks" onclick="window._openBookmarks()">&#x1F516;</button>
    <button class="ab-btn" title="Upload to Inbox" onclick="window._openUploadModal()">&#x2B06;</button>
    <button class="ab-btn" data-view="notifications" title="Notifications" style="position:relative">
      &#x1F514;<span class="ab-badge" id="badge-bell"></span>
    </button>
    <button class="ab-btn" title="More config pages" onclick="window._openConfigMenu(this)" style="position:relative">&#x22EF;</button>
    <button class="ab-btn" data-view="settings" title="Settings">&#x2699;</button>
  </div>
  <!-- Side Panel (pinnable, shows content based on active view) -->
  <div class="side-panel" id="sidePanel">
    <div class="sp-header">
      <span class="sp-title" id="spTitle">EXPLORER</span>
      <div style="display:flex;gap:4px;align-items:center">
        <button class="sp-pin" id="spPopout" title="Open as tab" onclick="window._popoutSideView()">&#x2197;</button>
        <button class="sp-pin" id="spPin" title="Pin sidebar">&#x1F4CC;</button>
      </div>
    </div>
    <div class="sp-content" id="spContent">
      <!-- Dynamically rendered based on active view -->
    </div>
  </div>
</div>
<!-- Notification panel (rendered on demand) -->
<div id="notificationPanel" style="display:none;position:fixed;top:40px;left:52px;z-index:200;background:var(--card);border:1px solid var(--border);border-radius:8px;max-height:340px;width:280px;overflow-y:auto;padding:10px;font-size:0.82em;box-shadow:0 4px 20px rgba(0,0,0,0.3)">
  <div id="notificationList"><p style="color:var(--muted);text-align:center">Loading...</p></div>
</div>

<!-- ── Context panel ───────────────────────────────────── -->
<main class="context-panel">
  <div id="tierBanner" style="display:none;background:linear-gradient(90deg,#a371f7,#7c3aed);color:#fff;text-align:center;padding:6px 12px;font-size:0.82em;font-weight:600">
    Simulating Tier <span id="tierBannerLevel">?</span>
    <button onclick="_resetTierBanner()" style="background:rgba(255,255,255,0.2);border:none;color:#fff;border-radius:4px;padding:2px 10px;margin-left:8px;cursor:pointer;font-size:0.9em">Reset</button>
  </div>
  <div class="tab-bar" id="tabBar"><!-- tabs rendered dynamically --></div>
  <div class="tab-breadcrumb" id="tabBreadcrumb"></div>
  <div class="page-header" id="pageHeader" style="display:none">
    <h1 id="pageTitle"></h1>
    <p id="pageSubtitle"></p>
  </div>
  <div id="tabPanes" style="flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative">
    <!-- tab panes created dynamically, pageContent kept as fallback -->
    <div class="page-content" id="pageContent" style="display:none"></div>
  </div>
</main>

<!-- ── Resize handle ──────────────────────────────────── -->
<div class="chat-resize-handle" id="chatResizeHandle"></div>

<!-- ── Chat panel (always visible, collapsible) ─────────────── -->
<aside class="chat-panel" id="chatPanel">
  <div class="chat-header">
    <h3>Copilot</h3>
    <div style="display:flex;align-items:center;gap:6px">
      <button class="chat-history-btn" id="chatHistoryBtn" title="Chat threads" onclick="window._toggleThreadList()">&#128172;</button>
      <span style="font-size:0.68em;color:var(--muted)">Ctrl+K</span>
      <button class="chat-collapse-btn" id="chatCollapseBtn" title="Collapse chat" onclick="window._toggleChatPanel()">&#10095;</button>
    </div>
  </div>
  <!-- Thread list overlay -->
  <div class="chat-threads" id="chatThreads" style="display:none">
    <div class="chat-threads-header">
      <strong>Recent Chats</strong>
      <button onclick="window._toggleThreadList()" style="background:none;border:none;color:var(--muted);cursor:pointer;font-size:1em">&times;</button>
    </div>
    <div id="chatThreadList"></div>
  </div>
  <!-- History divider + messages -->
  <div class="chat-messages" id="chatMessages"></div>
  <div class="chat-typing" id="chatTyping">Thinking...</div>
  <!-- Document bar (Cursor-style, expands upward) -->
  <div class="doc-bar" id="docBar">
    <div class="doc-bar-toggle" id="docBarToggle" onclick="window._toggleDocBar()">
      <span class="doc-bar-chevron" id="docBarChevron">&#9656;</span>
      <span id="docBarLabel">0 Files</span>
    </div>
    <div class="doc-bar-list" id="docBarList"></div>
  </div>
  <div class="chat-mode-indicator" id="chatModeIndicator"></div>
  <div class="chat-input">
    <div style="position:relative">
      <div class="cmd-palette" id="cmdPalette"></div>
      <textarea id="chatInput" placeholder="Message agents... type / for commands" rows="2"></textarea>
    </div>
    <div class="chat-controls">
      <select id="chatMode" class="chat-mode-select" onchange="window._onChatModeChange(this.value)" title="Chat mode">
        <option value="freeform">Free-form</option>
        <option value="interview">Interview Me</option>
      </select>
      <div class="agent-selector">
        <button class="agent-chief-btn" id="chiefBtn" onclick="window._switchAgent('charles')" title="Charles — Chief of Staff">&#9733; Charles</button>
        <select id="chatAgent" onchange="window._onAgentSwitch(this.value)">
          <option value="charles" selected>Charles (auto-route)</option>
        </select>
        <button style="background:none;border:1px solid var(--border);border-radius:4px;color:var(--muted);font-size:0.72em;padding:2px 6px;cursor:pointer;margin-left:4px" onclick="window._openCheckinDropdown(document.getElementById('chatAgent').value)" title="Check-in items">&#x2611;</button>
      </div>
      <button id="chatSend">Send</button>
    </div>
    <div class="cmd-section" id="cmdSection">
      <button class="cmd-toggle-btn" onclick="document.getElementById('cmdList').classList.toggle('open'); this.textContent = document.getElementById('cmdList').classList.contains('open') ? 'Commands &#9650;' : 'Commands &#9660;'">Commands &#9660;</button>
      <div class="cmd-list" id="cmdList"></div>
    </div>
  </div>
</aside>
<!-- Floating Chat button when collapsed -->
<button class="chat-toggle-btn" id="chatToggleBtn" onclick="window._toggleChatPanel()" title="Open chat (Ctrl+K)">&#128172; Chat</button>

</div><!-- #app -->
<div class="v-toast" id="toast"></div>

<!-- ── Global Command Palette (VS Code style) ──────── -->
<div class="vpal-overlay" id="vpalOverlay">
  <div class="vpal-box">
    <div class="vpal-input-wrap">
      <span class="vpal-icon">&#x1F50D;</span>
      <input class="vpal-input" id="vpalInput" type="text" placeholder="Search pages, documents, agents... (type > for commands)" autocomplete="off" spellcheck="false">
      <span class="vpal-hint">esc to close</span>
    </div>
    <div class="vpal-results" id="vpalResults"></div>
  </div>
</div>

<script>
(function() {
  /* ── Config ───────────────────────────────────────────── */
  var PAGES = {
    dashboard:  { label: 'Home',       desc: 'Charles — Chief of Staff',                render: renderDashboard },
    boards:     { label: 'Boards',     desc: 'Kanban task boards',                    fetch: '/feed?tab=kanban' },
    sessions:   { label: 'Sessions',   desc: 'Session roster and management',         fetch: '/roster' },
    personas:   { label: 'Personas',   desc: 'Persona radar, roleplay, and challenges', render: renderPersonas },
    scenarios:  { label: 'Scenarios',  desc: 'Browse and start life scenarios',       render: renderScenarios },
    plans:      { label: 'Plans',      desc: 'Health plans and spiritual plans',      render: renderPlans },
    metrics:    { label: 'Metrics',    desc: 'Definitions, baselines, and blueprints', render: renderMetrics },
    meetings:   { label: 'Meetings',   desc: 'Meeting transcripts and signals',       render: renderMeetings },
    councils:   { label: 'Councils',   desc: 'Mentor councils and wisdom sessions',   render: renderCouncils },
    documents:  { label: 'Documents',  desc: 'Agent documents and knowledge base',   render: renderDocuments },
    settings:   { label: 'Settings',   desc: 'System configuration',                  fetch: '/settings' },
    backup:     { label: 'Backup',     desc: 'Backup and restore',                    fetch: '/backup' },
    setup:      { label: 'Setup',      desc: 'First-run setup wizard',               render: renderSetup, hidden: true },
    roleplay:   { label: 'Roleplay',   desc: 'Active roleplay session',              render: renderActiveRoleplay, hidden: true },
  };

  var currentPage = '';
  var chatCommands = [];
  var chatAgents = [];

  /* ── Utilities ────────────────────────────────────────── */
  function esc(s) { var d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; }
  function $(id) { return document.getElementById(id); }
  function showToast(msg) {
    var t = $('toast');
    t.textContent = msg;
    t.classList.add('show');
    setTimeout(function() { t.classList.remove('show'); }, 2500);
  }

  /* ── Tab system (VS Code style) ─────────────────────── */
  var _openTabs = [];      /* [{id, label, icon, route, type, agentId, docName, closable}] */
  var _activeTabId = null;
  var _tabIdCounter = 0;

  /* Page-specific icons for tabs */
  var PAGE_ICONS = {
    dashboard: '&#9733;', boards: '&#x1F4CB;', sessions: '&#x1F4AC;',
    personas: '&#x1F52E;', scenarios: '&#x1F3AF;', plans: '&#x1F4D0;',
    metrics: '&#x1F4CA;', meetings: '&#x1F4DD;', councils: '&#x1F9E0;',
    documents: '&#x1F4C4;', settings: '&#x2699;', backup: '&#x1F4BE;',
    setup: '&#x1F680;', roleplay: '&#x1F3AD;'
  };

  function _genTabId() { return 'tab-' + (++_tabIdCounter); }

  function _findTabByRoute(route) {
    for (var i = 0; i < _openTabs.length; i++) {
      if (_openTabs[i].type === 'page' && _openTabs[i].route === route) return _openTabs[i];
    }
    return null;
  }

  function _findTabByDoc(agentId, docName) {
    for (var i = 0; i < _openTabs.length; i++) {
      if (_openTabs[i].type === 'document' && _openTabs[i].agentId === agentId && _openTabs[i].docName === docName) return _openTabs[i];
    }
    return null;
  }

  function _findTabByType(type, key) {
    for (var i = 0; i < _openTabs.length; i++) {
      if (_openTabs[i].type === type && _openTabs[i].viewKey === key) return _openTabs[i];
    }
    return null;
  }

  function _openTab(opts) {
    /* opts: {label, icon, route, type, agentId, docName, closable, viewKey, pinned} */
    var existing;
    if (opts.type === 'page') existing = _findTabByRoute(opts.route);
    else if (opts.type === 'document') existing = _findTabByDoc(opts.agentId, opts.docName);
    else if (opts.type === 'view') existing = _findTabByType('view', opts.viewKey);

    if (existing) {
      _activateTab(existing.id);
      return existing;
    }

    var tab = {
      id: _genTabId(),
      label: opts.label || 'Tab',
      icon: opts.icon || '&#x1F4C4;',
      route: opts.route || null,
      type: opts.type || 'page',
      agentId: opts.agentId || null,
      docName: opts.docName || null,
      viewKey: opts.viewKey || null,
      closable: opts.closable !== false,
      pinned: opts.pinned || false
    };
    _openTabs.push(tab);

    /* Create pane */
    var pane = document.createElement('div');
    pane.className = 'tab-pane';
    pane.id = 'tabPane-' + tab.id;
    if (tab.type === 'page' && PAGES[tab.route] && PAGES[tab.route].fetch) {
      pane.className = 'tab-pane fetched-page';
    }
    $('tabPanes').appendChild(pane);

    _renderTabBar();
    _activateTab(tab.id);
    return tab;
  }

  function _closeTab(tabId) {
    var idx = -1;
    for (var i = 0; i < _openTabs.length; i++) {
      if (_openTabs[i].id === tabId) { idx = i; break; }
    }
    if (idx === -1) return;
    var tab = _openTabs[idx];
    if (!tab.closable) return;

    /* Remove pane */
    var pane = $('tabPane-' + tabId);
    if (pane) pane.remove();

    _openTabs.splice(idx, 1);

    /* If we closed the active tab, activate the nearest */
    if (_activeTabId === tabId) {
      if (_openTabs.length > 0) {
        var newIdx = Math.min(idx, _openTabs.length - 1);
        _activateTab(_openTabs[newIdx].id);
      } else {
        _activeTabId = null;
      }
    }
    _renderTabBar();
  }
  window._closeTab = _closeTab;

  function _activateTab(tabId) {
    _activeTabId = tabId;
    /* Show/hide panes */
    var panes = $('tabPanes');
    if (panes) {
      var children = panes.querySelectorAll('.tab-pane');
      for (var i = 0; i < children.length; i++) {
        children[i].classList.toggle('active', children[i].id === 'tabPane-' + tabId);
      }
    }
    /* Hide legacy pageContent */
    var pc = $('pageContent');
    if (pc) pc.style.display = 'none';

    /* Update tab bar active state */
    _renderTabBar();

    /* Update breadcrumb */
    var tab = null;
    for (var j = 0; j < _openTabs.length; j++) {
      if (_openTabs[j].id === tabId) { tab = _openTabs[j]; break; }
    }
    var bc = $('tabBreadcrumb');
    if (bc && tab) {
      if (tab.type === 'document') {
        bc.textContent = (tab.agentId || '') + ' > ' + (tab.docName || '');
        bc.classList.add('show');
      } else {
        bc.classList.remove('show');
      }
    }

    /* Update current page tracker */
    if (tab && tab.route) currentPage = tab.route;

    /* Highlight nav items in the Pages side panel */
    document.querySelectorAll('.sp-nav-item[data-route]').forEach(function(el) {
      el.classList.toggle('active', tab && el.dataset.route === tab.route);
    });
  }

  function _renderTabBar() {
    var bar = $('tabBar');
    if (!bar) return;
    var html = '';
    _openTabs.forEach(function(tab) {
      var active = tab.id === _activeTabId ? ' active' : '';
      var pinCls = tab.pinned ? ' pinned' : '';
      html += '<div class="vtab' + active + pinCls + '" data-tab-id="' + tab.id + '">';
      html += '<span class="vtab-pin-dot"></span>';
      html += '<span class="vtab-icon">' + tab.icon + '</span>';
      html += '<span class="vtab-label">' + esc(tab.label) + '</span>';
      if (tab.closable && !tab.pinned) {
        html += '<button class="vtab-close" onclick="event.stopPropagation();_closeTab(\'' + tab.id + '\')">&times;</button>';
      }
      html += '</div>';
    });
    bar.innerHTML = html;
    /* Bind click, double-click, and right-click context menu */
    bar.querySelectorAll('.vtab').forEach(function(el) {
      var tid = el.dataset.tabId;
      el.addEventListener('click', function() { _activateTab(tid); });
      el.addEventListener('dblclick', function(e) {
        e.preventDefault();
        _toggleTabPin(tid);
      });
      el.addEventListener('contextmenu', function(e) {
        e.preventDefault();
        _showTabContextMenu(e, tid);
      });
    });
  }

  /* ── Tab right-click context menu ──────────────────── */
  function _showTabContextMenu(event, tabId) {
    /* Remove any existing context menu */
    var old = document.getElementById('vtab-ctx-menu');
    if (old) old.remove();

    var tab = null;
    for (var i = 0; i < _openTabs.length; i++) {
      if (_openTabs[i].id === tabId) { tab = _openTabs[i]; break; }
    }
    if (!tab) return;

    var menu = document.createElement('div');
    menu.id = 'vtab-ctx-menu';
    menu.style.cssText = 'position:fixed;z-index:9999;background:var(--card);border:1px solid var(--border);border-radius:8px;padding:4px 0;min-width:160px;box-shadow:0 8px 24px rgba(0,0,0,0.3);font-size:0.82em;';
    menu.style.left = event.clientX + 'px';
    menu.style.top = event.clientY + 'px';

    function menuItem(label, icon, onClick) {
      var item = document.createElement('div');
      item.style.cssText = 'padding:6px 12px;cursor:pointer;display:flex;align-items:center;gap:8px;color:var(--text);';
      item.innerHTML = '<span>' + icon + '</span><span>' + label + '</span>';
      item.onmouseover = function() { item.style.background = 'rgba(88,166,255,0.1)'; };
      item.onmouseout = function() { item.style.background = 'transparent'; };
      item.onclick = function() { menu.remove(); onClick(); };
      menu.appendChild(item);
    }

    /* Bookmark */
    var bm = JSON.parse(localStorage.getItem('vastian_bookmarks') || '[]');
    var isBookmarked = bm.some(function(b) { return b.path === (tab.route || tab.label); });
    if (isBookmarked) {
      menuItem('Remove Bookmark', '&#x1F516;', function() {
        var bookmarks = JSON.parse(localStorage.getItem('vastian_bookmarks') || '[]');
        var path = tab.route || tab.label;
        var filtered = bookmarks.filter(function(b) { return b.path !== path; });
        localStorage.setItem('vastian_bookmarks', JSON.stringify(filtered));
        showToast('Bookmark removed');
      });
    } else {
      menuItem('Bookmark Tab', '&#x1F516;', function() {
        window._addBookmark(tab.label, tab.route || tab.label, tab.icon);
      });
    }

    /* Pin/Unpin */
    menuItem(tab.pinned ? 'Unpin Tab' : 'Pin Tab', '&#x1F4CC;', function() {
      _toggleTabPin(tabId);
    });

    /* Close */
    if (tab.closable && !tab.pinned) {
      menuItem('Close Tab', '&times;', function() {
        _closeTab(tabId);
      });
    }

    /* Close Others */
    menuItem('Close Others', '&#x1F5D9;', function() {
      var toClose = [];
      _openTabs.forEach(function(t) {
        if (t.id !== tabId && t.closable && !t.pinned) toClose.push(t.id);
      });
      toClose.forEach(function(id) { _closeTab(id); });
    });

    document.body.appendChild(menu);

    /* Close context menu on any click elsewhere */
    var handler = function(evt) {
      if (!menu.contains(evt.target)) {
        menu.remove();
        document.removeEventListener('click', handler, true);
      }
    };
    setTimeout(function() { document.addEventListener('click', handler, true); }, 0);
  }

  function _toggleTabPin(tabId) {
    for (var i = 0; i < _openTabs.length; i++) {
      if (_openTabs[i].id === tabId) {
        _openTabs[i].pinned = !_openTabs[i].pinned;
        /* If pinned, make it non-closable; if unpinned, make closable (unless it's Home) */
        if (_openTabs[i].pinned) {
          _openTabs[i].closable = false;
        } else {
          _openTabs[i].closable = _openTabs[i].route !== 'dashboard';
        }
        break;
      }
    }
    _renderTabBar();
  }
  window._toggleTabPin = _toggleTabPin;
  window._activateTab = _activateTab;
  window._renderTabBar = _renderTabBar;

  /* ── Routing ──────────────────────────────────────────── */
  function getRoute() {
    var hash = window.location.hash.slice(1) || '/dashboard';
    var route = hash.replace(/^\/?/, '').split('/')[0] || 'dashboard';
    /* Support roleplay sub-routes like #/roleplay/rp-abc */
    if (route === 'roleplay') return 'roleplay';
    return route;
  }

  function navigate(route) {
    /* Handle settings sub-routes like /settings/api-keys */
    var settingsAnchor = '';
    if (typeof route === 'string' && route.indexOf('/settings/') === 0) {
      settingsAnchor = route.replace('/settings/', '');
      route = 'settings';
    }
    if (!PAGES[route]) route = 'dashboard';
    currentPage = route;

    /* Open or activate a page tab */
    var existingTab = _findTabByRoute(route);
    if (existingTab) {
      _activateTab(existingTab.id);
      /* Re-render if fetched page (to refresh content) */
      var pg = PAGES[route];
      if (pg && pg.fetch) {
        var pane = $('tabPane-' + existingTab.id);
        if (pane) loadFetchedPage(pg.fetch, pane, settingsAnchor);
      }
      return;
    }

    var pg = PAGES[route];
    var tab = _openTab({
      label: pg.label,
      icon: PAGE_ICONS[route] || '&#x1F4C4;',
      route: route,
      type: 'page',
      closable: route !== 'dashboard'
    });

    /* Render content into the tab pane */
    var pane = $('tabPane-' + tab.id);
    if (!pane) return;

    if (pg.fetch) {
      loadFetchedPage(pg.fetch, pane, settingsAnchor);
    } else if (pg.render) {
      pane.innerHTML = '<div class="v-loading">Loading...</div>';
      pg.render(pane);
    }
  }

  window.addEventListener('hashchange', function() { navigate(getRoute()); });

  /* ── Fetch and inject ─────────────────────────────────── */
  function loadFetchedPage(url, container, settingsAnchor) {
    fetch(url).then(function(r) { return r.text(); }).then(function(html) {
      var doc = new DOMParser().parseFromString(html, 'text/html');
      var styles = '';
      doc.querySelectorAll('style').forEach(function(s) { styles += s.textContent + '\n'; });
      var bodyHTML = doc.body ? doc.body.innerHTML : html;
      container.innerHTML = '<style>' + styles + '</style><div class="injected-body">' + bodyHTML + '</div>';
      /* Re-execute scripts */
      container.querySelectorAll('script').forEach(function(old) {
        var s = document.createElement('script');
        if (old.src) { s.src = old.src; } else { s.textContent = old.textContent; }
        old.parentNode.replaceChild(s, old);
      });
      /* Scroll to settings anchor if provided */
      if (settingsAnchor) {
        setTimeout(function() {
          var target = document.getElementById(settingsAnchor) || container.querySelector('[data-section="' + settingsAnchor + '"]');
          if (target) target.scrollIntoView({behavior: 'smooth', block: 'start'});
        }, 100);
      }
    }).catch(function(e) {
      container.innerHTML = '<div class="v-empty"><p>Failed to load: ' + esc(e.message) + '</p></div>';
    });
  }

  /* ── Dashboard ────────────────────────────────────────── */
  function renderDashboard(el) {
    fetch('/api/dashboard').then(function(r) { return r.json(); }).then(function(d) {
      var tier = d.tier || 0;
      var tierName = d.tier_name || 'New';
      var hint = d.next_hint || '';
      var tasksPending = d.tasks_pending || 0;
      var kanbanPending = d.kanban_pending || 0;
      var today = new Date().toLocaleDateString('en-US', {weekday:'long', month:'long', day:'numeric', year:'numeric'});
      var html = '';
      /* Charles welcome card */
      html += '<div class="v-card" style="border-left:3px solid ' + agentColor('charles') + '">';
      html += '<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">';
      html += '<span style="font-size:1.4em">&#9733;</span>';
      html += '<div><h2 style="color:var(--accent);margin:0;font-size:1.1em">Charles — Chief of Staff</h2>';
      html += '<p style="color:var(--muted);font-size:0.8em;margin:0">' + esc(today) + '</p></div></div>';
      html += '<p style="font-size:0.85em;color:var(--muted)">Charles coordinates your team. Talk to him in the chat panel, and he\'ll route your requests to the right specialist.</p>';
      html += '</div>';
      /* Stats row */
      html += '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;margin-bottom:16px">';
      html += '<div class="v-card" style="text-align:center"><div style="font-size:1.6em;color:var(--accent)">' + tasksPending + '</div><div style="font-size:0.78em;color:var(--muted)">Pending Tasks</div></div>';
      html += '<div class="v-card" style="text-align:center"><div style="font-size:1.6em;color:var(--accent)">' + kanbanPending + '</div><div style="font-size:0.78em;color:var(--muted)">Board Items</div></div>';
      html += '<div class="v-card" style="text-align:center"><div style="font-size:1.6em;color:var(--accent)">Tier ' + tier + '</div><div style="font-size:0.78em;color:var(--muted)">' + esc(tierName) + '</div></div>';
      html += '</div>';
      /* Tier progress */
      if (hint && tier < 3) {
        html += '<div class="v-card"><h3 style="font-size:0.88em;color:var(--muted);margin-bottom:8px">Next unlock</h3>';
        html += '<p style="font-size:0.85em">' + esc(hint) + '</p>';
        var pct = Math.min(100, Math.round((tier / 3) * 100));
        html += '<div style="background:var(--bg);border-radius:4px;height:6px;margin-top:10px;overflow:hidden"><div style="background:var(--accent);height:100%;width:' + pct + '%;border-radius:4px"></div></div>';
        html += '</div>';
      }
      /* What to do next */
      html += '<div class="v-card"><h3 style="font-size:0.92em;margin-bottom:10px">What to do next</h3>';
      if (tier === 0) {
        html += '<p style="font-size:0.85em;color:var(--muted);margin-bottom:8px">Start by telling Charles about yourself, your goals, and what you\'re working on. He\'ll coordinate the rest of your team.</p>';
        html += '<button class="v-btn" onclick="window._switchAgent(\'charles\');document.getElementById(\'chatInput\').focus();return false;">Talk to Charles</button>';
      } else if (tier === 1) {
        html += '<p style="font-size:0.85em;color:var(--muted);margin-bottom:8px">Great start! Run a check-in to see what your team has prepared for you.</p>';
        html += '<button class="v-btn" onclick="document.getElementById(\'chatInput\').value=\'/check-in\';document.getElementById(\'chatSend\').click();return false;">Run Check-in</button>';
      } else if (tier === 2) {
        html += '<p style="font-size:0.85em;color:var(--muted);margin-bottom:8px">Explore your boards and try talking to a specialist directly.</p>';
        html += '<button class="v-btn" onclick="window.location.hash=\'#/boards\';return false;">View Boards</button>';
      } else {
        html += '<p style="font-size:0.85em;color:var(--muted)">All features unlocked. Check your boards, run councils, or explore scenarios.</p>';
      }
      html += '</div>';
      /* Interview progress card (Tier 0-1 only) */
      if (tier <= 1) {
        html += '<div class="v-card" id="interviewProgressCard"><h3 style="font-size:0.92em;margin-bottom:10px">Getting to Know You</h3>';
        html += '<p style="font-size:0.85em;color:var(--muted);margin-bottom:8px">Switch to Interview Me mode in the chat panel and let your agents ask you a few questions. It helps them personalize everything to you.</p>';
        html += '<div id="interviewProgressBar" style="margin-bottom:8px"></div>';
        html += '<button class="v-btn" onclick="var m=document.getElementById(\'chatMode\');if(m)m.value=\'interview\';window._onChatModeChange(\'interview\');window._switchAgent(\'charles\');document.getElementById(\'chatInput\').focus();return false;">Start Interview</button>';
        html += '</div>';
        /* Load interview progress async */
        fetch('/api/interview/progress').then(function(r) { return r.json(); }).then(function(ip) {
          var bar = document.getElementById('interviewProgressBar');
          if (bar && ip.ok !== false) {
            var pct = ip.completion_pct || 0;
            var completed = ip.completed_questions || 0;
            var total = ip.total_questions || 0;
            if (total > 0) {
              bar.innerHTML = '<div style="font-size:0.78em;color:var(--muted);margin-bottom:4px">' + completed + ' of ' + total + ' questions answered (' + pct + '%)</div>' +
                '<div style="background:var(--bg);border-radius:4px;height:6px;overflow:hidden"><div style="background:var(--accent);height:100%;width:' + pct + '%;border-radius:4px;transition:width 0.3s"></div></div>';
            } else {
              bar.innerHTML = '<div style="font-size:0.78em;color:var(--muted)">No questions answered yet.</div>';
            }
          }
        }).catch(function() {});
      }
      /* Pending doc edits banner */
      html += '<div id="dashEditsBanner"></div>';
      fetch('/api/doc-edits/pending').then(function(r) { return r.json(); }).then(function(ed) {
        var banner = document.getElementById('dashEditsBanner');
        if (!banner || !ed.count) return;
        banner.innerHTML = '<div class="diff-review-banner" onclick="window._openDocEditsReview()">' +
          '<span class="diff-badge">' + ed.count + ' pending</span>' +
          '<div><div style="font-weight:600;font-size:0.9em">Document edits need your review</div>' +
          '<div style="font-size:0.78em;color:var(--muted)">Agents made changes that require your approval</div></div>' +
          '<span style="margin-left:auto;color:var(--muted)">&#10095;</span></div>';
      }).catch(function() {});

      /* Team overview — ALL agents with tier badges */
      html += '<div class="v-card"><h3 style="font-size:0.92em;margin-bottom:10px">Your Team</h3>';
      var deployMode = d.deployment_mode || 'local';
      var tierGroups = [
        { label: 'Chief', filter: function(a) { return a.tier === 'chief'; } },
        { label: 'Strategic', filter: function(a) { return a.tier === 'strategic'; } },
        { label: 'Specialists', filter: function(a) { return a.tier === 'specialist'; } },
        { label: 'Backend', filter: function(a) { return a.tier === 'backend'; } },
      ];
      tierGroups.forEach(function(group) {
        var agents = chatAgents.filter(group.filter);
        if (!agents.length) return;
        html += '<div style="margin-bottom:10px"><div style="font-size:0.72em;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px">' + esc(group.label) + '</div>';
        html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px">';
        agents.forEach(function(a) {
          var isGated = a.tier === 'backend' && deployMode !== 'cloud';
          html += '<div style="display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;background:rgba(188,140,255,0.05);cursor:pointer;' + (isGated ? 'opacity:0.65;' : '') + '" onclick="window._openAgentProfile(\'' + esc(a.id) + '\')">';
          html += '<span style="width:8px;height:8px;border-radius:50%;background:' + agentColor(a.id) + ';flex-shrink:0"></span>';
          html += '<div style="min-width:0;flex:1"><div style="display:flex;align-items:center;gap:5px"><span style="font-size:0.82em;font-weight:600;color:var(--text)">' + esc(a.name) + '</span>';
          if (isGated) html += '<span class="v-badge-cloud">Cloud</span>';
          html += '</div>';
          html += '<div style="font-size:0.7em;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' + esc(a.title) + '</div></div></div>';
        });
        html += '</div></div>';
      });
      html += '</div>';
      el.innerHTML = html;
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Failed to load dashboard: ' + esc(e.message) + '</p></div>';
    });
  }

  /* ── Inline Suggestions (top of page) ─────────────────── */
  function _renderInlineSuggestions(types, containerId) {
    /* types: array of suggestion type strings, e.g. ['activity','challenge'] */
    var promises = types.map(function(t) {
      return fetch('/api/suggestions?status=pending&type=' + t).then(function(r) { return r.json(); });
    });
    Promise.all(promises).then(function(results) {
      var items = [];
      results.forEach(function(d) { items = items.concat(d.suggestions || []); });
      var el = document.getElementById(containerId);
      if (!el) return;
      if (!items.length) { el.innerHTML = ''; return; }
      var html = '<div class="v-card" style="border-left:3px solid var(--accent);margin-bottom:12px">';
      html += '<h3 style="font-size:0.9em;margin-bottom:8px;color:var(--accent)">Pending Suggestions (' + items.length + ')</h3>';
      items.forEach(function(s) {
        html += '<div style="display:flex;align-items:center;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--border)">';
        html += '<div><strong style="font-size:0.88em">' + esc(s.title) + '</strong>';
        html += '<div style="color:var(--muted);font-size:0.78em">' + esc(s.description || '').slice(0, 100) + '</div>';
        html += '<div style="color:var(--muted);font-size:0.7em">from ' + esc(s.agent_id) + '</div></div>';
        html += '<div style="display:flex;gap:4px">';
        html += '<button onclick="_acceptSuggestion(\'' + esc(s.id) + '\',\'' + esc(s.agent_id) + '\')" style="background:var(--green);color:#fff;border:none;border-radius:4px;padding:3px 10px;font-size:0.76em;cursor:pointer">Accept</button>';
        html += '<button onclick="_rejectSuggestion(\'' + esc(s.id) + '\')" style="background:var(--red);color:#fff;border:none;border-radius:4px;padding:3px 10px;font-size:0.76em;cursor:pointer">Reject</button>';
        html += '</div></div>';
      });
      html += '</div>';
      el.innerHTML = html;
    }).catch(function() {});
  }

  /* ── Scenarios ────────────────────────────────────────── */
  function renderScenarios(el) {
    fetch('/api/scenarios').then(function(r) { return r.json(); }).then(function(d) {
      var list = d.scenarios || [];
      var html = '<div id="suggestions-scenarios"></div>';
      if (!list.length) {
        html += '<div class="v-empty"><p>No scenarios available yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Victor will recommend scenarios based on your situation. Keep chatting with your agents to unlock this feature.</p></div>';
        el.innerHTML = html;
        _renderInlineSuggestions(['scenario'], 'suggestions-scenarios');
        return;
      }
      html += '<div class="v-grid">';
      list.forEach(function(s) {
        var name = s.name || s.title || s.id || 'Untitled';
        var desc = s.description || s.overview || '';
        var domain = s.domain || '';
        html += '<div class="v-card" style="cursor:pointer" onclick="window._viewScenario(\'' + esc(s.id) + '\')">';
        html += '<h2>' + esc(name) + '</h2>';
        if (domain) html += '<span class="v-badge v-badge-accent">' + esc(domain) + '</span> ';
        if (desc) html += '<p style="margin-top:8px;font-size:0.88em;color:var(--muted)">' + esc(desc.slice(0, 150)) + '</p>';
        html += '</div>';
      });
      html += '</div>';
      el.innerHTML = html;
      _renderInlineSuggestions(['scenario'], 'suggestions-scenarios');
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Error loading scenarios: ' + esc(e.message) + '</p></div>';
    });
  }

  window._viewScenario = function(id) {
    fetch('/api/scenarios/' + encodeURIComponent(id)).then(function(r) { return r.json(); }).then(function(s) {
      if (s.error) { showToast(s.error); return; }
      var el = $('pageContent');
      var name = s.name || s.title || s.id || 'Untitled';
      var html = '<div class="v-detail-back" onclick="navigate(\'scenarios\')">&larr; Back to Scenarios</div>';
      html += '<div class="v-card"><h2>' + esc(name) + '</h2>';
      if (s.domain) html += '<span class="v-badge v-badge-accent">' + esc(s.domain) + '</span> ';
      if (s.source) html += '<span class="v-badge v-badge-green">' + esc(s.source) + '</span>';
      if (s.description || s.overview) html += '<p style="margin-top:12px;line-height:1.5">' + esc(s.description || s.overview) + '</p>';
      html += '</div>';
      if (s.progression || s.challenges) {
        html += '<div class="v-card"><h2>Details</h2>';
        if (s.progression) html += '<p><strong>Progression:</strong> ' + esc(JSON.stringify(s.progression)) + '</p>';
        html += '</div>';
      }
      html += '<button class="v-btn" onclick="window._startScenario(\'' + esc(id) + '\',\'' + esc(name) + '\')">Start Scenario via Copilot</button>';
      el.innerHTML = html;
    });
  };

  window._startScenario = function(id, name) {
    $('chatInput').value = '/scenario ' + name;
    sendChat();
  };

  /* ── Plans ────────────────────────────────────────────── */
  function renderPlans(el) {
    Promise.all([
      fetch('/api/plans/health').then(function(r) { return r.json(); }),
      fetch('/api/plans/spiritual').then(function(r) { return r.json(); })
    ]).then(function(results) {
      var health = results[0].plans || [];
      var spiritual = results[1].plans || [];
      var html = '<div id="suggestions-plans"></div>';
      html += '<div class="v-tabs" id="planTabs">';
      html += '<button class="v-tab active" onclick="window._switchPlanTab(\'health\')">Health Plans (' + health.length + ')</button>';
      html += '<button class="v-tab" onclick="window._switchPlanTab(\'spiritual\')">Spiritual Plans (' + spiritual.length + ')</button>';
      html += '</div>';
      html += '<div class="v-tab-pane active" id="pane-health">';
      if (!health.length) {
        html += '<div class="v-empty"><p>No health plans yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Health plans are created as you work with your agents. Complete onboarding first.</p></div>';
      } else {
        html += '<div class="v-grid">';
        health.forEach(function(p) {
          var rawName = p.name || p.title || p.id || 'Untitled';
          var name = rawName.replace(/_/g, ' ').replace(/\b\w/g, function(c) { return c.toUpperCase(); });
          var desc = p.description || p.overview || p.goal || '';
          html += '<div class="v-card" style="cursor:pointer" onclick="window._viewPlan(\'health\',\'' + esc(p.id) + '\')">';
          html += '<h2>' + esc(name) + '</h2>';
          if (desc) html += '<p style="font-size:0.85em;color:var(--muted);margin-top:6px">' + esc(desc.slice(0, 120)) + '</p>';
          html += '</div>';
        });
        html += '</div>';
      }
      html += '</div>';
      html += '<div class="v-tab-pane" id="pane-spiritual">';
      if (!spiritual.length) {
        html += '<div class="v-empty"><p>No spiritual plans yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Spiritual plans are unlocked after deeper onboarding. Keep working with Victor and your agents.</p></div>';
      } else {
        html += '<div class="v-grid">';
        spiritual.forEach(function(p) {
          var rawName = p.name || p.title || p.archetype || p.id || 'Untitled';
          var name = rawName.replace(/_/g, ' ').replace(/\b\w/g, function(c) { return c.toUpperCase(); });
          var desc = p.description || p.overview || p.archetype || '';
          var bucket = p.bucket || '';
          html += '<div class="v-card" style="cursor:pointer" onclick="window._viewPlan(\'spiritual\',\'' + esc(p.id) + '\')">';
          html += '<h2>' + esc(name) + '</h2>';
          if (bucket) html += '<span class="v-badge v-badge-yellow">' + esc(bucket) + '</span> ';
          if (desc) html += '<p style="font-size:0.85em;color:var(--muted);margin-top:6px">' + esc(desc.slice(0, 120)) + '</p>';
          html += '</div>';
        });
        html += '</div>';
      }
      html += '</div>';
      el.innerHTML = html;
      _renderInlineSuggestions(['plan'], 'suggestions-plans');
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
    });
  }

  window._switchPlanTab = function(tab) {
    document.querySelectorAll('#planTabs .v-tab').forEach(function(b, i) {
      b.classList.toggle('active', (tab === 'health' ? i === 0 : i === 1));
    });
    var h = document.getElementById('pane-health');
    var s = document.getElementById('pane-spiritual');
    if (h) h.classList.toggle('active', tab === 'health');
    if (s) s.classList.toggle('active', tab === 'spiritual');
  };

  window._viewPlan = function(type, id) {
    var url = '/api/plans/' + type + '/' + encodeURIComponent(id);
    fetch(url).then(function(r) { return r.json(); }).then(function(p) {
      if (p.error) { showToast(p.error); return; }
      var el = $('pageContent');
      var name = p.name || p.title || p.id || id;
      var html = '<div class="v-detail-back" onclick="navigate(\'plans\')">&larr; Back to Plans</div>';
      html += '<div class="v-card"><h2>' + esc(name) + '</h2>';
      if (p.description || p.overview) html += '<p style="margin-top:10px;line-height:1.5">' + esc(p.description || p.overview) + '</p>';
      if (p.goal) html += '<p style="margin-top:8px"><strong>Goal:</strong> ' + esc(p.goal) + '</p>';
      if (p.archetype) html += '<p style="margin-top:6px"><strong>Archetype:</strong> ' + esc(p.archetype) + '</p>';
      if (p.bucket) html += '<span class="v-badge v-badge-yellow">' + esc(p.bucket) + '</span> ';
      if (p.overlay) html += '<span class="v-badge v-badge-accent">' + esc(p.overlay) + '</span>';
      html += '</div>';
      /* Show milestones or phases if available */
      var phases = p.phases || p.milestones || p.stages || [];
      if (Array.isArray(phases) && phases.length) {
        html += '<div class="v-card"><h2>Phases / Milestones</h2>';
        phases.forEach(function(ph, i) {
          var label = ph.name || ph.title || ph.label || ('Phase ' + (i + 1));
          var desc = ph.description || ph.goal || '';
          html += '<div class="v-list-item" style="cursor:default"><h3>' + esc(label) + '</h3>';
          if (desc) html += '<p>' + esc(desc) + '</p>';
          html += '</div>';
        });
        html += '</div>';
      }
      el.innerHTML = html;
    });
  };

  /* ── Metrics ──────────────────────────────────────────── */
  function renderMetrics(el) {
    Promise.all([
      fetch('/api/metrics/definitions').then(function(r) { return r.json(); }),
      fetch('/api/metrics/baselines').then(function(r) { return r.json(); }),
      fetch('/api/metrics/blueprints').then(function(r) { return r.json(); }),
      fetch('/api/metrics/domain-sets').then(function(r) { return r.json(); })
    ]).then(function(res) {
      var defs = res[0].definitions || {};
      var baselines = (res[1].defaults || res[1]);
      var blueprints = res[2].blueprints || [];
      var domainSets = res[3] || {};
      var html = '<div class="v-tabs" id="metricTabs">';
      html += '<button class="v-tab active" onclick="window._switchMetricTab(\'defs\')">Definitions (' + Object.keys(defs).length + ')</button>';
      html += '<button class="v-tab" onclick="window._switchMetricTab(\'blueprints\')">Blueprints (' + blueprints.length + ')</button>';
      html += '<button class="v-tab" onclick="window._switchMetricTab(\'domains\')">Domain Sets</button>';
      html += '</div>';
      /* Definitions tab */
      html += '<div class="v-tab-pane active" id="mpane-defs"><div class="v-card">';
      var defKeys = Object.keys(defs);
      if (!defKeys.length) {
        html += '<div class="v-empty"><p>No metric definitions yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Jake will set up your metrics as you work with the team. Try <code>/checkin</code> in the Copilot.</p></div>';
      } else {
        defKeys.forEach(function(k) {
          var m = defs[k];
          var base = baselines && baselines[k] !== undefined ? baselines[k] : '';
          html += '<div class="v-metric-row">';
          html += '<div class="v-metric-name"><strong>' + esc(m.name || k) + '</strong>';
          if (m.category) html += ' <span class="v-badge v-badge-accent">' + esc(m.category) + '</span>';
          if (m.description) html += '<div style="font-size:0.8em;color:var(--muted);margin-top:2px">' + esc(m.description) + '</div>';
          html += '</div>';
          var baseStr = base !== '' ? (typeof base === 'object' && base !== null ? (base.target !== undefined ? 'Target: ' + base.target : JSON.stringify(base)) : esc(String(base))) : '-';
          html += '<div class="v-metric-val">' + baseStr + '</div>';
          html += '</div>';
        });
      }
      html += '</div></div>';
      /* Blueprints tab */
      html += '<div class="v-tab-pane" id="mpane-blueprints">';
      if (!blueprints.length) {
        html += '<div class="v-empty"><p>No blueprints found.</p></div>';
      } else {
        html += '<div class="v-grid">';
        blueprints.forEach(function(bp) {
          var name = bp.name || bp.title || bp.id || 'Blueprint';
          var desc = bp.description || bp.philosophy || '';
          html += '<div class="v-card"><h2>' + esc(name) + '</h2>';
          if (desc) html += '<p style="font-size:0.85em;color:var(--muted);margin-top:6px">' + esc(desc.slice(0, 150)) + '</p>';
          if (bp.metrics) {
            html += '<div style="margin-top:10px">';
            Object.keys(bp.metrics).slice(0, 6).forEach(function(mk) {
              html += '<div class="v-metric-row"><span class="v-metric-name">' + esc(mk) + '</span>';
              var mv = bp.metrics[mk];
              var mvStr = typeof mv === 'object' && mv !== null ? (mv.target !== undefined ? 'Target: ' + mv.target : JSON.stringify(mv)) : esc(String(mv));
              html += '<span class="v-metric-val">' + mvStr + '</span></div>';
            });
            html += '</div>';
          }
          html += '</div>';
        });
        html += '</div>';
      }
      html += '</div>';
      /* Domain sets tab */
      html += '<div class="v-tab-pane" id="mpane-domains"><div class="v-card">';
      var jake = domainSets.jake_metrics || [];
      var kiran = domainSets.kiran_metrics || [];
      html += '<h2>Jake Metrics (' + jake.length + ')</h2>';
      if (jake.length) {
        jake.forEach(function(m) { html += '<span class="v-badge v-badge-accent" style="margin:3px">' + esc(m) + '</span>'; });
      } else { html += '<p style="color:var(--muted)">None configured</p>'; }
      html += '<h2 style="margin-top:16px">Kiran (Ojas) Metrics (' + kiran.length + ')</h2>';
      if (kiran.length) {
        kiran.forEach(function(m) { html += '<span class="v-badge v-badge-green" style="margin:3px">' + esc(m) + '</span>'; });
      } else { html += '<p style="color:var(--muted)">None configured</p>'; }
      html += '</div></div>';
      el.innerHTML = html;
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
    });
  }

  window._switchMetricTab = function(tab) {
    document.querySelectorAll('#metricTabs .v-tab').forEach(function(b, i) {
      b.classList.toggle('active', (tab === 'defs' ? i === 0 : tab === 'blueprints' ? i === 1 : i === 2));
    });
    ['defs', 'blueprints', 'domains'].forEach(function(t) {
      var p = document.getElementById('mpane-' + t);
      if (p) p.classList.toggle('active', t === tab);
    });
  };

  /* ── Meetings ─────────────────────────────────────────── */
  function renderMeetings(el) {
    fetch('/api/meetings').then(function(r) { return r.json(); }).then(function(d) {
      var meetings = d.meetings || [];
      var html = '';

      /* Upload drop zone */
      html += '<div class="v-card" id="meetingDropZone" style="border:2px dashed var(--border);text-align:center;padding:24px;cursor:pointer;transition:border-color 0.2s">';
      html += '<div style="font-size:1.4em;margin-bottom:6px">&#128196;</div>';
      html += '<p style="font-weight:600;margin-bottom:4px">Upload Transcript or AI Summary</p>';
      html += '<p style="color:var(--muted);font-size:0.82em">Drag and drop .txt, .md, .vtt, .srt, .json files here, or click to browse</p>';
      html += '<input type="file" id="meetingFileInput" accept=".txt,.md,.vtt,.srt,.json,.yaml" style="display:none" multiple>';
      html += '<div id="meetingUploadStatus" style="margin-top:8px;font-size:0.82em;min-height:1.2em"></div>';
      html += '</div>';

      /* Meeting list */
      html += '<div class="v-card"><h2>Meetings</h2>';
      if (!meetings.length) {
        html += '<div class="v-empty"><p>No meetings yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Upload transcripts above and Maya will extract key signals automatically.</p></div>';
      } else {
        meetings.forEach(function(m) {
          var date = m.modified ? new Date(m.modified * 1000).toLocaleDateString() : '';
          html += '<div class="v-list-item">';
          html += '<h3>' + esc(m.name) + '</h3><p>' + esc(date) + ' &middot; ' + esc(m.file) + '</p>';
          html += '</div>';
        });
      }
      html += '</div>';
      el.innerHTML = html;

      /* Wire up drag-and-drop + click */
      var dz = document.getElementById('meetingDropZone');
      var fi = document.getElementById('meetingFileInput');
      var st = document.getElementById('meetingUploadStatus');
      if (dz && fi) {
        dz.addEventListener('click', function() { fi.click(); });
        dz.addEventListener('dragover', function(e) { e.preventDefault(); dz.style.borderColor = 'var(--accent)'; });
        dz.addEventListener('dragleave', function() { dz.style.borderColor = 'var(--border)'; });
        dz.addEventListener('drop', function(e) {
          e.preventDefault(); dz.style.borderColor = 'var(--border)';
          if (e.dataTransfer.files.length) _uploadMeetingFiles(e.dataTransfer.files, st);
        });
        fi.addEventListener('change', function() { if (fi.files.length) _uploadMeetingFiles(fi.files, st); });
      }
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
    });
  }

  function _uploadMeetingFiles(files, statusEl) {
    var pending = files.length;
    var done = 0;
    if (statusEl) statusEl.innerHTML = '<span style="color:var(--accent)">Uploading ' + pending + ' file(s)...</span>';
    for (var i = 0; i < files.length; i++) {
      (function(file) {
        var fd = new FormData();
        fd.append('file', file);
        fetch('/api/meetings/upload', { method: 'POST', body: fd })
          .then(function(r) { return r.json(); })
          .then(function(d) {
            done++;
            if (d.ok) {
              if (done === pending) {
                if (statusEl) statusEl.innerHTML = '<span style="color:var(--green)">Uploaded ' + done + ' file(s). Maya is extracting signals...</span>';
                setTimeout(function() { renderMeetings(document.getElementById('pageContent')); }, 2000);
              }
            } else {
              if (statusEl) statusEl.innerHTML = '<span style="color:var(--red)">Error: ' + esc(d.error || 'Upload failed') + '</span>';
            }
          })
          .catch(function(e) {
            if (statusEl) statusEl.innerHTML = '<span style="color:var(--red)">Error: ' + esc(e.message) + '</span>';
          });
      })(files[i]);
    }
  }

  /* ── Councils ─────────────────────────────────────────── */
  function renderCouncils(el) {
    Promise.all([
      fetch('/api/councils/mentors').then(function(r) { return r.json(); }),
      fetch('/api/councils/sessions').then(function(r) { return r.json(); })
    ]).then(function(res) {
      var mentors = res[0].mentors || [];
      var sessions = res[1].sessions || [];
      var html = '<div id="suggestions-councils"></div>';
      /* Start council form */
      html += '<div class="v-card"><h2>Start Council</h2>';
      html += '<div class="v-form-row"><label>Topic</label><input type="text" id="councilTopic" placeholder="e.g. Career pivot decision"></div>';
      html += '<div class="v-form-row"><label>Council Type</label><select id="councilType"><option value="ad_hoc">Ad hoc (quick)</option><option value="full_council">Full Council</option></select></div>';
      html += '<button class="v-btn" id="startCouncilBtn" onclick="window._startCouncil()">Start Council</button></div>';
      /* Mentor grid */
      html += '<div class="v-card"><h2>Mentor Roster</h2>';
      html += '<p style="color:var(--muted);font-size:0.82em;margin-bottom:12px">Suggested by Victor based on your profile. Click to consult.</p>';
      if (!mentors.length) {
        html += '<div class="v-empty"><p>No mentors yet. Victor will suggest mentors as he learns about you. You can also ask: "I\'d like a mentor who specializes in..."</p></div>';
      } else {
        html += '<div class="v-grid">';
        mentors.forEach(function(m) {
          html += '<div class="v-list-item" style="cursor:pointer" onclick="window._chatMentor(\'' + esc(m.id) + '\')">';
          html += '<h3>' + esc(m.name) + ' <span class="v-badge v-badge-yellow" style="font-size:0.65em;vertical-align:middle">Suggested</span></h3>';
          html += '<p>' + esc(m.archetype || '') + '</p>';
          html += '</div>';
        });
        html += '</div>';
      }
      html += '</div>';
      /* History */
      html += '<div class="v-card"><h2>Council History</h2>';
      if (!sessions.length) {
        html += '<div class="v-empty"><p>No council sessions yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Start a council by typing <code>/council [topic]</code> in the Copilot or use the form above.</p></div>';
      } else {
        sessions.forEach(function(s) {
          html += '<div class="v-list-item">';
          html += '<h3>' + esc(s.topic || 'Untitled') + '</h3>';
          html += '<p>' + esc(s.council_type || '') + ' &middot; ' + esc((s.created_at || '').slice(0, 19)) + '</p>';
          if (s.synthesis) html += '<p style="margin-top:6px;font-size:0.85em">' + esc(s.synthesis.slice(0, 200)) + '</p>';
          html += '</div>';
        });
      }
      html += '</div>';
      el.innerHTML = html;
      _renderInlineSuggestions(['council', 'mentor'], 'suggestions-councils');
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
    });
  }

  window._startCouncil = function() {
    var topic = (document.getElementById('councilTopic') || {}).value;
    var ctype = (document.getElementById('councilType') || {}).value;
    if (!topic || !topic.trim()) { showToast('Enter a topic'); return; }
    var btn = document.getElementById('startCouncilBtn');
    if (btn) { btn.disabled = true; btn.innerHTML = '&#x23F3; Council convening...'; }
    /* Show loading state in the page */
    var loadingEl = document.createElement('div');
    loadingEl.className = 'v-card';
    loadingEl.id = 'council-loading';
    loadingEl.innerHTML = '<div style="text-align:center;padding:24px"><div style="font-size:1.4em;margin-bottom:8px">&#x1F9E0;</div><p style="color:var(--accent);font-weight:600">Council is convening...</p><p style="color:var(--muted);font-size:0.85em;margin-top:6px">Your mentors are deliberating on: <strong>' + esc(topic.trim()) + '</strong></p><p style="color:var(--muted);font-size:0.78em;margin-top:4px">This typically takes 30-60 seconds.</p></div>';
    var content = $('pageContent');
    if (content) content.insertBefore(loadingEl, content.firstChild);
    fetch('/api/councils/start', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({topic: topic.trim(), council_type: ctype})
    }).then(function(r) { return r.json(); }).then(function(d) {
      var el = document.getElementById('council-loading');
      if (el) el.remove();
      if (d.ok) {
        /* Show synthesis prominently */
        var resultCard = document.createElement('div');
        resultCard.className = 'v-card';
        resultCard.style.borderLeft = '4px solid var(--accent)';
        var rhtml = '<h2 style="margin-bottom:12px">Council Result: ' + esc(topic.trim()) + '</h2>';
        /* Per-mentor perspectives if available */
        if (d.perspectives && d.perspectives.length) {
          d.perspectives.forEach(function(p) {
            rhtml += '<div style="margin-bottom:10px;padding:8px;background:var(--bg);border-radius:6px"><strong style="color:var(--accent);font-size:0.88em">' + esc(p.mentor || p.name || 'Mentor') + '</strong><p style="font-size:0.85em;margin-top:4px">' + esc(p.response || p.perspective || '') + '</p></div>';
          });
        }
        if (d.synthesis) {
          rhtml += '<div style="margin-top:12px;padding:12px;background:rgba(88,166,255,0.08);border-radius:6px"><strong style="font-size:0.88em">Synthesis</strong><p style="font-size:0.88em;margin-top:6px;white-space:pre-wrap">' + esc(d.synthesis) + '</p></div>';
        }
        rhtml += '<button class="v-btn" style="margin-top:12px" onclick="document.getElementById(\'chatInput\').value=\'Continue the council discussion about: ' + esc(topic.trim()).replace(/'/g, "\\'") + '\';document.getElementById(\'chatSend\').click()">Continue in Copilot</button>';
        resultCard.innerHTML = rhtml;
        if (content) content.insertBefore(resultCard, content.firstChild);
        if (d.synthesis) appendChatMsg('agent', d.synthesis, 'council');
      } else {
        showToast('Error: ' + (d.error || 'Failed'));
      }
      if (btn) { btn.disabled = false; btn.textContent = 'Start Council'; }
    }).catch(function(e) {
      var el = document.getElementById('council-loading');
      if (el) el.remove();
      showToast('Error: ' + e.message);
      if (btn) { btn.disabled = false; btn.textContent = 'Start Council'; }
    });
  };

  window._chatMentor = function(id) {
    /* Send a natural language request to Charles who will route to Sage for facilitation */
    var inp = $('chatInput');
    inp.value = "I'd like to consult with " + id + " about ";
    inp.focus();
  };

  /* ── Documents ───────────────────────────────────────── */
  var _docCurrentAgent = '';
  var _docCurrentName = '';

  function renderDocuments(el) {
    fetch('/api/agent-docs').then(function(r) { return r.json(); }).then(function(agents) {
      var html = '<div class="doc-viewer">';
      /* Left tree */
      html += '<div class="doc-tree">';
      html += '<div class="doc-tree-header">Agent Documents</div>';
      agents.forEach(function(ag) {
        if (!ag.docs || !ag.docs.length) return;
        html += '<div class="doc-agent-group">';
        html += '<div class="doc-agent-name" onclick="this.parentNode.classList.toggle(\'collapsed\')">';
        html += '<span class="doc-agent-arrow">&#x25BE;</span> ';
        html += '<span style="color:' + agentColor(ag.agent_id) + '">' + esc(ag.name) + '</span>';
        html += '<span class="doc-agent-count">' + ag.docs.length + '</span>';
        html += '</div>';
        html += '<div class="doc-file-list">';
        ag.docs.forEach(function(d) {
          var cls = d.exists ? 'doc-file' : 'doc-file doc-missing';
          html += '<div class="' + cls + '" data-agent="' + esc(ag.agent_id) + '" data-doc="' + esc(d.name) + '" onclick="window._openDoc(\'' + esc(ag.agent_id) + '\',\'' + esc(d.name) + '\')">';
          html += '<span class="doc-file-icon">' + (d.exists ? '&#x1F4C4;' : '&#x1F6AB;') + '</span> ';
          html += esc(d.name.replace('.md', ''));
          html += '</div>';
        });
        html += '</div></div>';
      });
      html += '</div>';
      /* Right content */
      html += '<div class="doc-content" id="docContentArea">';
      html += '<div class="doc-placeholder">';
      html += '<div style="font-size:2em;margin-bottom:12px">&#x1F4C4;</div>';
      html += '<h3>Select a document</h3>';
      html += '<p style="color:var(--muted);font-size:0.88em;margin-top:8px">Choose a document from the tree on the left. The owning agent will brief you on its contents in the chat panel.</p>';
      html += '</div>';
      html += '</div>';
      html += '</div>';
      el.innerHTML = html;
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Error loading documents: ' + esc(e.message) + '</p></div>';
    });
  }

  window._openDoc = function(agentId, docName) {
    /* Redirect to the new tab system */
    _docCurrentAgent = agentId;
    _docCurrentName = docName;
    window._openDocTab(agentId, docName);
  };

  function renderMarkdown(md) {
    /* Lightweight markdown to HTML — headings, bold, italic, code, lists, links, tables, hr */
    var lines = md.split('\n');
    var html = '';
    var inList = false;
    var inOl = false;
    var inTable = false;
    var inCode = false;
    for (var i = 0; i < lines.length; i++) {
      var line = lines[i];
      /* Fenced code blocks */
      if (line.trim().match(/^```/)) {
        if (inCode) { html += '</pre>'; inCode = false; }
        else { html += '<pre>'; inCode = true; }
        continue;
      }
      if (inCode) { html += esc(line) + '\n'; continue; }
      /* Table */
      if (line.trim().match(/^\|.*\|$/)) {
        if (line.trim().match(/^\|[\s\-:|]+\|$/)) continue; /* separator */
        if (!inTable) { html += '<table class="doc-md-table">'; inTable = true; }
        var cells = line.trim().split('|').filter(function(c) { return c.trim() !== ''; });
        html += '<tr>';
        cells.forEach(function(c) { html += '<td>' + inlineFormat(c.trim()) + '</td>'; });
        html += '</tr>';
        continue;
      } else if (inTable) { html += '</table>'; inTable = false; }
      /* Close lists */
      if (inList && !line.match(/^\s*[-*]\s/)) { html += '</ul>'; inList = false; }
      if (inOl && !line.match(/^\s*\d+\.\s/)) { html += '</ol>'; inOl = false; }
      /* Headings */
      var hm = line.match(/^(#{1,6})\s+(.+)/);
      if (hm) { var lvl = hm[1].length; html += '<h' + lvl + '>' + inlineFormat(hm[2]) + '</h' + lvl + '>'; continue; }
      /* Horizontal rule */
      if (line.trim().match(/^[-*_]{3,}$/)) { html += '<hr>'; continue; }
      /* Unordered list */
      var ul = line.match(/^\s*[-*]\s+(.+)/);
      if (ul) { if (!inList) { html += '<ul>'; inList = true; } html += '<li>' + inlineFormat(ul[1]) + '</li>'; continue; }
      /* Ordered list */
      var ol = line.match(/^\s*\d+\.\s+(.+)/);
      if (ol) { if (!inOl) { html += '<ol>'; inOl = true; } html += '<li>' + inlineFormat(ol[1]) + '</li>'; continue; }
      /* Empty line */
      if (!line.trim()) { html += '<br>'; continue; }
      /* Paragraph */
      html += '<p>' + inlineFormat(line) + '</p>';
    }
    if (inList) html += '</ul>';
    if (inOl) html += '</ol>';
    if (inTable) html += '</table>';
    if (inCode) html += '</pre>';
    return html;
  }

  function inlineFormat(s) {
    s = esc(s);
    s = s.replace(/`([^`]+)`/g, '<code>$1</code>');
    s = s.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
    s = s.replace(/\*(.+?)\*/g, '<em>$1</em>');
    s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
    return s;
  }

  var _docBriefInFlight = false;
  window._docAutoBrief = function(agentId, docName) {
    /* Switch chat agent */
    var chatAgentEl = $('chatAgent');
    if (chatAgentEl && chatAgentEl.value !== agentId) {
      chatAgentEl.value = agentId;
      if (window._onAgentSwitch) window._onAgentSwitch(agentId);
    }
    /* Don't send another brief if one is already in flight */
    if (_docBriefInFlight) return;
    /* Send auto-brief message */
    var briefMsg = 'I just opened ' + docName + '. Brief me on the latest changes and current status of this document.';
    appendChatMsg('user', briefMsg, null, null);
    _cacheMsg(agentId, 'user', briefMsg);
    chatTyping.classList.add('show');
    _docBriefInFlight = true;
    fetch('/api/chat', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({message: briefMsg, agent_id: agentId})
    }).then(function(r) { return r.json(); }).then(function(d) {
      chatTyping.classList.remove('show');
      _docBriefInFlight = false;
      if (d.ok) {
        var respAgent = d.routed_to || d.agent_id || agentId;
        appendChatMsg('agent', d.response || '(no response)', respAgent, d.routed_to);
        _cacheMsg(respAgent, 'assistant', d.response || '');
      } else {
        appendChatMsg('system', 'Error: ' + (d.error || d.response || 'Could not get a response. LLM may be unavailable.'), 'system', null);
      }
    }).catch(function(e) {
      chatTyping.classList.remove('show');
      _docBriefInFlight = false;
      appendChatMsg('system', 'Error: ' + (e.message || 'Network error'), 'system', null);
    });
  };

  window._editDocInChat = function() {
    if (!_docCurrentAgent || !_docCurrentName) return;
    var inp = $('chatInput');
    if (!inp) return;
    /* Clear any stuck typing indicator */
    chatTyping.classList.remove('show');
    var chatAgentEl = $('chatAgent');
    if (chatAgentEl) chatAgentEl.value = _docCurrentAgent;
    inp.value = 'Update ' + _docCurrentName + ': ';
    inp.focus();
  };

  window._refreshDoc = function() {
    if (_docCurrentAgent && _docCurrentName) {
      window._openDocTab(_docCurrentAgent, _docCurrentName);
    }
  };

  /* ── Personas ─────────────────────────────────────────── */
  function renderPersonas(el) {
    var html = '<div id="suggestions-personas"></div>';
    html += '<div class="v-tabs" id="personaTabs">';
    html += '<button class="v-tab active" onclick="window._switchPersonaTab(\'radar\')">Radar Chart</button>';
    html += '<button class="v-tab" onclick="window._switchPersonaTab(\'roleplay\')">Roleplay</button>';
    html += '</div>';
    html += '<div class="v-tab-pane active" id="ptab-radar"><div class="v-loading">Loading radar chart...</div></div>';
    html += '<div class="v-tab-pane" id="ptab-roleplay"></div>';
    el.innerHTML = html;
    _renderInlineSuggestions(['activity', 'challenge'], 'suggestions-personas');
    /* Load radar via fetch-inject from /persona */
    fetch('/persona').then(function(r) { return r.text(); }).then(function(h) {
      var doc = new DOMParser().parseFromString(h, 'text/html');
      var styles = '';
      doc.querySelectorAll('style').forEach(function(s) { styles += s.textContent; });
      var body = doc.body ? doc.body.innerHTML : '';
      var container = document.getElementById('ptab-radar');
      container.innerHTML = '<style>' + styles + '</style><div>' + body + '</div>';
      container.querySelectorAll('script').forEach(function(old) {
        var s = document.createElement('script');
        s.textContent = old.textContent;
        old.parentNode.replaceChild(s, old);
      });
    }).catch(function() {
      var c = document.getElementById('ptab-radar');
      if (c) c.innerHTML = '<div class="v-empty"><p>Radar chart unavailable. Start with <code>vastian serve</code></p></div>';
    });
    /* Roleplay tab */
    var rp = document.getElementById('ptab-roleplay');
    rp.innerHTML = '<div class="v-card"><h2>Persona Roleplay</h2><p style="color:var(--muted);margin-bottom:12px">Interactive roleplay led by your persona allies. Story-driven scenarios from your personal growth journey.</p><button class="v-btn" onclick="window._chatMentor(\'adrian\')">Start Roleplay via Copilot</button></div>';
    /* Activities */
    var _actPage = 0, _allActs = [], _allTypes = [];
    fetch('/api/personas/activities').then(function(r) { return r.json(); }).then(function(d) {
      _allActs = d.activities || [];
      _allTypes = d.activity_types || [];
      var container = document.getElementById('ptab-activities');
      if (!_allActs.length && !_allTypes.length) {
        container.innerHTML = '<div class="v-empty"><p>No activities yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Activities are suggested by your agents based on your progress. Start with a session to unlock personalized activities.</p></div>';
        return;
      }
      var h = '';
      if (_allTypes.length) {
        h += '<div class="v-card"><h2 style="color:var(--accent)">Activity Types (' + _allTypes.length + ')</h2><div style="display:flex;flex-wrap:wrap;gap:6px">';
        _allTypes.forEach(function(t) {
          var cat = t.category || t.name || t.id || 'Unknown';
          var desc = t.description || '';
          h += '<div class="v-badge v-badge-accent" style="margin:2px;cursor:help" title="' + esc(desc) + '">' + esc(cat) + '</div>';
        });
        h += '</div></div>';
      }
      if (_allActs.length) {
        h += '<div class="v-card"><h2 style="color:var(--accent)">Activities (' + _allActs.length + ')</h2><div id="actList"></div>';
        h += '<div id="actLoadMore" style="margin-top:12px;text-align:center"></div></div>';
      }
      container.innerHTML = h;
      _actPage = 0;
      window._loadMoreActs();
    }).catch(function() {
      var c = document.getElementById('ptab-activities');
      if (c) c.innerHTML = '<div class="v-empty"><p>Activities unavailable.</p></div>';
    });
    window._loadMoreActs = function() {
      var PER = 30;
      var start = _actPage * PER;
      var slice = _allActs.slice(start, start + PER);
      var el = document.getElementById('actList');
      if (!el) return;
      slice.forEach(function(a) {
        var type = a.activity_type || '';
        var framework = a.obvious_frameworks || a.creative_frameworks || '';
        var row = document.createElement('div');
        row.className = 'v-metric-row';
        row.innerHTML = '<div style="flex:1;min-width:0"><span class="v-metric-name">' + esc(type) + '</span>' +
          '<div style="font-size:0.78em;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' + esc(framework) + '</div></div>' +
          (a.tier ? '<span class="v-badge v-badge-green">Tier ' + a.tier + '</span>' : '');
        el.appendChild(row);
      });
      _actPage++;
      var btn = document.getElementById('actLoadMore');
      if (btn) {
        if (_actPage * PER < _allActs.length) {
          btn.innerHTML = '<button class="v-btn" onclick="window._loadMoreActs()">Load more (' + (_allActs.length - _actPage * PER) + ' remaining)</button>';
        } else {
          btn.innerHTML = '';
        }
      }
    };
    /* Challenges */
    var _chalPage = 0, _allChals = [];
    fetch('/api/personas/challenges').then(function(r) { return r.json(); }).then(function(d) {
      _allChals = d.challenges || [];
      var container = document.getElementById('ptab-challenges');
      if (!_allChals.length) {
        container.innerHTML = '<div class="v-empty"><p>No challenges yet.</p><p style="color:var(--muted);font-size:0.88em;margin-top:8px">Challenges are suggested by your agents based on your knowledge profile. Keep working with your agents to unlock them.</p></div>';
        return;
      }
      var h = '<div class="v-card"><h2 style="color:var(--accent)">Challenges (' + _allChals.length + ')</h2><div id="chalList"></div>';
      h += '<div id="chalLoadMore" style="margin-top:12px;text-align:center"></div></div>';
      container.innerHTML = h;
      _chalPage = 0;
      window._loadMoreChals();
    }).catch(function() {
      var c = document.getElementById('ptab-challenges');
      if (c) c.innerHTML = '<div class="v-empty"><p>Challenges unavailable.</p></div>';
    });
    window._loadMoreChals = function() {
      var PER = 20;
      var start = _chalPage * PER;
      var slice = _allChals.slice(start, start + PER);
      var el = document.getElementById('chalList');
      if (!el) return;
      slice.forEach(function(c) {
        var setup = c.setup || 'Challenge';
        var text = c.challenge_text || '';
        var type = c.activity_type || '';
        var fmt = c.format || '';
        var item = document.createElement('div');
        item.className = 'v-list-item';
        item.style.cursor = 'default';
        item.innerHTML = '<h3 style="font-size:0.88em;margin-bottom:4px">' + esc(setup) + '</h3>' +
          (text ? '<p style="color:var(--muted);font-size:0.82em;margin-bottom:6px">' + esc(text.slice(0, 150)) + (text.length > 150 ? '...' : '') + '</p>' : '') +
          '<div style="display:flex;gap:6px;flex-wrap:wrap">' +
          (type ? '<span class="v-badge v-badge-accent">' + esc(type) + '</span>' : '') +
          (fmt ? '<span class="v-badge v-badge-green">' + esc(fmt) + '</span>' : '') +
          '</div>';
        el.appendChild(item);
      });
      _chalPage++;
      var btn = document.getElementById('chalLoadMore');
      if (btn) {
        if (_chalPage * PER < _allChals.length) {
          btn.innerHTML = '<button class="v-btn" onclick="window._loadMoreChals()">Load more (' + (_allChals.length - _chalPage * PER) + ' remaining)</button>';
        } else {
          btn.innerHTML = '';
        }
      }
    };
  }

  window._switchPersonaTab = function(tab) {
    document.querySelectorAll('#personaTabs .v-tab').forEach(function(b, i) {
      b.classList.toggle('active', (tab === 'radar' ? i === 0 : tab === 'roleplay' ? i === 1 : tab === 'activities' ? i === 2 : i === 3));
    });
    ['radar', 'roleplay', 'activities', 'challenges'].forEach(function(t) {
      var p = document.getElementById('ptab-' + t);
      if (p) p.classList.toggle('active', t === tab);
    });
  };

  /* ── Chat system ──────────────────────────────────────── */
  var chatMsgs = $('chatMessages');
  var chatInput = $('chatInput');
  var chatSend = $('chatSend');
  var chatAgent = $('chatAgent');
  var cmdPalette = $('cmdPalette');
  var chatTyping = $('chatTyping');

  /* Per-agent chat thread cache: chatThreads[agentId] = [{role, content, timestamp}, ...] */
  var chatThreads = {};
  var _historyLoaded = {};

  /* Per-agent color map */
  var AGENT_COLORS = {
    charles: '#58a6ff', dominic: '#bc8cff', liam: '#3fb950', henry: '#f0883e',
    finley: '#56d364', theo: '#d2a8ff', elias: '#79c0ff', victor: '#a371f7',
    adrian: '#ffa657', concord: '#7ee787', jake: '#39d353', orion: '#e3b341',
    rowan: '#ff7b72', arjuna: '#58a6ff', maya: '#f778ba', noah: '#79c0ff',
    felix: '#3fb950', maria: '#d29922', sage: '#a5d6ff', kiran: '#ff9bce',
    system: '#d29922', council: '#bc8cff'
  };
  function agentColor(id) { return AGENT_COLORS[id] || '#8b949e'; }

  function relativeTime() {
    return new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
  }

  function appendChatMsg(role, text, agentId, routedTo) {
    var div = document.createElement('div');
    div.className = 'chat-msg ' + role;
    var tier = _agentTierMap[agentId] || 'background';
    if (role === 'agent' || role === 'system') div.setAttribute('data-tier', tier);
    if (agentId) div.style.borderLeftColor = agentColor(agentId);

    var displayName = _agentNameMap[agentId] || agentId || '';
    var tierLabel = tier === 'chief' ? ' &#9733;' : '';

    var header = '<div class="msg-header">';
    if (role === 'user') {
      header += '<span class="agent-name" style="color:var(--muted)">You</span>';
    } else if (agentId) {
      header += '<span class="agent-name" style="color:' + agentColor(agentId) + '">' + esc(displayName) + tierLabel + '</span>';
      if (routedTo && routedTo !== agentId) {
        var routedName = _agentNameMap[routedTo] || routedTo;
        header += '<span class="msg-route">&#8594; <span style="color:' + agentColor(routedTo) + '">' + esc(routedName) + '</span></span>';
      }
    }
    header += '<span class="msg-time">' + relativeTime() + '</span></div>';

    var formatted = formatMsg(text);
    var bodyId = 'msg-' + Date.now();
    var lineCount = (text || '').split('\n').length;
    var isLong = lineCount > 4 || (text || '').length > 300;
    var bodyClass = isLong ? 'msg-body collapsed' : 'msg-body';
    var toggleHtml = isLong ? '<span class="msg-toggle visible" onclick="window._toggleMsg(\'' + bodyId + '\', this)">Show more</span>' : '';

    div.innerHTML = header + '<div class="' + bodyClass + '" id="' + bodyId + '">' + formatted + '</div>' + toggleHtml;
    chatMsgs.appendChild(div);
    chatMsgs.scrollTop = chatMsgs.scrollHeight;
    return div;
  }

  window._toggleMsg = function(id, el) {
    var body = document.getElementById(id);
    if (!body) return;
    if (body.classList.contains('collapsed')) {
      body.classList.remove('collapsed');
      el.textContent = 'Show less';
    } else {
      body.classList.add('collapsed');
      el.textContent = 'Show more';
    }
  };

  function formatMsg(text) {
    /* Basic markdown: **bold**, `code`, ```blocks```, newlines */
    var s = esc(text);
    s = s.replace(/```([\s\S]*?)```/g, '<pre>$1</pre>');
    s = s.replace(/`([^`]+)`/g, '<code>$1</code>');
    s = s.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
    return s;
  }

  function sendChat(overrideMsg) {
    var msg = overrideMsg || chatInput.value.trim();
    if (!msg) return;
    var agentId = chatAgent.value;
    var m = msg.match(/^@(\w+)\s+/i);
    var targetAgent = m ? m[1].toLowerCase() : agentId;
    var cleanMsg = m ? msg.slice(m[0].length) : msg;
    chatInput.value = '';
    chatSend.disabled = true;
    chatTyping.classList.add('show');
    appendChatMsg('user', msg, null, null);
    _cacheMsg(targetAgent, 'user', msg);
    var chatModeEl = $('chatMode');
    var mode = chatModeEl ? chatModeEl.value : 'freeform';
    /* If in interview mode, include the active document context (if user has a doc tab open) */
    var activeDoc = '';
    if (mode === 'interview' && window._lastOpenedDoc) {
      activeDoc = window._lastOpenedDoc;
    }
    fetch('/api/chat', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({message: cleanMsg || msg, agent_id: targetAgent, mode: mode, active_doc: activeDoc})
    }).then(function(r) { return r.json(); }).then(function(d) {
      chatTyping.classList.remove('show');
      if (d.ok) {
        var respAgent = d.routed_to || d.agent_id || targetAgent;
        appendChatMsg(d.command ? 'system' : 'agent', d.response || '(no response)', respAgent, d.routed_to);
        _cacheMsg(respAgent, 'assistant', d.response || '');
        /* Handle switch_agent command */
        if (d.command === 'switch_agent' && d.agent_id) {
          chatAgent.value = d.agent_id;
        }
        /* Render structured question cards if any */
        if (d.structured_questions && d.structured_questions.length > 0) {
          d.structured_questions.forEach(function(sq, idx) {
            _renderStructuredQuestion(sq, idx, d.structured_questions.length, respAgent);
          });
        }
        /* Render tool execution cards if any */
        if (d.tool_calls && d.tool_calls.length > 0) {
          d.tool_calls.forEach(function(tc) {
            _renderToolExecCard(tc, respAgent);
          });
        }
      } else {
        appendChatMsg('system', 'Error: ' + (d.error || 'Unknown'), 'system', null);
      }
      chatSend.disabled = false;
    }).catch(function(e) {
      chatTyping.classList.remove('show');
      appendChatMsg('system', 'Error: ' + e.message, 'system', null);
      chatSend.disabled = false;
    });
  }

  if (chatSend) chatSend.addEventListener('click', function() { sendChat(); });
  if (chatInput) chatInput.addEventListener('keydown', function(e) {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendChat(); }
  });

  /* ── Structured Question Card Renderer ───────────────── */

  var _LETTERS = 'ABCDEFGHIJKLMNOP';

  function _renderStructuredQuestion(sq, idx, total, agentId) {
    var card = document.createElement('div');
    card.className = 'sq-card';
    var selectedId = null;
    var isFreeEntry = sq.free_entry_only === true;

    /* Context */
    var html = '';
    if (sq.context) {
      html += '<div class="sq-context">' + esc(sq.context) + '</div>';
    }

    /* Question + counter */
    html += '<div class="sq-question">';
    html += esc(sq.question);
    if (total > 1) {
      html += ' <span class="sq-counter">' + (idx + 1) + ' of ' + total + '</span>';
    }
    html += '</div>';

    if (isFreeEntry) {
      /* ── Free-entry only: just a text area, no option buttons ── */
      html += '<div class="sq-options" id="sqOpts_' + idx + '">';
      html += '<div class="sq-free-entry">';
      html += '<textarea class="sq-free-input" id="sqFreeInput_' + idx + '" placeholder="Type your answer..." rows="2" oninput="var b=document.getElementById(\'sqContinue_' + idx + '\');if(b)b.disabled=!this.value.trim()"></textarea>';
      html += '</div>';
      html += '</div>';
    } else {
      /* ── Multiple-choice: option buttons ── */
      html += '<div class="sq-options" id="sqOpts_' + idx + '">';
      (sq.options || []).forEach(function(opt, i) {
        var letter = _LETTERS[i] || (i + 1);
        html += '<div class="sq-option" data-oid="' + esc(opt.id) + '" onclick="window._sqSelect(this, ' + idx + ')">';
        html += '<span class="sq-letter">' + letter + '</span>';
        html += '<span class="sq-label">' + esc(opt.label) + '</span>';
        html += '</div>';
      });

      /* Other / custom option */
      if (sq.allow_custom !== false) {
        html += '<div class="sq-other-row" id="sqOther_' + idx + '" onclick="window._sqExpandOther(' + idx + ')">';
        html += '<span class="sq-letter" style="color:var(--muted)">&#9999;</span>';
        html += '<span class="sq-label" style="color:var(--muted)">Other...</span>';
        html += '<textarea class="sq-other-input" id="sqOtherInput_' + idx + '" placeholder="Type your answer..." rows="1" onclick="event.stopPropagation()"></textarea>';
        html += '</div>';
      }
      html += '</div>';
    }

    /* Actions */
    html += '<div class="sq-actions">';
    html += '<a class="sq-skip" href="#" onclick="window._sqSkip(' + idx + ');return false">Skip</a>';
    html += '<button class="sq-continue" id="sqContinue_' + idx + '" onclick="window._sqContinue(' + idx + ', \'' + esc(agentId) + '\')" disabled>Continue</button>';
    html += '</div>';

    card.innerHTML = html;
    chatMsgs.appendChild(card);
    chatMsgs.scrollTop = chatMsgs.scrollHeight;
  }

  /* Select an option */
  window._sqSelect = function(el, idx) {
    /* Deselect siblings */
    var container = $('sqOpts_' + idx);
    if (container) {
      container.querySelectorAll('.sq-option').forEach(function(o) { o.classList.remove('selected'); });
    }
    /* Deselect Other row */
    var otherRow = $('sqOther_' + idx);
    if (otherRow) { otherRow.classList.remove('expanded'); }

    el.classList.add('selected');
    /* Enable continue */
    var btn = $('sqContinue_' + idx);
    if (btn) btn.disabled = false;
  };

  /* Expand "Other" free-text */
  window._sqExpandOther = function(idx) {
    var otherRow = $('sqOther_' + idx);
    if (!otherRow) return;
    /* Deselect pre-made options */
    var container = $('sqOpts_' + idx);
    if (container) {
      container.querySelectorAll('.sq-option').forEach(function(o) { o.classList.remove('selected'); });
    }
    otherRow.classList.add('expanded');
    var inp = $('sqOtherInput_' + idx);
    if (inp) { inp.focus(); }
    var btn = $('sqContinue_' + idx);
    if (btn) btn.disabled = false;
  };

  /* Skip a question */
  window._sqSkip = function(idx) {
    var card = chatMsgs.querySelectorAll('.sq-card')[idx];
    if (card) {
      card.style.opacity = '0.5';
      card.style.pointerEvents = 'none';
    }
    /* Send "Skip" as the user response */
    sendChat('[Skipped question]');
  };

  /* Continue with the selected answer */
  window._sqContinue = function(idx, agentId) {
    var answer = '';
    /* Check free-entry-only input first */
    var freeInput = $('sqFreeInput_' + idx);
    if (freeInput && freeInput.value.trim()) {
      answer = freeInput.value.trim();
    }
    /* Check if a pre-made option is selected */
    if (!answer) {
      var container = $('sqOpts_' + idx);
      if (container) {
        var sel = container.querySelector('.sq-option.selected');
        if (sel) {
          var letter = sel.querySelector('.sq-letter');
          var label = sel.querySelector('.sq-label');
          answer = (letter ? letter.textContent : '') + ') ' + (label ? label.textContent : '');
        }
      }
    }
    /* Check if "Other" is active */
    if (!answer) {
      var otherInput = $('sqOtherInput_' + idx);
      if (otherInput && otherInput.value.trim()) {
        answer = otherInput.value.trim();
      }
    }
    if (!answer) return;

    /* Disable the card */
    var cards = chatMsgs.querySelectorAll('.sq-card');
    if (cards[idx]) {
      cards[idx].style.opacity = '0.7';
      cards[idx].style.pointerEvents = 'none';
    }

    /* Ensure we're talking to the right agent */
    if (agentId && chatAgent.value !== agentId) {
      chatAgent.value = agentId;
    }
    /* Send the answer as a chat message */
    sendChat(answer);
  };

  /* ── Tool Execution Cards (Cursor-style) ────────────── */
  function _renderToolExecCard(tc, agentId) {
    var card = document.createElement('div');
    card.className = 'tool-exec-card' + (tc.status === 'failed' ? '' : '');
    var statusClass = tc.status === 'done' ? 'done' : (tc.status === 'failed' ? 'failed' : 'running');
    var statusIcon = tc.status === 'done' ? '&#10003;' : (tc.status === 'failed' ? '&#10007;' : '&#9679;');
    var statusLabel = tc.status === 'done' ? 'Ran' : (tc.status === 'failed' ? 'Failed' : 'Running');
    var agentName = _agentNameMap[tc.agent_id || agentId] || (tc.agent_id || agentId);

    card.innerHTML = '<div class="tool-exec-header" onclick="this.parentElement.classList.toggle(\'expanded\')">' +
      '<div class="tool-exec-status ' + statusClass + '">' + statusIcon + '</div>' +
      '<div class="tool-exec-label">' + statusLabel + ' <span>' + esc(tc.label) + '</span></div>' +
      '<span style="font-size:0.72em;color:var(--muted)">' + esc(agentName) + '</span>' +
      '<span class="tool-exec-chevron">&#10095;</span>' +
      '</div>' +
      '<div class="tool-exec-body">' +
      '<div style="margin-bottom:4px;color:var(--text);font-family:var(--mono)">' + esc(tc.command || tc.tool) + '</div>' +
      (tc.result_preview ? '<div style="color:var(--muted);font-size:0.9em;margin-top:4px">' + esc(tc.result_preview) + '</div>' : '') +
      '</div>';
    chatMsgs.appendChild(card);
    chatMsgs.scrollTop = chatMsgs.scrollHeight;
  }

  /* ── Tool Approval Card (Cursor-style allowlist) ────── */
  function _renderToolApprovalCard(tc, agentId, onApprove, onSkip) {
    var card = document.createElement('div');
    card.className = 'tool-approval-card';
    var agentName = _agentNameMap[agentId] || agentId;
    var toolLabel = tc.label || tc.tool;

    card.innerHTML = '<div class="tool-approval-header">' +
      '<div class="tool-approval-icon">&#9881;</div>' +
      '<div><div class="tool-approval-title">Run ' + esc(toolLabel) + '</div>' +
      '<div class="tool-approval-subtitle">' + esc(agentName) + ' wants to use ' + esc(tc.tool) + '</div></div></div>' +
      '<div class="tool-approval-detail">' + esc(tc.command || tc.tool) + '</div>' +
      '<div class="tool-approval-actions">' +
      '<label style="font-size:0.75em;color:var(--muted);display:flex;align-items:center;gap:4px;cursor:pointer">' +
      '<input type="checkbox" id="ta-remember-' + esc(tc.tool) + '" style="accent-color:var(--accent)"> Use Allowlist</label>' +
      '<button class="ta-skip-btn" id="ta-skip-' + esc(tc.tool) + '">Skip</button>' +
      '<button class="ta-allowlist-btn" id="ta-allowlist-' + esc(tc.tool) + '">Allowlist Tool</button>' +
      '<button class="ta-run-btn" id="ta-run-' + esc(tc.tool) + '">Run &#x25B6;</button>' +
      '</div>';

    chatMsgs.appendChild(card);
    chatMsgs.scrollTop = chatMsgs.scrollHeight;

    /* Bind action buttons */
    var runBtn = card.querySelector('.ta-run-btn');
    var skipBtn = card.querySelector('.ta-skip-btn');
    var allowlistBtn = card.querySelector('.ta-allowlist-btn');
    var rememberCheck = card.querySelector('input[type="checkbox"]');

    if (runBtn) runBtn.addEventListener('click', function() {
      if (rememberCheck && rememberCheck.checked) _allowlistTool(tc.tool);
      card.style.opacity = '0.5'; card.style.pointerEvents = 'none';
      if (onApprove) onApprove(tc);
    });

    if (skipBtn) skipBtn.addEventListener('click', function() {
      card.style.opacity = '0.5'; card.style.pointerEvents = 'none';
      if (onSkip) onSkip(tc);
    });

    if (allowlistBtn) allowlistBtn.addEventListener('click', function() {
      _allowlistTool(tc.tool);
      card.style.opacity = '0.5'; card.style.pointerEvents = 'none';
      if (onApprove) onApprove(tc);
    });
  }

  function _allowlistTool(toolName) {
    _toolAllowlist[toolName] = true;
    localStorage.setItem('vastian_tool_allowlist', JSON.stringify(_toolAllowlist));
    fetch('/api/tool-allowlist', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({tool: toolName, allowed: true})
    }).catch(function() {});
    showToast('Allowlisted: ' + toolName);
  }

  function _isToolAllowed(toolName) {
    return _toolAllowlist[toolName] === true;
  }

  /* ── Chat Mode Toggle Logic ──────────────────────────── */

  var _chatModeEl = $('chatMode');
  var _chatModeIndicator = $('chatModeIndicator');

  window._onChatModeChange = function(mode, skipAutoStart) {
    localStorage.setItem('vastian_chat_mode', mode);
    if (mode === 'interview') {
      if (_chatModeIndicator) {
        var agentName = _agentNameMap[chatAgent.value] || chatAgent.value;
        _chatModeIndicator.textContent = 'Interview mode \u2014 ' + agentName + ' will guide you through questions';
        _chatModeIndicator.classList.add('show');
      }
      /* Auto-start: send a kick-off message so the agent begins asking questions */
      if (!skipAutoStart) {
        setTimeout(function() {
          var agentName = _agentNameMap[chatAgent.value] || chatAgent.value;
          var docHint = window._lastOpenedDoc ? ' I have "' + window._lastOpenedDoc + '" open.' : '';
          sendChat('I want you to interview me.' + docHint + ' Look at your documents, find what\\'s missing or incomplete, and ask me your first question using ask_structured_question. Do NOT just acknowledge — ask a real question right now.');
        }, 150);
      }
    } else {
      if (_chatModeIndicator) _chatModeIndicator.classList.remove('show');
    }
  };

  /* Set default mode based on tier */
  function _initChatMode() {
    var savedMode = localStorage.getItem('vastian_chat_mode');
    fetch('/api/user/tier').then(function(r) { return r.json(); }).then(function(d) {
      var tier = d.tier || 0;
      var defaultMode;
      if (tier <= 1) {
        defaultMode = 'interview';
      } else {
        defaultMode = 'freeform';
      }
      /* Use saved preference if within the same tier bracket, otherwise reset */
      var mode = savedMode || defaultMode;
      /* If user hasn't explicitly chosen, use tier-appropriate default */
      var savedTierBracket = localStorage.getItem('vastian_chat_mode_tier_bracket');
      var currentBracket = tier <= 1 ? 'early' : 'advanced';
      if (savedTierBracket !== currentBracket) {
        mode = defaultMode;
        localStorage.setItem('vastian_chat_mode_tier_bracket', currentBracket);
        localStorage.setItem('vastian_chat_mode', mode);
      }
      if (_chatModeEl) _chatModeEl.value = mode;
      window._onChatModeChange(mode, true);  /* skipAutoStart on page load */
    }).catch(function() {
      /* Fallback: use saved mode or freeform */
      if (savedMode && _chatModeEl) _chatModeEl.value = savedMode;
    });
  }
  _initChatMode();

  /* ── Chat history persistence ─────────────────────────── */

  function loadChatHistory(agentId) {
    if (_historyLoaded[agentId]) {
      /* Already loaded — restore from cache */
      _renderCachedThread(agentId);
      return;
    }
    fetch('/api/chat/history?agent_id=' + encodeURIComponent(agentId) + '&limit=50')
      .then(function(r) { return r.json(); })
      .then(function(d) {
        var msgs = d.messages || [];
        if (msgs.length > 0) {
          chatThreads[agentId] = msgs;
          _historyLoaded[agentId] = true;
          _renderCachedThread(agentId);
        }
      }).catch(function() {});
  }

  function _renderCachedThread(agentId) {
    chatMsgs.innerHTML = '';
    var msgs = chatThreads[agentId] || [];
    if (msgs.length === 0) return;
    /* History divider */
    var div = document.createElement('div');
    div.className = 'chat-history-divider';
    div.textContent = '--- Previous messages ---';
    chatMsgs.appendChild(div);
    msgs.forEach(function(m) {
      var role = m.role === 'user' ? 'user' : 'agent';
      var aid = m.role === 'user' ? null : agentId;
      appendChatMsg(role, m.content, aid, null);
    });
  }

  /* Save new messages to local cache as they're sent */
  var _origAppendChatMsg = appendChatMsg;
  /* We override to also cache, but only after history is loaded */
  function _cacheMsg(agentId, role, content) {
    if (!chatThreads[agentId]) chatThreads[agentId] = [];
    chatThreads[agentId].push({role: role, content: content, timestamp: new Date().toISOString()});
  }

  window._onAgentSwitch = function(newAgentId) {
    /* Save current scroll position conceptually, clear chat, load new thread */
    chatMsgs.innerHTML = '';
    loadChatHistory(newAgentId);
    /* Update chief button highlight */
    var chiefBtn = $('chiefBtn');
    if (chiefBtn) chiefBtn.classList.toggle('active', newAgentId === 'charles');
    /* Sync the dropdown */
    if (chatAgent.value !== newAgentId) chatAgent.value = newAgentId;
    /* Refresh doc bar for the new agent */
    _refreshDocBar(newAgentId);
  };

  /* ── Document bar (Cursor-style, per-agent) ──────────── */
  var _docBarExpanded = false;
  var _docBarDocs = {};  /* agentId -> [{name, exists}] */

  window._toggleDocBar = function() {
    var bar = $('docBar');
    _docBarExpanded = !_docBarExpanded;
    if (_docBarExpanded) bar.classList.add('expanded');
    else bar.classList.remove('expanded');
  };

  function _refreshDocBar(agentId) {
    var bar = $('docBar');
    var label = $('docBarLabel');
    var list = $('docBarList');
    if (!bar || !label || !list) return;
    /* Check cache first */
    if (_docBarDocs[agentId]) {
      _renderDocBarItems(_docBarDocs[agentId], agentId, label, list);
      return;
    }
    /* Fetch from API — response is a bare array of {agent_id, name, docs:[{name,exists}]} */
    fetch('/api/agent-docs').then(function(r) { return r.json(); }).then(function(d) {
      var agents = Array.isArray(d) ? d : (d.agents || []);
      agents.forEach(function(a) {
        var aid = a.agent_id || a.id;
        _docBarDocs[aid] = a.docs || a.documents || [];
      });
      var docs = _docBarDocs[agentId] || [];
      _renderDocBarItems(docs, agentId, label, list);
    }).catch(function() {
      label.textContent = '0 Files';
      list.innerHTML = '';
    });
  }

  function _renderDocBarItems(docs, agentId, label, list) {
    label.textContent = docs.length + ' File' + (docs.length !== 1 ? 's' : '');
    if (!docs.length) {
      list.innerHTML = '<div style="padding:8px 12px;font-size:0.8em;color:var(--muted)">No documents for this agent.</div>';
      return;
    }
    var html = '';
    docs.forEach(function(doc) {
      var name = doc.name || doc;
      var icon = doc.exists === false ? '&#9675;' : '&#9679;';
      html += '<div class="doc-bar-item" onclick="window._openDocFromBar(\'' + esc(agentId) + '\',\'' + esc(name) + '\')">';
      html += '<span class="doc-icon">' + icon + '</span>';
      html += '<span>' + esc(name) + '</span></div>';
    });
    list.innerHTML = html;
  }

  window._openDocFromBar = function(agentId, docName) {
    /* Close the bar */
    _docBarExpanded = false;
    var bar = $('docBar');
    if (bar) bar.classList.remove('expanded');
    /* Open as a tab instead of navigating to documents page */
    window._openDocTab(agentId, docName);
  };

  /* ── Open a document in a new tab (Cursor-style) ───── */
  window._lastOpenedDoc = '';  /* Track active doc for interview context */
  window._openDocTab = function(agentId, docName) {
    window._lastOpenedDoc = agentId + '/' + docName;
    /* Check if tab already exists */
    var existing = _findTabByDoc(agentId, docName);
    if (existing) {
      _activateTab(existing.id);
      /* Just switch the chat agent, don't re-send the brief */
      var chatAgentEl = $('chatAgent');
      if (chatAgentEl && chatAgentEl.value !== agentId) {
        chatAgentEl.value = agentId;
        if (window._onAgentSwitch) window._onAgentSwitch(agentId);
      }
      return;
    }
    /* Lookup agent name */
    var agentLabel = _agentNameMap[agentId] || agentId;
    var tab = _openTab({
      label: docName.replace('.md', '') + ' — ' + agentLabel,
      icon: '&#x1F4C4;',
      route: null,
      type: 'document',
      agentId: agentId,
      docName: docName,
      closable: true
    });
    var pane = $('tabPane-' + tab.id);
    if (!pane) return;
    pane.innerHTML = '<div class="v-loading">Loading ' + esc(docName) + '...</div>';
    /* Fetch doc content */
    fetch('/api/agent-docs/' + encodeURIComponent(agentId) + '/' + encodeURIComponent(docName))
      .then(function(r) { return r.json(); })
      .then(function(d) {
        if (d.error) {
          var errHtml = '<div class="v-empty"><p>' + esc(d.error) + '</p>';
          errHtml += '<div style="margin-top:16px;display:flex;gap:8px;justify-content:center;flex-wrap:wrap">';
          errHtml += '<button class="v-btn" onclick="window._createDocWithAgent(\'' + esc(agentId) + '\',\'' + esc(docName) + '\')">Create with ' + esc(agentLabel) + '</button>';
          errHtml += '<button class="v-btn" style="background:var(--card);border:1px solid var(--border);color:var(--text)" onclick="window._startInterviewChat(\'' + esc(agentId) + '\')">Interview with ' + esc(agentLabel) + '</button>';
          errHtml += '</div></div>';
          pane.innerHTML = errHtml;
          return;
        }
        var html = '<div style="padding:20px 24px">';
        html += '<div class="doc-toolbar">';
        html += '<span style="color:' + agentColor(agentId) + ';font-weight:600">' + esc(agentLabel) + '</span>';
        html += '<span style="color:var(--muted);font-size:0.82em"> / ' + esc(docName) + '</span>';
        if (d.last_modified) html += '<span class="doc-modified">Last modified: ' + esc(d.last_modified.slice(0, 19).replace('T', ' ')) + '</span>';
        html += '<button class="v-btn v-btn-sm" onclick="window._editDocInChat()">Edit in Chat</button>';
        html += '<button class="v-btn v-btn-sm" onclick="window._openDocTab(\'' + esc(agentId) + '\',\'' + esc(docName) + '\')">Refresh</button>';
        html += '</div>';
        html += '<div class="doc-rendered">' + renderMarkdown(d.content || '') + '</div>';
        html += '</div>';
        pane.innerHTML = html;
      }).catch(function(e) {
        pane.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
      });
    /* Switch chat agent and auto-brief */
    window._docAutoBrief(agentId, docName);
  };

  /* ── Create missing doc via agent ────────────────────── */
  window._createDocWithAgent = function(agentId, docName) {
    /* Tell the agent to create the document */
    var msg = 'Please create the document "' + docName + '" with appropriate initial content for your role.';
    fetch('/api/chat', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({message: msg, agent_id: agentId})
    }).then(function() {
      showToast('Asked ' + agentId + ' to create ' + docName);
      /* Reopen the doc tab after a brief delay */
      setTimeout(function() { window._openDocTab(agentId, docName); }, 3000);
    }).catch(function(e) {
      showToast('Error: ' + e.message, 'error');
    });
  };

  /* ── Start interview chat with an agent ────────────── */
  window._startInterviewChat = function(agentId) {
    chatAgent.value = agentId;
    if (typeof window._setChatMode === 'function') window._setChatMode('interview');
    var chatInput = document.querySelector('#chatInput') || document.querySelector('textarea');
    if (chatInput) { chatInput.focus(); }
    showToast('Interview mode with ' + agentId);
  };

  /* Initial load */
  setTimeout(function() { _refreshDocBar(chatAgent.value); }, 1500);

  /* ── Agent Profile Popup ────────────────────────────── */
  window._openAgentProfile = function(agentId) {
    /* Open as a tab showing the agent's full profile */
    var existingTab = _findTabByType('view', 'profile-' + agentId);
    if (existingTab) { _activateTab(existingTab.id); return; }

    var agentLabel = _agentNameMap[agentId] || agentId;
    var tab = _openTab({
      label: agentLabel + ' — Profile',
      icon: '&#x1F464;',
      type: 'view',
      viewKey: 'profile-' + agentId,
      closable: true
    });
    var pane = $('tabPane-' + tab.id);
    if (!pane) return;
    pane.innerHTML = '<div class="v-loading">Loading profile...</div>';

    fetch('/api/agents/' + encodeURIComponent(agentId) + '/profile')
      .then(function(r) { return r.json(); })
      .then(function(p) {
        if (p.error) { pane.innerHTML = '<div class="v-empty"><p>' + esc(p.error) + '</p></div>'; return; }
        var color = p.color || agentColor(agentId);
        var html = '<div style="max-width:640px;margin:0 auto;padding:4px 0">';

        /* Header */
        html += '<div class="agent-profile-card" style="border-left:4px solid ' + color + '">';
        html += '<div class="agent-profile-header">';
        html += '<span class="agent-profile-sigil">' + esc(p.sigil || '') + '</span>';
        html += '<div>';
        html += '<div class="agent-profile-name" style="color:' + color + '">' + esc(p.name) + '</div>';
        html += '<div class="agent-profile-title">' + esc(p.title) + '</div>';
        /* Tier badge and reporting line */
        var tierLabel = p.tier === 'chief' ? 'Chief' : p.tier === 'strategic' ? 'Strategic' : p.tier === 'specialist' ? 'Specialist' : 'Backend';
        html += '<div style="display:flex;gap:8px;align-items:center;margin-top:4px">';
        html += '<span style="font-size:0.68em;background:' + color + '22;color:' + color + ';padding:2px 8px;border-radius:8px;font-weight:600">Level ' + (p.level || 0) + ' — ' + tierLabel + '</span>';
        if (p.reports_to) html += '<span style="font-size:0.72em;color:var(--muted)">Reports to: ' + esc(p.reports_to) + '</span>';
        html += '</div>';
        html += '</div></div>';

        /* Role description */
        if (p.role_description) {
          html += '<div style="font-size:0.85em;color:var(--muted);line-height:1.6;margin-bottom:12px">' + esc(p.role_description) + '</div>';
        }

        /* Responsibilities */
        if (p.responsibilities && p.responsibilities.length) {
          html += '<div class="agent-profile-section"><h4>Key Responsibilities</h4><ul>';
          p.responsibilities.forEach(function(r) {
            html += '<li>' + esc(r) + '</li>';
          });
          html += '</ul></div>';
        }

        /* Tools */
        if (p.tools && p.tools.length) {
          html += '<div class="agent-profile-section"><h4>Tools</h4><div style="display:flex;flex-wrap:wrap;gap:4px">';
          p.tools.forEach(function(t) {
            html += '<span class="agent-tool-chip">' + esc(t.name) + '</span>';
          });
          html += '</div></div>';
        }

        /* Documents */
        if (p.owned_documents && p.owned_documents.length) {
          html += '<div class="agent-profile-section"><h4>Documents</h4><ul>';
          p.owned_documents.forEach(function(docName) {
            html += '<li style="cursor:pointer;color:var(--accent)" onclick="window._openDocTab(\'' + esc(agentId) + '\',\'' + esc(docName) + '\')">' + esc(docName) + '</li>';
          });
          html += '</ul></div>';
        }

        /* Quick actions */
        html += '<div class="agent-quick-actions">';
        html += '<button class="v-btn v-btn-sm" onclick="window._switchAgent(\'' + esc(agentId) + '\');document.getElementById(\'chatInput\').focus()">Chat with ' + esc(p.name) + '</button>';
        html += '<button class="v-btn v-btn-sm" onclick="window.location.hash=\'#/documents\';navigate(\'documents\')">View All Documents</button>';
        html += '</div>';

        html += '</div>'; /* close profile card */

        /* Suggestions for this agent */
        html += '<div id="agentProfileSuggestions" style="margin-top:14px"></div>';

        html += '</div>'; /* close wrapper */
        pane.innerHTML = html;

        /* Load agent-specific suggestions */
        fetch('/api/suggestions?agent_id=' + encodeURIComponent(agentId) + '&status=pending').then(function(r) { return r.json(); }).then(function(sd) {
          var sugEl = document.getElementById('agentProfileSuggestions');
          if (!sugEl || !sd.suggestions || !sd.suggestions.length) return;
          var sh = '<div class="v-card"><h3 style="font-size:0.88em;margin-bottom:8px">Pending Suggestions</h3>';
          sd.suggestions.forEach(function(s) {
            sh += '<div style="padding:6px 0;border-bottom:1px solid var(--border);font-size:0.85em">';
            sh += '<div style="font-weight:600">' + esc(s.title || s.type) + '</div>';
            sh += '<div style="color:var(--muted);font-size:0.82em">' + esc(s.description || '') + '</div>';
            sh += '</div>';
          });
          sh += '</div>';
          sugEl.innerHTML = sh;
        }).catch(function() {});
      }).catch(function(e) {
        pane.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
      });
  };

  /* ── Doc Edits Diff Review ──────────────────────────── */
  window._openDocEditsReview = function() {
    var existingTab = _findTabByType('view', 'doc-edits-review');
    if (existingTab) { _activateTab(existingTab.id); }
    var tab = _openTab({
      label: 'Review Edits',
      icon: '&#x1F4DD;',
      type: 'view',
      viewKey: 'doc-edits-review',
      closable: true
    });
    var pane = $('tabPane-' + tab.id);
    if (!pane) return;
    pane.innerHTML = '<div class="v-loading">Loading pending edits...</div>';

    fetch('/api/doc-edits/pending').then(function(r) { return r.json(); }).then(function(d) {
      var edits = d.edits || [];
      if (!edits.length) {
        pane.innerHTML = '<div style="padding:24px"><div class="v-card" style="text-align:center;padding:32px"><div style="font-size:2em;margin-bottom:8px">&#x2705;</div><h3>All caught up</h3><p style="color:var(--muted);font-size:0.85em;margin-top:6px">No pending document edits to review.</p></div></div>';
        return;
      }
      var html = '<div style="padding:24px;max-width:800px;margin:0 auto">';
      html += '<h2 style="font-size:1.1em;margin-bottom:16px">Pending Document Edits <span style="color:var(--muted);font-weight:400">(' + edits.length + ')</span></h2>';
      edits.forEach(function(edit, idx) {
        html += '<div class="v-card" style="margin-bottom:12px" id="editCard-' + idx + '">';
        html += '<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">';
        html += '<span style="width:8px;height:8px;border-radius:50%;background:' + agentColor(edit.agent_id) + '"></span>';
        html += '<span style="font-weight:600;font-size:0.88em">' + esc(edit.agent_id) + '</span>';
        html += '<span style="color:var(--muted);font-size:0.82em">' + esc(edit.doc_path) + '</span>';
        html += '</div>';
        /* Simple diff display */
        html += '<div class="diff-viewer">';
        var oldLines = (edit.old_content || '').split('\\n');
        var newLines = (edit.new_content || '').split('\\n');
        /* Show removals (red) then additions (green) for simplicity */
        var maxShow = 30;
        if (oldLines.length <= maxShow && newLines.length <= maxShow) {
          oldLines.forEach(function(line) {
            if (line.trim()) html += '<div class="diff-line-del">- ' + esc(line) + '</div>';
          });
          newLines.forEach(function(line) {
            if (line.trim()) html += '<div class="diff-line-add">+ ' + esc(line) + '</div>';
          });
        } else {
          html += '<div class="diff-line-ctx" style="padding:6px">Large document change — ' + oldLines.length + ' lines → ' + newLines.length + ' lines</div>';
        }
        html += '</div>';
        html += '<div class="diff-actions">';
        html += '<button class="v-btn v-btn-sm" style="background:var(--green);color:#fff" onclick="window._acceptEdit(\'' + esc(edit.id) + '\',' + idx + ')">&#x2713; Accept</button>';
        html += '<button class="v-btn v-btn-sm" style="background:var(--red);color:#fff" onclick="window._rejectEdit(\'' + esc(edit.id) + '\',' + idx + ')">&#x2715; Reject</button>';
        html += '<button class="v-btn v-btn-sm" onclick="window._openDocTab(\'' + esc(edit.agent_id) + '\',\'' + esc(edit.doc_path.split("/").pop()) + '\')">View Current</button>';
        html += '</div>';
        html += '</div>';
      });
      html += '</div>';
      pane.innerHTML = html;
    }).catch(function(e) {
      pane.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
    });
  };

  window._acceptEdit = function(editId, idx) {
    fetch('/api/doc-edits/' + editId + '/accept', {method:'POST'}).then(function(r) { return r.json(); }).then(function(d) {
      if (d.ok) {
        var card = document.getElementById('editCard-' + idx);
        if (card) { card.style.opacity = '0.3'; card.style.pointerEvents = 'none'; card.querySelector('.diff-actions').innerHTML = '<span style="color:var(--green);font-weight:600">&#x2713; Accepted</span>'; }
        showToast('Edit accepted: ' + (d.written || ''));
      }
    }).catch(function() { showToast('Error accepting edit'); });
  };

  window._rejectEdit = function(editId, idx) {
    fetch('/api/doc-edits/' + editId + '/reject', {method:'POST'}).then(function(r) { return r.json(); }).then(function(d) {
      if (d.ok) {
        var card = document.getElementById('editCard-' + idx);
        if (card) { card.style.opacity = '0.3'; card.style.pointerEvents = 'none'; card.querySelector('.diff-actions').innerHTML = '<span style="color:var(--red);font-weight:600">&#x2715; Rejected</span>'; }
        showToast('Edit rejected');
      }
    }).catch(function() { showToast('Error rejecting edit'); });
  };

  /* ── Thread list ──────────────────────────────────────── */
  window._toggleThreadList = function() {
    var el = $('chatThreads');
    if (!el) return;
    if (el.style.display === 'none') {
      el.style.display = 'block';
      _refreshThreadList();
    } else {
      el.style.display = 'none';
    }
  };

  function _refreshThreadList() {
    fetch('/api/chat/threads').then(function(r) { return r.json(); }).then(function(d) {
      var list = $('chatThreadList');
      if (!list) return;
      var threads = d.threads || [];
      if (threads.length === 0) {
        list.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.8em;text-align:center">No conversations yet. Start chatting!</div>';
        return;
      }
      var currentAgent = chatAgent.value;
      var html = '';
      threads.forEach(function(t) {
        var isActive = t.agent_id === currentAgent ? ' active' : '';
        var timeStr = t.last_timestamp ? new Date(t.last_timestamp).toLocaleDateString([], {month: 'short', day: 'numeric'}) : '';
        html += '<div class="chat-thread-item' + isActive + '" onclick="window._switchToThread(\'' + esc(t.agent_id) + '\')">' +
          '<div class="chat-thread-dot" style="background:' + agentColor(t.agent_id) + '"></div>' +
          '<div class="chat-thread-info">' +
            '<div class="chat-thread-name">' + esc(t.name || t.agent_id) + '</div>' +
            '<div class="chat-thread-preview">' + esc(t.preview || '') + '</div>' +
          '</div>' +
          '<div class="chat-thread-time">' + esc(timeStr) + '</div>' +
        '</div>';
      });
      list.innerHTML = html;
    }).catch(function() {});
  }

  window._switchToThread = function(agentId) {
    chatAgent.value = agentId;
    $('chatThreads').style.display = 'none';
    window._onAgentSwitch(agentId);
  };

  /* ── Collapsible chat panel ──────────────────────────── */
  var _chatCollapsed = localStorage.getItem('vastian-chat-collapsed') === 'true';

  function _applyChatCollapsed() {
    var panel = $('chatPanel');
    var btn = $('chatToggleBtn');
    if (_chatCollapsed) {
      panel.classList.add('collapsed');
      if (btn) btn.style.display = 'flex';
    } else {
      panel.classList.remove('collapsed');
      if (btn) btn.style.display = 'none';
      chatInput.focus();
    }
  }

  window._toggleChatPanel = function() {
    _chatCollapsed = !_chatCollapsed;
    localStorage.setItem('vastian-chat-collapsed', _chatCollapsed ? 'true' : 'false');
    _applyChatCollapsed();
  };

  _applyChatCollapsed();

  /* ── Ctrl+K keyboard shortcut ────────────────────────── */
  document.addEventListener('keydown', function(e) {
    if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
      e.preventDefault();
      window._toggleChatPanel();
    }
  });

  /* Load history for default agent on startup */
  loadChatHistory(chatAgent.value);

  /* ── Resizable chat panel ─────────────────────────────── */
  (function() {
    var handle = $('chatResizeHandle');
    var panel = $('chatPanel');
    var dragging = false;
    var savedWidth = localStorage.getItem('vastian-chat-width');
    if (savedWidth) {
      var w = parseInt(savedWidth, 10);
      if (w >= 300 && w <= window.innerWidth * 0.6) {
        document.documentElement.style.setProperty('--chat-w', w + 'px');
      }
    }
    handle.addEventListener('mousedown', function(e) {
      e.preventDefault();
      dragging = true;
      handle.classList.add('active');
    });
    document.addEventListener('mousemove', function(e) {
      if (!dragging) return;
      var newW = window.innerWidth - e.clientX;
      if (newW < 300) newW = 300;
      if (newW > window.innerWidth * 0.6) newW = window.innerWidth * 0.6;
      document.documentElement.style.setProperty('--chat-w', newW + 'px');
    });
    document.addEventListener('mouseup', function() {
      if (!dragging) return;
      dragging = false;
      handle.classList.remove('active');
      var w = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--chat-w'));
      localStorage.setItem('vastian-chat-width', w);
    });
  })();
  /* Auto-resize textarea */
  chatInput.addEventListener('input', function() {
    this.style.height = 'auto';
    this.style.height = Math.min(this.scrollHeight, 120) + 'px';
  });

  /* ── Legacy / palette (still works in chat input) ───── */
  chatInput.addEventListener('input', function() {
    var v = this.value;
    if (v === '/') {
      cmdPalette.classList.add('open');
      cmdPalette.innerHTML = chatCommands.map(function(c) {
        return '<div class="cmd-palette-item" data-cmd="' + esc(c.cmd) + '"><span class="cmd-name">' + esc(c.cmd) + '</span> &mdash; ' + esc(c.desc) + '</div>';
      }).join('');
      cmdPalette.querySelectorAll('.cmd-palette-item').forEach(function(el) {
        el.addEventListener('click', function() {
          chatInput.value = el.dataset.cmd + ' ';
          cmdPalette.classList.remove('open');
          chatInput.focus();
        });
      });
    } else if (!v.startsWith('/')) {
      cmdPalette.classList.remove('open');
    }
  });
  chatInput.addEventListener('blur', function() { setTimeout(function() { cmdPalette.classList.remove('open'); }, 200); });

  /* ═══ Global Command Palette (VS Code style, Ctrl+K / Ctrl+P) ═══ */
  var _vpalOverlay = $('vpalOverlay');
  var _vpalInput = $('vpalInput');
  var _vpalResults = $('vpalResults');
  var _vpalOpen = false;
  var _vpalActiveIdx = -1;
  var _vpalItems = [];  /* current rendered items: [{type, label, icon, source, action, shortcut}] */

  function _vpalShow(prefill) {
    _vpalOpen = true;
    _vpalOverlay.classList.add('open');
    _vpalInput.value = prefill || '';
    _vpalInput.focus();
    _vpalActiveIdx = -1;
    _vpalRender();
  }

  function _vpalHide() {
    _vpalOpen = false;
    _vpalOverlay.classList.remove('open');
    _vpalInput.value = '';
    _vpalActiveIdx = -1;
  }

  function _vpalRender() {
    var query = _vpalInput.value;
    var isCommandMode = query.startsWith('>');
    var searchTerm = isCommandMode ? query.slice(1).trim().toLowerCase() : query.trim().toLowerCase();
    var html = '';
    _vpalItems = [];

    if (isCommandMode) {
      /* ── Command mode: show slash commands grouped by agent/system ── */
      var cmdGroups = {};
      chatCommands.forEach(function(c) {
        /* Format: "System: /help", "Charles: /check-in" etc. */
        var source = 'System';
        var topic = (c.topic || '').toLowerCase();
        if (topic === 'navigation') source = 'Navigation';
        else if (topic === 'tasks') source = 'Tasks';
        else if (topic === 'collaboration') source = 'Agents';
        else if (topic === 'simulations') source = 'Simulations';
        else if (topic === 'metrics') source = 'Data';
        else if (topic === 'setup') source = 'Setup';
        else if (topic === 'system') source = 'System';

        var label = c.cmd;
        var desc = c.desc || '';
        /* Filter by search */
        if (searchTerm && label.toLowerCase().indexOf(searchTerm) === -1 && desc.toLowerCase().indexOf(searchTerm) === -1 && source.toLowerCase().indexOf(searchTerm) === -1) return;
        if (!cmdGroups[source]) cmdGroups[source] = [];
        cmdGroups[source].push({type: 'command', label: label, desc: desc, icon: '&#x2318;', source: source, action: function() { chatInput.value = label + ' '; chatInput.focus(); _vpalHide(); }});
      });
      Object.keys(cmdGroups).forEach(function(g) {
        html += '<div class="vpal-group">' + esc(g) + '</div>';
        cmdGroups[g].forEach(function(item, i) {
          var idx = _vpalItems.length;
          _vpalItems.push(item);
          html += '<div class="vpal-item" data-idx="' + idx + '">';
          html += '<span class="vpal-item-icon">' + item.icon + '</span>';
          html += '<span class="vpal-item-label">' + esc(item.label) + ' <span style="color:var(--muted);font-size:0.85em">' + esc(item.desc) + '</span></span>';
          html += '<span class="vpal-item-source">' + esc(item.source) + '</span>';
          html += '</div>';
        });
      });
      if (!_vpalItems.length) html = '<div class="vpal-empty">No commands matching "' + esc(searchTerm) + '"</div>';
    } else {
      /* ── Search mode: pages, agents, documents ── */

      /* Pages */
      var pageResults = [];
      Object.keys(PAGES).forEach(function(route) {
        var pg = PAGES[route];
        if (pg.hidden) return;
        var label = pg.label;
        if (searchTerm && label.toLowerCase().indexOf(searchTerm) === -1 && route.toLowerCase().indexOf(searchTerm) === -1) return;
        pageResults.push({type: 'page', label: label, icon: PAGE_ICONS[route] || '&#x1F4C4;', source: 'Pages', shortcut: '#/' + route, action: function() { window.location.hash = '#/' + route; navigate(route); _vpalHide(); }});
      });
      if (pageResults.length) {
        html += '<div class="vpal-group">Pages</div>';
        pageResults.forEach(function(item) {
          var idx = _vpalItems.length;
          _vpalItems.push(item);
          html += '<div class="vpal-item" data-idx="' + idx + '">';
          html += '<span class="vpal-item-icon">' + item.icon + '</span>';
          html += '<span class="vpal-item-label">' + esc(item.label) + '</span>';
          html += '<span class="vpal-item-source">' + esc(item.source) + '</span>';
          html += '</div>';
        });
      }

      /* Agents */
      var agentResults = [];
      chatAgents.forEach(function(a) {
        var label = (a.name || a.id);
        var title = a.title || '';
        if (searchTerm && label.toLowerCase().indexOf(searchTerm) === -1 && title.toLowerCase().indexOf(searchTerm) === -1 && (a.id || '').toLowerCase().indexOf(searchTerm) === -1) return;
        agentResults.push({type: 'agent', label: label + (title ? ' \u2014 ' + title : ''), icon: '&#x1F464;', source: 'Agents', action: function() { window._switchAgent(a.id); chatInput.focus(); _vpalHide(); }});
      });
      if (agentResults.length) {
        html += '<div class="vpal-group">Agents</div>';
        agentResults.forEach(function(item) {
          var idx = _vpalItems.length;
          _vpalItems.push(item);
          html += '<div class="vpal-item" data-idx="' + idx + '">';
          html += '<span class="vpal-item-icon">' + item.icon + '</span>';
          html += '<span class="vpal-item-label">' + esc(item.label) + '</span>';
          html += '<span class="vpal-item-source">' + esc(item.source) + '</span>';
          html += '</div>';
        });
      }

      /* Quick actions (always shown when empty query) */
      if (!searchTerm) {
        html += '<div class="vpal-group">Quick Actions</div>';
        var quickActions = [
          {label: 'Run Check-in', icon: '&#x2713;', source: 'Charles', action: function() { chatInput.value = '/check-in'; _vpalHide(); sendChat('/check-in'); }},
          {label: 'Run Briefing', icon: '&#x1F4CB;', source: 'Charles', action: function() { chatInput.value = '/briefing'; _vpalHide(); sendChat('/briefing'); }},
          {label: 'Start Interview', icon: '&#x1F399;', source: 'Chat', action: function() { var m = $('chatMode'); if (m) m.value = 'interview'; window._onChatModeChange('interview'); _vpalHide(); }},
          {label: 'Toggle Theme', icon: '&#x1F3A8;', source: 'Settings', shortcut: '#/settings', action: function() { window.location.hash = '#/settings'; navigate('settings'); _vpalHide(); }}
        ];
        quickActions.forEach(function(item) {
          var idx = _vpalItems.length;
          _vpalItems.push(item);
          html += '<div class="vpal-item" data-idx="' + idx + '">';
          html += '<span class="vpal-item-icon">' + item.icon + '</span>';
          html += '<span class="vpal-item-label">' + esc(item.label) + '</span>';
          html += '<span class="vpal-item-source">' + esc(item.source) + '</span>';
          html += '</div>';
        });
      }

      if (!_vpalItems.length) html = '<div class="vpal-empty">No results for "' + esc(searchTerm) + '"</div>';
    }
    _vpalResults.innerHTML = html;
    /* Bind clicks */
    _vpalResults.querySelectorAll('.vpal-item').forEach(function(el) {
      el.addEventListener('click', function() {
        var idx = parseInt(el.dataset.idx);
        if (_vpalItems[idx] && _vpalItems[idx].action) _vpalItems[idx].action();
      });
    });
    _vpalActiveIdx = _vpalItems.length > 0 ? 0 : -1;
    _vpalHighlight();
  }

  function _vpalHighlight() {
    _vpalResults.querySelectorAll('.vpal-item').forEach(function(el, i) {
      el.classList.toggle('active', parseInt(el.dataset.idx) === _vpalActiveIdx);
    });
    /* Scroll active into view */
    var activeEl = _vpalResults.querySelector('.vpal-item.active');
    if (activeEl) activeEl.scrollIntoView({block: 'nearest'});
  }

  _vpalInput.addEventListener('input', function() { _vpalActiveIdx = -1; _vpalRender(); });

  _vpalInput.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') { _vpalHide(); e.preventDefault(); return; }
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      if (_vpalItems.length) {
        _vpalActiveIdx = (_vpalActiveIdx + 1) % _vpalItems.length;
        _vpalHighlight();
      }
      return;
    }
    if (e.key === 'ArrowUp') {
      e.preventDefault();
      if (_vpalItems.length) {
        _vpalActiveIdx = (_vpalActiveIdx - 1 + _vpalItems.length) % _vpalItems.length;
        _vpalHighlight();
      }
      return;
    }
    if (e.key === 'Enter') {
      e.preventDefault();
      if (_vpalActiveIdx >= 0 && _vpalItems[_vpalActiveIdx] && _vpalItems[_vpalActiveIdx].action) {
        _vpalItems[_vpalActiveIdx].action();
      }
      return;
    }
  });

  _vpalOverlay.addEventListener('click', function(e) {
    if (e.target === _vpalOverlay) _vpalHide();
  });

  /* Ctrl+K / Ctrl+P opens the palette */
  document.addEventListener('keydown', function(e) {
    if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'p')) {
      e.preventDefault();
      if (_vpalOpen) { _vpalHide(); } else { _vpalShow(''); }
      return;
    }
    /* Ctrl+Shift+P opens in command mode */
    if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'P') {
      e.preventDefault();
      if (_vpalOpen) { _vpalHide(); } else { _vpalShow('>'); }
      return;
    }
    /* Ctrl+. — Cancel current agent operation */
    if ((e.metaKey || e.ctrlKey) && e.key === '.') {
      e.preventDefault();
      window._abortChat();
      return;
    }
    /* Ctrl+Shift+W — Toggle background worker */
    if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'W') {
      e.preventDefault();
      window._toggleWorker();
      return;
    }
    if (e.key === 'Escape' && _vpalOpen) {
      _vpalHide();
    }
  });

  window._openCommandPalette = function(prefill) { _vpalShow(prefill || ''); };

  /* ── Abort current chat operation (Ctrl+.) ─────────────── */
  var _chatAbortController = null;
  window._abortChat = function() {
    /* Abort the in-flight fetch */
    if (_chatAbortController) {
      _chatAbortController.abort();
      _chatAbortController = null;
    }
    /* Signal backend */
    fetch('/api/chat/abort', {method: 'POST'}).catch(function() {});
    /* Clear typing indicator */
    chatTyping.classList.remove('show');
    chatSend.disabled = false;
    appendChatMsg('system', 'Operation cancelled (Ctrl+.)', 'system', null);
    showToast('Agent operation cancelled');
  };

  /* ── Toggle background worker (Ctrl+Shift+W) ──────────── */
  window._toggleWorker = function() {
    fetch('/api/worker/toggle', {method: 'POST'}).then(function(r) { return r.json(); }).then(function(d) {
      if (d.ok) {
        showToast(d.running ? 'Background worker started' : 'Background worker stopped');
        var statusEl = $('workerStatusIndicator');
        if (statusEl) statusEl.textContent = d.running ? 'Worker: ON' : 'Worker: OFF';
      }
    }).catch(function() { showToast('Failed to toggle worker'); });
  };

  /* ── Check-in dropdown (Cursor-style todo list per agent) ── */
  window._openCheckinDropdown = function(agentId) {
    var existing = $('checkinDropdown');
    if (existing) { existing.remove(); return; }

    var dropdown = document.createElement('div');
    dropdown.id = 'checkinDropdown';
    dropdown.style.cssText = 'position:fixed;top:60px;right:420px;width:340px;max-height:400px;overflow-y:auto;background:var(--card);border:1px solid var(--border);border-radius:10px;box-shadow:0 8px 32px rgba(0,0,0,0.4);z-index:999;padding:0;';
    dropdown.innerHTML = '<div style="padding:12px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px"><span style="font-size:0.82em;font-weight:600;color:var(--text)">Check-ins</span><span style="font-size:0.72em;color:var(--muted)">' + esc(_agentNameMap[agentId] || agentId) + '</span><span style="margin-left:auto;font-size:0.7em;color:var(--muted);cursor:pointer" onclick="this.parentElement.parentElement.remove()">&#x2715;</span></div><div id="checkinItems" style="padding:8px"></div>';
    document.body.appendChild(dropdown);

    var itemsEl = $('checkinItems');
    itemsEl.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.8em">Loading...</div>';

    fetch('/api/checkins/' + encodeURIComponent(agentId)).then(function(r) { return r.json(); }).then(function(d) {
      if (!d.items || !d.items.length) {
        itemsEl.innerHTML = '<div style="padding:16px;text-align:center;color:var(--muted);font-size:0.82em">No pending check-in items</div>';
        return;
      }
      var html = '';
      d.items.forEach(function(item) {
        var icon = item.type === 'suggestion' ? '&#x1F4A1;' : item.type === 'task' ? '&#x2611;' : item.type === 'doc_edit' ? '&#x1F4DD;' : item.type === 'interview' ? '&#x1F3A4;' : '&#x25CB;';
        var actionLabel = item.action === 'approve' ? 'Approve' : item.action === 'review_edit' ? 'Review' : item.action === 'interview' ? 'Start' : 'Open';
        var prio = (item.priority || 'none').toLowerCase();
        var prioCls = 'ci-priority-' + (['critical','high','medium','low'].indexOf(prio) >= 0 ? prio : 'none');
        html += '<div class="' + prioCls + '" style="display:flex;align-items:flex-start;gap:10px;padding:8px 6px 8px 10px;border-radius:6px;cursor:pointer;transition:background 0.1s;margin-bottom:2px" onmouseover="this.style.background=\'rgba(88,166,255,0.06)\'" onmouseout="this.style.background=\'transparent\'" onclick="window._handleCheckinItem(\'' + esc(agentId) + '\',\'' + esc(item.type) + '\',\'' + esc(item.id) + '\')">';
        html += '<span style="font-size:1.1em;flex-shrink:0;margin-top:1px">' + icon + '</span>';
        html += '<div style="min-width:0;flex:1"><div style="font-size:0.82em;font-weight:600;color:var(--text)">' + esc(item.title) + '</div>';
        html += '<div style="font-size:0.72em;color:var(--muted);margin-top:2px">' + esc(item.description) + '</div></div>';
        html += '<span style="font-size:0.7em;color:var(--accent);font-weight:600;flex-shrink:0;padding:2px 8px;background:rgba(88,166,255,0.1);border-radius:4px">' + actionLabel + '</span>';
        html += '</div>';
      });
      itemsEl.innerHTML = html;
    }).catch(function() {
      itemsEl.innerHTML = '<div style="padding:12px;color:var(--red);font-size:0.8em">Failed to load</div>';
    });

    /* Close on outside click */
    setTimeout(function() {
      document.addEventListener('click', function _closeCheckin(e) {
        if (!dropdown.contains(e.target)) {
          dropdown.remove();
          document.removeEventListener('click', _closeCheckin);
        }
      });
    }, 100);
  };

  window._handleCheckinItem = function(agentId, type, itemId) {
    /* Close dropdown */
    var dd = $('checkinDropdown');
    if (dd) dd.remove();
    /* Switch to the agent */
    if (chatAgent.value !== agentId) {
      chatAgent.value = agentId;
      if (window._onAgentSwitch) window._onAgentSwitch(agentId);
    }
    /* Send contextual message to start the check-in item */
    if (type === 'interview') {
      if ($('chatMode')) $('chatMode').value = 'interview';
      window._onChatModeChange('interview');
    } else if (type === 'suggestion') {
      sendChat('I want to review the pending suggestion: ' + itemId + '. Tell me what it is and give me options.');
    } else if (type === 'task') {
      sendChat('Tell me about task ' + itemId + ' and what it involves. Give me the intention behind it and your recommendation.');
    } else if (type === 'doc_edit') {
      window._openDocEditsReview();
    } else {
      sendChat('/checkin');
    }
  };

  /* ── Activity detail view (from activity bar) ──────────── */
  window._openActivityView = function() {
    var existingTab = _findTabByType('view', 'activity-log');
    if (existingTab) { _activateTab(existingTab.id); return; }
    var tab = _openTab({
      label: 'Activity',
      icon: '&#x1F4CA;',
      type: 'view',
      viewKey: 'activity-log',
      closable: true
    });
    var pane = $('tabPane-' + tab.id);
    if (!pane) return;
    pane.innerHTML = '<div class="v-loading">Loading activity...</div>';

    fetch('/api/activity-log').then(function(r) { return r.json(); }).then(function(d) {
      var items = d.activities || [];
      if (!items.length) {
        pane.innerHTML = '<div style="padding:24px"><div class="v-card" style="text-align:center;padding:32px"><div style="font-size:2em;margin-bottom:8px">&#x1F4AD;</div><h3>No activity yet</h3><p style="color:var(--muted);font-size:0.85em;margin-top:6px">Agent actions and conversations will appear here.</p></div></div>';
        return;
      }
      var html = '<div style="padding:20px 24px;max-width:700px;margin:0 auto">';
      html += '<h2 style="font-size:1.1em;margin-bottom:16px">Recent Activity</h2>';
      items.forEach(function(act) {
        var color = agentColor(act.agent_id);
        var icon = act.type === 'delegation' ? '&#x2699;' : '&#x1F514;';
        var timeStr = act.timestamp ? new Date(act.timestamp).toLocaleString() : '';
        html += '<div class="v-card" style="margin-bottom:8px;border-left:3px solid ' + color + ';padding:10px 14px">';
        html += '<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">';
        html += '<span style="font-size:0.9em">' + icon + '</span>';
        html += '<span style="font-size:0.85em;font-weight:600;color:' + color + '">' + esc(_agentNameMap[act.agent_id] || act.agent_id) + '</span>';
        html += '<span style="font-size:0.72em;color:var(--muted);margin-left:auto">' + esc(timeStr) + '</span>';
        html += '</div>';
        html += '<div style="font-size:0.85em;color:var(--text)">' + esc(act.title) + '</div>';
        if (act.preview) html += '<div style="font-size:0.78em;color:var(--muted);margin-top:4px;white-space:pre-wrap">' + esc(act.preview) + '</div>';
        html += '</div>';
      });
      html += '</div>';
      pane.innerHTML = html;
    }).catch(function(e) {
      pane.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
    });
  };

  /* ── Archive view ──────────────────────────────────────── */
  window._openArchiveView = function() {
    var existingTab = _findTabByType('view', 'archive');
    if (existingTab) { _activateTab(existingTab.id); return; }
    var tab = _openTab({
      label: 'Archive',
      icon: '&#x1F4E6;',
      type: 'view',
      viewKey: 'archive',
      closable: true
    });
    var pane = $('tabPane-' + tab.id);
    if (!pane) return;
    pane.innerHTML = '<div class="v-loading">Loading archive...</div>';

    fetch('/api/archive').then(function(r) { return r.json(); }).then(function(d) {
      var items = d.items || [];
      var html = '<div style="padding:20px 24px;max-width:700px;margin:0 auto">';
      html += '<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px"><h2 style="font-size:1.1em;margin:0">Archive</h2>';
      html += '<span style="font-size:0.78em;color:var(--muted)">' + items.length + ' items \u2022 60-day retention</span></div>';
      if (!items.length) {
        html += '<div class="v-card" style="text-align:center;padding:32px"><div style="font-size:2em;margin-bottom:8px">&#x1F4E6;</div><h3>Archive is empty</h3><p style="color:var(--muted);font-size:0.85em;margin-top:6px">Archived items will appear here with a 60-day retention period.</p></div>';
      } else {
        items.forEach(function(item, idx) {
          html += '<div class="v-card" style="margin-bottom:8px;display:flex;align-items:center;gap:12px">';
          html += '<div style="flex:1"><div style="font-size:0.85em;font-weight:600">' + esc(item.type) + ': ' + esc(item.id) + '</div>';
          html += '<div style="font-size:0.72em;color:var(--muted)">' + (item.days_remaining || 0) + ' days remaining</div></div>';
          html += '<button class="v-btn v-btn-sm" style="background:var(--red);color:#fff" onclick="window._deleteArchived(\'' + esc(item.id) + '\',' + idx + ')">Delete</button>';
          html += '</div>';
        });
      }
      html += '</div>';
      pane.innerHTML = html;
    }).catch(function(e) {
      pane.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
    });
  };

  window._deleteArchived = function(itemId, idx) {
    fetch('/api/archive/' + encodeURIComponent(itemId), {method: 'DELETE'}).then(function(r) { return r.json(); }).then(function(d) {
      if (d.ok) showToast('Permanently deleted');
      window._openArchiveView(); /* refresh */
    }).catch(function() { showToast('Delete failed'); });
  };

  window._archiveItem = function(type, id) {
    fetch('/api/archive', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({type: type, id: id, auto_delete: true})
    }).then(function(r) { return r.json(); }).then(function(d) {
      if (d.ok) showToast('Archived (' + d.expires_in_days + ' days)');
    }).catch(function() { showToast('Archive failed'); });
  };

  /* ---- Bookmarks (Zo-style) ---- */
  window._openBookmarks = function() {
    var bm = JSON.parse(localStorage.getItem('vastian_bookmarks') || '[]');
    var html = '<div style="padding:12px"><h4 style="margin:0 0 10px;color:var(--text)">Bookmarks</h4>';
    if (!bm.length) { html += '<p style="color:var(--muted);font-size:0.85em">No bookmarks yet. Right-click a tab to bookmark it.</p>'; }
    bm.forEach(function(b, i) {
      html += '<div style="display:flex;align-items:center;gap:8px;padding:6px 8px;cursor:pointer;border-radius:6px" onclick="navigate(\'' + (b.path || '/') + '\')" onmouseover="this.style.background=\'rgba(88,166,255,0.08)\'" onmouseout="this.style.background=\'transparent\'">';
      html += '<span style="font-size:1.1em">' + (b.icon || '&#x1F4D1;') + '</span>';
      html += '<span style="flex:1;font-size:0.85em;color:var(--text)">' + esc(b.label || b.path) + '</span>';
      html += '<button style="background:none;border:none;color:var(--muted);cursor:pointer;font-size:0.8em" onclick="event.stopPropagation();window._removeBookmark(' + i + ')">&#x2715;</button>';
      html += '</div>';
    });
    html += '</div>';
    var sp = document.getElementById('spContent');
    if (sp) sp.innerHTML = html;
  };
  window._addBookmark = function(label, path, icon) {
    var bm = JSON.parse(localStorage.getItem('vastian_bookmarks') || '[]');
    if (bm.some(function(b) { return b.path === path; })) return;
    bm.push({label: label, path: path, icon: icon || '&#x1F4D1;'});
    localStorage.setItem('vastian_bookmarks', JSON.stringify(bm));
    showToast('Bookmarked: ' + label);
  };
  window._removeBookmark = function(idx) {
    var bm = JSON.parse(localStorage.getItem('vastian_bookmarks') || '[]');
    bm.splice(idx, 1);
    localStorage.setItem('vastian_bookmarks', JSON.stringify(bm));
    window._openBookmarks();
  };

  /* ---- Upload Modal (inbox + files) ---- */
  window._openUploadModal = function() {
    var existing = document.querySelector('.upload-modal-overlay');
    if (existing) existing.remove();
    var overlay = document.createElement('div');
    overlay.className = 'upload-modal-overlay';
    overlay.onclick = function(e) { if (e.target === overlay) overlay.remove(); };
    overlay.innerHTML = '<div class="upload-modal">'
      + '<h3>&#x2B06; Upload</h3>'
      + '<div class="upload-target-btn" onclick="window._uploadFor(\'inbox\')">'
      +   '<span class="ut-icon">&#x1F4E5;</span>'
      +   '<div><strong>Inbox Update</strong><div class="ut-desc">Drop a .txt file for agents to process.<br>'
      +   '<a href="https://vastianconnect.com/docs/inbox" target="_blank" style="color:var(--accent);font-size:0.85em" title="Read about inbox processing on VastianConnect Docs">Learn more &rarr;</a></div></div>'
      + '</div>'
      + '<div class="upload-target-btn" onclick="window._uploadFor(\'files\')">'
      +   '<span class="ut-icon">&#x1F4C4;</span>'
      +   '<div><strong>Files / Documents</strong><div class="ut-desc">Upload documents, images, or data files.<br>'
      +   '<a href="https://vastianconnect.com/docs/storage" target="_blank" style="color:var(--accent);font-size:0.85em" title="Read about file storage on VastianConnect Docs">Learn more &rarr;</a></div></div>'
      + '</div>'
      + '<div class="upload-dropzone" id="uploadDropzone">Drag &amp; drop files here, or click to browse</div>'
      + '<input type="file" id="uploadFileInput" style="display:none" multiple />'
      + '<div id="uploadStatus" style="margin-top:8px;font-size:0.82em;color:var(--muted)"></div>'
      + '<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:14px">'
      +   '<button class="v-btn v-btn-sm" onclick="this.closest(\'.upload-modal-overlay\').remove()">Cancel</button>'
      + '</div></div>';
    document.body.appendChild(overlay);
    var _uploadTarget = 'inbox';
    var dz = document.getElementById('uploadDropzone');
    var fi = document.getElementById('uploadFileInput');
    window._uploadFor = function(target) {
      _uploadTarget = target;
      document.querySelectorAll('.upload-target-btn').forEach(function(b) {
        b.style.borderColor = 'var(--border)';
      });
      event.currentTarget.style.borderColor = 'var(--accent)';
      fi.click();
    };
    dz.onclick = function() { fi.click(); };
    dz.ondragover = function(e) { e.preventDefault(); dz.classList.add('dragover'); };
    dz.ondragleave = function() { dz.classList.remove('dragover'); };
    dz.ondrop = function(e) {
      e.preventDefault(); dz.classList.remove('dragover');
      window._handleUploadFiles(e.dataTransfer.files, _uploadTarget);
    };
    fi.onchange = function() { window._handleUploadFiles(fi.files, _uploadTarget); };
  };
  window._handleUploadFiles = function(files, target) {
    if (!files.length) return;
    var status = document.getElementById('uploadStatus');
    if (status) status.textContent = 'Uploading ' + files.length + ' file(s) to ' + target + '...';
    var form = new FormData();
    for (var i = 0; i < files.length; i++) form.append('files', files[i]);
    form.append('target', target);
    fetch('/api/upload', { method: 'POST', body: form })
      .then(function(r) { return r.json(); })
      .then(function(d) {
        if (status) status.textContent = d.ok ? ('Uploaded ' + d.count + ' file(s) to ' + target) : (d.error || 'Upload failed');
        if (d.ok) setTimeout(function() {
          var overlay = document.querySelector('.upload-modal-overlay');
          if (overlay) overlay.remove();
          showToast('Uploaded ' + d.count + ' file(s) to ' + target);
        }, 1200);
      })
      .catch(function() { if (status) status.textContent = 'Upload failed'; });
  };

  /* ---- Config Menu (ellipsis — Zo-style) ---- */
  window._openConfigMenu = function(btn) {
    var existing = document.querySelector('.config-menu');
    if (existing) { existing.remove(); return; }
    var rect = btn.getBoundingClientRect();
    var menu = document.createElement('div');
    menu.className = 'config-menu';
    menu.style.left = (rect.right + 6) + 'px';
    menu.style.bottom = (window.innerHeight - rect.bottom) + 'px';
    var items = [
      {icon: '&#x1F510;', label: 'API Keys', page: '/settings/api-keys'},
      {icon: '&#x1F9E0;', label: 'Agent Suggestions', page: '/settings/suggestions'},
      {icon: '&#x1F4E6;', label: 'Storage Provider', page: '/settings/storage'},
      {icon: '&#x1F3A8;', label: 'Theme & Display', page: '/settings/theme'},
      {icon: '&#x1F4B0;', label: 'Token Budget', page: '/settings/tokens'},
      {icon: '&#x1F465;', label: 'Personas', page: '/settings/personas'},
      {icon: '&#x1F50C;', label: 'Integrations', page: '/settings/integrations'}
    ];
    items.forEach(function(it) {
      menu.innerHTML += '<div class="config-menu-item" onclick="navigate(\'' + it.page + '\');document.querySelector(\'.config-menu\')&&document.querySelector(\'.config-menu\').remove()">'
        + '<span class="cm-icon">' + it.icon + '</span>'
        + '<span>' + it.label + '</span></div>';
    });
    document.body.appendChild(menu);
    setTimeout(function() {
      document.addEventListener('click', function _closeMenu(e) {
        if (!menu.contains(e.target) && e.target !== btn) { menu.remove(); document.removeEventListener('click', _closeMenu); }
      });
    }, 10);
  };

  /* ---- Suggestion Frequency Control ---- */
  var _FREQ_DEFAULTS_BY_LEVEL = {open: 'high', standard: 'medium', pro: 'low', cloud: 'low'};
  window._getSuggestionFrequency = function() {
    return localStorage.getItem('vastian_suggestion_freq') || _FREQ_DEFAULTS_BY_LEVEL[window._currentUserLevel || 'open'] || 'medium';
  };
  window._setSuggestionFrequency = function(freq) {
    localStorage.setItem('vastian_suggestion_freq', freq);
    fetch('/api/settings', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({suggestion_frequency: freq})
    }).catch(function() {});
    /* Update UI active state */
    document.querySelectorAll('.freq-btn').forEach(function(b) {
      b.classList.toggle('active', b.dataset.freq === freq);
    });
    showToast('Suggestion frequency: ' + freq);
  };
  window._renderFrequencyControl = function() {
    var current = window._getSuggestionFrequency();
    return '<div class="freq-control">'
      + '<span style="font-size:0.78em;color:var(--muted);margin-right:6px">Suggestions:</span>'
      + '<button class="freq-btn' + (current === 'high' ? ' active' : '') + '" data-freq="high" onclick="window._setSuggestionFrequency(\'high\')">High</button>'
      + '<button class="freq-btn' + (current === 'medium' ? ' active' : '') + '" data-freq="medium" onclick="window._setSuggestionFrequency(\'medium\')">Medium</button>'
      + '<button class="freq-btn' + (current === 'low' ? ' active' : '') + '" data-freq="low" onclick="window._setSuggestionFrequency(\'low\')">Low</button>'
      + '</div>';
  };

  /* ---- Background Job Toast Notifications ---- */
  var _bgToastQueue = [];
  var _bgToastActive = false;
  window._showBgJobToast = function(agentId, agentName, taskSummary) {
    _bgToastQueue.push({agentId: agentId, agentName: agentName, summary: taskSummary});
    if (!_bgToastActive) window._processBgToastQueue();
  };
  window._processBgToastQueue = function() {
    if (!_bgToastQueue.length) { _bgToastActive = false; return; }
    _bgToastActive = true;
    var item = _bgToastQueue.shift();
    var toast = document.createElement('div');
    toast.className = 'bg-job-toast';
    toast.innerHTML = '<span class="bgt-icon">&#x2705;</span>'
      + '<div class="bgt-body"><div class="bgt-title">' + esc(item.agentName) + ' finished</div>'
      + '<div class="bgt-sub">' + esc(item.summary || 'Background task complete') + '</div></div>'
      + '<div class="bgt-actions">'
      + '<button class="bgt-btn bgt-done" onclick="this.closest(\'.bg-job-toast\').remove();window._processBgToastQueue()">Done</button>'
      + '<button class="bgt-btn bgt-goto" onclick="window._switchAgent(\'' + esc(item.agentId) + '\');this.closest(\'.bg-job-toast\').remove();window._processBgToastQueue()">Go to ' + esc(item.agentName) + '</button>'
      + '</div>';
    document.body.appendChild(toast);
    /* Auto-dismiss after 15s */
    setTimeout(function() { if (toast.parentElement) { toast.remove(); window._processBgToastQueue(); } }, 15000);
  };

  /* Poll for completed background jobs */
  var _lastBgCheckTimestamp = Date.now();
  setInterval(function() {
    fetch('/api/activity-log?since=' + _lastBgCheckTimestamp)
      .then(function(r) { return r.json(); })
      .then(function(data) {
        _lastBgCheckTimestamp = Date.now();
        if (data.items) {
          data.items.forEach(function(item) {
            if (item.type === 'delegation_complete' && item.agent_id) {
              window._showBgJobToast(item.agent_id, item.agent_name || item.agent_id, item.summary || 'Task completed');
              /* Also update badge on activity bar */
              var badge = document.getElementById('badge-activity');
              if (badge) { badge.textContent = '+'; badge.style.display = 'block'; }
            }
          });
        }
      }).catch(function() {});
  }, 30000); /* every 30s */

  /* Agent tier lookup */
  var _agentTierMap = {};
  var _agentNameMap = {};
  var _appDeployMode = 'local';
  var _toolAllowlist = JSON.parse(localStorage.getItem('vastian_tool_allowlist') || '{}');

  window._switchAgent = function(id) {
    var sel = chatAgent;
    sel.value = id;
    window._onAgentSwitch(id);
    var chiefBtn = $('chiefBtn');
    if (chiefBtn) chiefBtn.classList.toggle('active', id === 'charles');
  };

  /* Load agents + commands */
  function loadChatMeta() {
    fetch('/api/chat/commands').then(function(r) {
      if (!r.ok) throw new Error('api/chat/commands ' + r.status);
      return r.json();
    }).then(function(d) {
      chatCommands = Array.isArray(d.commands) ? d.commands : [];
      chatAgents = Array.isArray(d.agents) ? d.agents : [];
      /* Build tier + name lookup maps */
      chatAgents.forEach(function(a) {
        _agentTierMap[a.id] = a.tier || 'background';
        _agentNameMap[a.id] = a.name || a.id;
      });
      var sel = chatAgent;
      sel.innerHTML = '<option value="charles">Charles (auto-route)</option>';
      /* Specialists group */
      var specs = chatAgents.filter(function(a) { return a.tier === 'specialist'; });
      if (specs.length) {
        sel.innerHTML += '<optgroup label="Specialists">';
        specs.forEach(function(a) {
          sel.innerHTML += '<option value="' + esc(a.id) + '">' + esc(a.name) + ' — ' + esc(a.title) + '</option>';
        });
        sel.innerHTML += '</optgroup>';
      }
      /* Strategic group */
      var stratAgents = chatAgents.filter(function(a) { return a.tier === 'strategic'; });
      if (stratAgents.length) {
        sel.innerHTML += '<optgroup label="Strategic">';
        stratAgents.forEach(function(a) {
          sel.innerHTML += '<option value="' + esc(a.id) + '">' + esc(a.name) + ' \u2014 ' + esc(a.title) + '</option>';
        });
        sel.innerHTML += '</optgroup>';
      }
      /* Backend group — always show, badge indicates cloud-only */
      fetch('/api/user/tier').then(function(r) { return r.json(); }).then(function(td) {
        _appDeployMode = td.deployment_mode || 'local';
        var isCloud = _appDeployMode === 'cloud';
        var bgAgents = chatAgents.filter(function(a) { return a.tier === 'backend'; });
        if (bgAgents.length) {
          sel.innerHTML += '<optgroup label="Backend' + (isCloud ? '' : ' \u2601 Cloud') + '">';
          bgAgents.forEach(function(a) {
            sel.innerHTML += '<option value="' + esc(a.id) + '"' + (isCloud ? '' : ' disabled') + '>' + esc(a.name) + ' \u2014 ' + esc(a.title) + (isCloud ? '' : ' \u2601') + '</option>';
          });
          sel.innerHTML += '</optgroup>';
        }
      }).catch(function() {});
      var chiefBtn = $('chiefBtn');
      if (chiefBtn) chiefBtn.classList.add('active');
      /* Populate collapsible command list */
      var cmdListEl = $('cmdList');
      if (cmdListEl) {
        var html = '';
        var groups = {navigation: 'Navigation', tasks: 'Tasks', collaboration: 'Collaboration', system: 'System', simulations: 'Advanced', metrics: 'Advanced', setup: 'Setup'};
        var grouped = {};
        chatCommands.forEach(function(c) {
          var g = c.topic || 'other';
          if (!grouped[g]) grouped[g] = [];
          grouped[g].push(c);
        });
        Object.keys(grouped).forEach(function(g) {
          var label = groups[g] || g;
          html += '<div class="cmd-list-group">' + esc(label) + '</div>';
          grouped[g].forEach(function(c) {
            html += '<div class="cmd-list-item" data-cmd="' + esc(c.cmd) + '"><span class="cmd-name">' + esc(c.cmd) + '</span><span>' + esc(c.desc) + '</span></div>';
          });
        });
        cmdListEl.innerHTML = html;
        cmdListEl.querySelectorAll('.cmd-list-item').forEach(function(el) {
          el.addEventListener('click', function() {
            if (el.classList.contains('locked')) return;
            chatInput.value = el.dataset.cmd + ' ';
            chatInput.focus();
          });
        });
      }
    }).catch(function(e) {
      console.error('[Vastian] loadChatMeta failed:', e && e.message ? e.message : e);
    });
  }

  /* ── Setup Wizard ──────────────────────────────────────── */
  function renderSetup(el) {
    var html = '<div style="max-width:560px;margin:20px auto">';
    html += '<div style="text-align:center;margin-bottom:18px"><img src="/static/vastian-icon.png" alt="Vastian" style="width:64px;height:64px;border-radius:14px;box-shadow:0 2px 12px rgba(0,0,0,0.3)"></div>';
    html += '<h1 style="color:var(--accent);font-size:1.6em;margin-bottom:6px;text-align:center">Welcome to Vastian</h1>';
    html += '<p style="color:var(--muted);font-size:0.92em;margin-bottom:28px;text-align:center">Set up your AI team in 60 seconds.</p>';

    /* Step 1: Name */
    html += '<div class="v-card" id="setup-step1">';
    html += '<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px"><span class="v-badge v-badge-accent">1</span><strong>Your Name</strong></div>';
    html += '<div class="v-form-row"><input type="text" id="setupName" placeholder="What should your agents call you?" style="width:100%"></div>';
    html += '</div>';

    /* Step 2: LLM Provider — default to Ollama (local, no API keys) */
    html += '<div class="v-card" id="setup-step2">';
    html += '<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px"><span class="v-badge v-badge-green">2</span><strong>AI Provider</strong></div>';
    html += '<p style="color:var(--muted);font-size:0.88em;margin-bottom:12px">Want to use Ollama for local AI (no API keys or cloud costs)?</p>';
    html += '<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px">';
    html += '<button class="v-btn" onclick="window._setupProvider(\'ollama\')" id="sp-ollama" style="flex:1;min-width:120px">Ollama (local)</button>';
    html += '<button class="v-btn" onclick="window._setupProvider(\'openai\')" id="sp-openai" style="flex:1;min-width:120px;background:var(--card);border:1px solid var(--border);color:var(--text)">OpenAI</button>';
    html += '<button class="v-btn" onclick="window._setupProvider(\'anthropic\')" id="sp-anthropic" style="flex:1;min-width:120px;background:var(--card);border:1px solid var(--border);color:var(--text)">Anthropic</button>';
    html += '</div>';
    html += '<div id="setup-apikey-row" class="v-form-row" style="display:none"><label id="setup-apikey-label">OpenAI API Key</label><input type="password" id="setupApiKey" placeholder="sk-..." style="width:100%"></div>';
    html += '<div id="setup-ollama-row">';
    html += '<div id="ollama-status" style="padding:12px 0">';
    html += '<p style="color:var(--muted);font-size:0.85em">Checking Ollama status...</p>';
    html += '</div>';
    html += '<p id="ollama-reminder" style="font-size:0.8em;color:var(--muted);margin-top:8px;display:none">You can change this anytime in Settings &gt; API Keys.</p>';
    html += '</div>';
    html += '</div>';

    /* Save */
    html += '<button class="v-btn" style="width:100%;padding:14px;font-size:1em" id="setupSaveBtn" onclick="window._setupSave()">Save &amp; Continue</button>';
    html += '<p id="setup-msg" style="text-align:center;margin-top:10px;font-size:0.85em;min-height:1.4em"></p>';
    html += '</div>';
    el.innerHTML = html;
    setTimeout(function() {
      if (typeof window._setupProvider === 'function') window._setupProvider('ollama');
      if (typeof window._checkOllamaStatus === 'function') window._checkOllamaStatus();
    }, 0);
  }

  var _setupSelectedProvider = 'ollama';
  var _ollamaSelectedModel = '';

  window._setupProvider = function(provider) {
    _setupSelectedProvider = provider;
    var btns = ['ollama', 'openai', 'anthropic'];
    btns.forEach(function(p) {
      var btn = document.getElementById('sp-' + p);
      if (btn) {
        if (p === provider) { btn.style.background = 'var(--accent)'; btn.style.color = '#fff'; btn.style.border = 'none'; }
        else { btn.style.background = 'var(--card)'; btn.style.color = 'var(--text)'; btn.style.border = '1px solid var(--border)'; }
      }
    });
    var apiRow = document.getElementById('setup-apikey-row');
    var ollamaRow = document.getElementById('setup-ollama-row');
    var reminder = document.getElementById('ollama-reminder');
    var label = document.getElementById('setup-apikey-label');
    if (provider === 'ollama') {
      if (apiRow) apiRow.style.display = 'none';
      if (ollamaRow) ollamaRow.style.display = 'block';
      if (reminder) reminder.style.display = 'block';
      window._checkOllamaStatus();
    } else {
      if (apiRow) apiRow.style.display = 'block';
      if (ollamaRow) ollamaRow.style.display = 'none';
      if (reminder) reminder.style.display = 'none';
      if (label) label.textContent = provider === 'anthropic' ? 'Anthropic API Key' : 'OpenAI API Key';
      var input = document.getElementById('setupApiKey');
      if (input) input.placeholder = provider === 'anthropic' ? 'sk-ant-...' : 'sk-...';
    }
  };

  window._checkOllamaStatus = function() {
    var container = document.getElementById('ollama-status');
    if (!container) return;
    container.innerHTML = '<p style="color:var(--muted);font-size:0.85em">Checking Ollama status...</p>';

    fetch('/api/ollama/status').then(function(r) { return r.json(); }).then(function(d) {
      var html = '';
      if (d.running && d.models && d.models.length > 0) {
        /* Ollama running with models available */
        html += '<div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">';
        html += '<span style="color:var(--green);font-size:1.1em">&#x2713;</span>';
        html += '<span style="color:var(--green);font-size:0.88em;font-weight:600">Ollama detected — you can start with it</span>';
        html += '</div>';
        html += '<label style="font-size:0.85em;color:var(--text);display:block;margin-bottom:6px">Select a model</label>';
        html += '<select id="setupOllamaSelect" onchange="window._ollamaSelectedModel=this.value" style="width:100%;padding:10px;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:6px;font-size:0.9em">';
        d.models.forEach(function(m) {
          var rec = m === d.recommended ? ' (recommended)' : '';
          var sel = m === d.recommended ? ' selected' : '';
          html += '<option value="' + esc(m) + '"' + sel + '>' + esc(m) + rec + '</option>';
        });
        html += '</select>';
        _ollamaSelectedModel = d.recommended || d.models[0];
      } else if (d.running) {
        /* Ollama running but no models pulled */
        html += '<div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">';
        html += '<span style="color:var(--yellow,#e3b341);font-size:1.1em">&#x26A0;</span>';
        html += '<span style="color:var(--yellow,#e3b341);font-size:0.88em;font-weight:600">Ollama is running but no models are installed</span>';
        html += '</div>';
        html += '<p style="font-size:0.85em;color:var(--text);margin-bottom:8px">Open a terminal and run:</p>';
        html += '<div style="background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:10px 14px;font-family:monospace;font-size:0.85em;color:var(--accent);margin-bottom:10px;user-select:all">ollama pull llama3.1</div>';
        html += '<p style="font-size:0.78em;color:var(--muted);margin-bottom:12px">This downloads ~4.7 GB. After it finishes, click Check Again.</p>';
        html += '<button class="v-btn" onclick="window._checkOllamaStatus()" style="width:100%;padding:10px">Check Again</button>';
        _ollamaSelectedModel = '';
      } else {
        /* Ollama not installed or not running */
        html += '<div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">';
        html += '<span style="color:var(--red,#f85149);font-size:1.1em">&#x2717;</span>';
        html += '<span style="color:var(--red,#f85149);font-size:0.88em;font-weight:600">Ollama is not running</span>';
        html += '</div>';
        html += '<p style="font-size:0.85em;color:var(--text);margin-bottom:8px">Ollama runs AI models locally on your machine — no API keys or cloud costs.</p>';
        html += '<ol style="font-size:0.85em;color:var(--text);padding-left:20px;margin-bottom:10px">';
        html += '<li style="margin-bottom:6px">Download and install Ollama from <a href="https://ollama.com/download" target="_blank" rel="noopener noreferrer" style="color:var(--accent);text-decoration:underline">ollama.com/download</a></li>';
        html += '<li style="margin-bottom:6px">Start Ollama (it runs in the background)</li>';
        html += '<li>Pull a model: <code style="background:var(--bg);padding:2px 6px;border-radius:3px">ollama pull llama3.1</code></li>';
        html += '</ol>';
        html += '<p style="font-size:0.8em;color:var(--muted);margin-bottom:10px">Save &amp; Continue will set Ollama as your provider. Install from the link above, then click Check Again.</p>';
        html += '<button class="v-btn" onclick="window._checkOllamaStatus()" style="width:100%;padding:10px">Check Again</button>';
        _ollamaSelectedModel = 'llama3.1';
      }
      container.innerHTML = html;
    }).catch(function() {
      container.innerHTML = '<p style="color:var(--red,#f85149);font-size:0.85em">Failed to check Ollama status.</p>' +
        '<button class="v-btn" onclick="window._checkOllamaStatus()" style="width:100%;padding:10px;margin-top:8px">Retry</button>';
      _ollamaSelectedModel = '';
    });
  };

  window._setupSave = function() {
    var name = (document.getElementById('setupName') || {}).value || '';
    var apiKey = (document.getElementById('setupApiKey') || {}).value || '';
    var ollamaSelect = document.getElementById('setupOllamaSelect');
    var ollamaModel = (ollamaSelect ? ollamaSelect.value : null) || _ollamaSelectedModel || '';
    var msg = document.getElementById('setup-msg');
    var btn = document.getElementById('setupSaveBtn');

    if (!name.trim()) { if (msg) { msg.textContent = 'Please enter your name.'; msg.style.color = 'var(--red)'; } return; }
    if (_setupSelectedProvider !== 'ollama' && !apiKey.trim()) { if (msg) { msg.textContent = 'Please enter your API key.'; msg.style.color = 'var(--red)'; } return; }
    if (_setupSelectedProvider === 'ollama' && !ollamaModel) ollamaModel = 'llama3.1';

    if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }
    var body = { user_name: name.trim(), llm_provider: _setupSelectedProvider };
    if (_setupSelectedProvider === 'openai') body.openai_api_key = apiKey.trim();
    if (_setupSelectedProvider === 'anthropic') body.anthropic_api_key = apiKey.trim();
    if (_setupSelectedProvider === 'ollama') body.ollama_model = ollamaModel;

    fetch('/api/setup', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify(body)
    }).then(function(r) { return r.json(); }).then(function(d) {
      if (d.ok) {
        if (msg) { msg.textContent = 'Setup complete! Redirecting...'; msg.style.color = 'var(--green)'; }
        setTimeout(function() { window.location.hash = '#/dashboard'; navigate('dashboard'); }, 1000);
      } else {
        if (msg) { msg.textContent = d.error || 'Save failed'; msg.style.color = 'var(--red)'; }
        if (btn) { btn.disabled = false; btn.textContent = 'Save & Continue'; }
      }
    }).catch(function(e) {
      if (msg) { msg.textContent = 'Error: ' + e.message; msg.style.color = 'var(--red)'; }
      if (btn) { btn.disabled = false; btn.textContent = 'Save & Continue'; }
    });
  };

  /* ── Roleplay center display ───────────────────────────── */
  function renderActiveRoleplay(el) {
    /* Parse session ID from hash: #/roleplay/rp-abc123 */
    var hash = window.location.hash || '';
    var parts = hash.replace('#/', '').split('/');
    var sessionId = parts.length > 1 ? parts[1] : '';

    if (!sessionId) {
      /* No specific session — show session list */
      fetch('/api/roleplay/sessions').then(function(r) { return r.json(); }).then(function(d) {
        var sessions = d.sessions || [];
        var active = sessions.filter(function(s) { return s.status === 'active'; });
        var history = sessions.filter(function(s) { return s.status !== 'active'; });
        var html = '<div class="v-card"><h2>Roleplay Sessions</h2>';
        if (active.length) {
          html += '<h3 style="color:var(--green);margin:12px 0 6px">Active</h3>';
          active.forEach(function(s) {
            html += '<div class="v-list-item" style="cursor:pointer" onclick="window.location.hash=\'#/roleplay/' + esc(s.session_id) + '\';navigate(\'roleplay\')">';
            html += '<h3>' + esc(s.title || 'Untitled') + '</h3>';
            html += '<p style="color:var(--muted)">' + esc(s.persona_id || '') + ' &middot; ' + esc(s.agent_id || 'adrian') + '</p>';
            html += '</div>';
          });
        }
        if (history.length) {
          html += '<h3 style="color:var(--muted);margin:12px 0 6px">History</h3>';
          history.forEach(function(s) {
            html += '<div class="v-list-item" style="cursor:pointer;opacity:0.7" onclick="window.location.hash=\'#/roleplay/' + esc(s.session_id) + '\';navigate(\'roleplay\')">';
            html += '<h3>' + esc(s.title || 'Untitled') + ' <span style="font-size:0.7em;color:var(--muted)">' + esc(s.status) + '</span></h3>';
            html += '<p style="color:var(--muted)">' + esc(s.created_at || '') + '</p>';
            html += '</div>';
          });
        }
        if (!sessions.length) {
          html += '<div class="v-empty"><p>No roleplay sessions yet.</p><p style="color:var(--muted);font-size:0.85em;margin-top:6px">Agents will suggest roleplay sessions for you. Accept one from the notification bell to begin.</p></div>';
        }
        html += '</div>';
        el.innerHTML = html;
      }).catch(function(e) {
        el.innerHTML = '<div class="v-empty"><p>Error: ' + esc(e.message) + '</p></div>';
      });
      return;
    }

    /* Specific session — show roleplay center */
    fetch('/api/roleplay/sessions/' + encodeURIComponent(sessionId)).then(function(r) { return r.json(); }).then(function(d) {
      var s = d.session || {};
      var html = '<div style="max-width:720px;margin:0 auto">';
      html += '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px">';
      html += '<h2 style="margin:0">' + esc(s.title || 'Roleplay') + '</h2>';
      html += '<span style="background:' + (s.status === 'active' ? 'var(--green)' : 'var(--muted)') + ';color:#fff;font-size:0.7em;padding:2px 8px;border-radius:10px">' + esc(s.status || 'unknown') + '</span>';
      html += '</div>';
      html += '<p style="color:var(--muted);font-size:0.85em;margin-bottom:16px">Agent: ' + esc(s.agent_id || '') + ' &middot; Persona: ' + esc(s.persona_id || '') + '</p>';

      /* Narrative blocks */
      var narrative = s.narrative || [];
      if (narrative.length) {
        narrative.forEach(function(block) {
          if (block.role === 'narrator') {
            html += '<div class="v-card" style="border-left:3px solid var(--accent);margin-bottom:10px"><p style="white-space:pre-wrap">' + esc(block.text || '') + '</p></div>';
          } else if (block.role === 'user_choice') {
            html += '<div style="text-align:right;margin-bottom:10px"><span style="background:var(--accent);color:#fff;padding:6px 14px;border-radius:16px;display:inline-block;font-size:0.9em">' + esc(block.text || '') + '</span></div>';
          }
        });
      } else {
        html += '<div class="v-card"><p style="color:var(--muted);text-align:center">Waiting for the story to begin... Chat with ' + esc(s.agent_id || 'Adrian') + ' in the Copilot to start.</p></div>';
      }

      /* Choice buttons (if pending choices) */
      var choices = s.choices || [];
      if (choices.length && s.status === 'active') {
        html += '<div style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px">';
        choices.forEach(function(c, i) {
          html += '<button class="v-btn" style="flex:1;min-width:140px" onclick="_roleplayChoice(\'' + esc(sessionId) + '\',' + i + ',\'' + esc(c) + '\')">' + esc(c) + '</button>';
        });
        html += '</div>';
      }

      /* Back link */
      html += '<div style="margin-top:24px;text-align:center"><a href="#/roleplay" style="color:var(--muted);font-size:0.85em" onclick="navigate(\'roleplay\')">Back to all sessions</a></div>';
      html += '</div>';
      el.innerHTML = html;

      /* Switch chat agent to the roleplay agent */
      if (s.agent_id && typeof switchAgent === 'function') switchAgent(s.agent_id);
    }).catch(function(e) {
      el.innerHTML = '<div class="v-empty"><p>Session not found: ' + esc(e.message) + '</p></div>';
    });
  }

  function _roleplayChoice(sessionId, index, text) {
    /* Send the choice to the API and refresh */
    fetch('/api/roleplay/sessions', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({session_id: sessionId, last_choice: {index: index, text: text}})
    }).then(function() {
      /* Also send as chat message to the agent */
      var input = $('chatInput');
      if (input) {
        input.value = text;
        var ev = new Event('submit', {bubbles: true});
        var form = input.closest('form');
        if (form) form.dispatchEvent(ev);
      }
      /* Refresh the roleplay view after a short delay */
      setTimeout(function() {
        var el = $('pageContent');
        if (el) renderActiveRoleplay(el);
      }, 2000);
    });
  }

  /* ── First-run detection ───────────────────────────────── */
  function checkFirstRun() {
    fetch('/api/setup/status').then(function(r) { return r.json(); }).then(function(d) {
      if (!d.configured) {
        window.location.hash = '#/setup';
        navigate('setup');
      }
    }).catch(function() {});
  }

  /* ── Feature gates (progressive sidebar unlock) ──────── */
  var _featureGates = {};
  function applyFeatureGates() {
    fetch('/api/feature-gates').then(function(r) { return r.json(); }).then(function(d) {
      _featureGates = d.gates || {};
      /* Apply to side panel nav items */
      document.querySelectorAll('.sp-nav-item[data-route]').forEach(function(el) {
        var route = el.dataset.route;
        if (route in _featureGates) {
          if (_featureGates[route]) {
            el.classList.remove('nav-locked');
            el.style.pointerEvents = '';
          } else {
            el.classList.add('nav-locked');
          }
        }
      });
    }).catch(function() {});
  }

  /* ── Notification badges ────────────────────────────── */
  function pollBadges() {
    fetch('/api/notifications/count').then(function(r) { return r.json(); }).then(function(d) {
      /* Update side-panel badges if rendered */
      _setBadge('sp-badge-sessions', d.ready_sessions || 0);
      _setBadge('sp-badge-boards', d.kanban_pending || 0);
    }).catch(function() {});
  }

  /* ── Suggestion badge polling ─────────────────────────── */
  function pollSuggestionBadges() {
    fetch('/api/notifications/count').then(function(r) { return r.json(); }).then(function(d) {
      _setBadge('sp-badge-personas', d.personas || 0);
      _setBadge('sp-badge-councils', d.councils || 0);
      _setBadge('sp-badge-plans', d.plans || 0);
      _setBadge('sp-badge-scenarios', d.scenarios || 0);
      /* Bell badge on activity bar */
      var bellTotal = (d.chat || 0) + (d.personas || 0) + (d.councils || 0) + (d.plans || 0) + (d.scenarios || 0);
      _setBadge('badge-bell', bellTotal);
    }).catch(function() {});
  }

  function _setBadge(id, count) {
    var el = $(id);
    if (!el) return;
    if (count > 0) { el.textContent = count; el.style.display = 'inline-flex'; }
    else { el.style.display = 'none'; }
  }

  var _notifPanelOpen = false;
  function _toggleNotificationPanel() {
    var panel = $('notificationPanel');
    if (!panel) return;
    _notifPanelOpen = !_notifPanelOpen;
    panel.style.display = _notifPanelOpen ? 'block' : 'none';
    if (_notifPanelOpen) _loadNotificationPanel();
  }

  function _loadNotificationPanel() {
    var list = $('notificationList');
    if (!list) return;
    fetch('/api/suggestions?status=pending&type=roleplay').then(function(r) { return r.json(); }).then(function(d1) {
      fetch('/api/suggestions?status=pending&type=real_coach').then(function(r) { return r.json(); }).then(function(d2) {
        var items = (d1.suggestions || []).concat(d2.suggestions || []);
        if (!items.length) {
          list.innerHTML = '<p style="color:var(--muted);text-align:center;padding:12px">No pending suggestions</p>';
          return;
        }
        var html = '';
        items.forEach(function(s) {
          html += '<div style="border-bottom:1px solid var(--border);padding:8px 4px">';
          html += '<div style="font-weight:600;font-size:0.9em">' + esc(s.title) + '</div>';
          html += '<div style="color:var(--muted);font-size:0.8em;margin:2px 0">' + esc(s.description || '') + '</div>';
          html += '<div style="color:var(--muted);font-size:0.72em;margin-bottom:4px">from ' + esc(s.agent_id) + ' &middot; ' + esc(s.type) + '</div>';
          html += '<button onclick="_acceptSuggestion(\'' + esc(s.id) + '\',\'' + esc(s.agent_id) + '\')" style="background:var(--green);color:#fff;border:none;border-radius:4px;padding:3px 10px;font-size:0.78em;cursor:pointer;margin-right:4px">Accept</button>';
          html += '<button onclick="_rejectSuggestion(\'' + esc(s.id) + '\')" style="background:var(--red);color:#fff;border:none;border-radius:4px;padding:3px 10px;font-size:0.78em;cursor:pointer">Reject</button>';
          html += '</div>';
        });
        list.innerHTML = html;
      });
    }).catch(function() { list.innerHTML = '<p style="color:var(--red)">Error loading</p>'; });
  }

  function _acceptSuggestion(id, agentId) {
    fetch('/api/suggestions/' + id + '/accept', { method: 'POST' }).then(function() {
      pollSuggestionBadges();
      _loadNotificationPanel();
      /* Switch to the agent who suggested it in the chat */
      if (agentId && typeof switchAgent === 'function') switchAgent(agentId);
    });
  }

  function _rejectSuggestion(id) {
    fetch('/api/suggestions/' + id + '/reject', { method: 'POST' }).then(function() {
      pollSuggestionBadges();
      _loadNotificationPanel();
    });
  }

  /* ── Tier simulation banner ──────────────────────────── */
  function checkTierBanner() {
    fetch('/api/admin/tier').then(function(r) { return r.json(); }).then(function(d) {
      var banner = $('tierBanner');
      var level = $('tierBannerLevel');
      if (banner && d.override) {
        banner.style.display = 'block';
        if (level) level.textContent = d.tier;
      } else if (banner) {
        banner.style.display = 'none';
      }
    }).catch(function() {});
  }

  window._resetTierBanner = function() {
    fetch('/api/admin/tier', { method: 'DELETE' }).then(function() {
      checkTierBanner();
      applyFeatureGates();
    });
  };

  /* ════════════════════════════════════════════════════════
     Activity Bar + Side Panel (Cursor/VS Code style)
     ════════════════════════════════════════════════════════ */
  var _activeSideView = null;
  var _sidebarPinned = localStorage.getItem('vastian_sidebar_pinned') === 'true';
  var _explorerCache = null;

  /* ── Side panel pin toggle ───────────────────────────── */
  function _initPin() {
    var pin = $('spPin');
    var panel = $('sidePanel');
    if (!pin || !panel) return;
    _applySidebarLayout();
    pin.addEventListener('click', function() {
      _sidebarPinned = !_sidebarPinned;
      localStorage.setItem('vastian_sidebar_pinned', _sidebarPinned ? 'true' : 'false');
      _applySidebarLayout();
    });
  }

  function _applySidebarLayout() {
    var panel = $('sidePanel');
    var pin = $('spPin');
    if (!panel) return;
    if (_sidebarPinned) {
      panel.classList.remove('overlay');
      if (pin) pin.classList.add('pinned');
    } else {
      if (_activeSideView) panel.classList.add('overlay');
      if (pin) pin.classList.remove('pinned');
    }
  }

  /* ── Activity bar click handling ─────────────────────── */
  function _initActivityBar() {
    var btns = document.querySelectorAll('.ab-btn[data-view]');
    btns.forEach(function(btn) {
      btn.addEventListener('click', function() {
        var view = btn.dataset.view;
        if (view === _activeSideView) {
          /* Toggle off */
          _closeSidePanel();
          return;
        }
        /* Set active */
        btns.forEach(function(b) { b.classList.remove('active'); });
        btn.classList.add('active');
        _activeSideView = view;
        _openSidePanel(view);
      });
    });
    /* Click outside side panel to close (when unpinned) */
    document.addEventListener('click', function(e) {
      if (_sidebarPinned || !_activeSideView) return;
      var container = $('sidebarContainer');
      if (container && !container.contains(e.target)) {
        _closeSidePanel();
      }
    });
  }

  function _openSidePanel(view) {
    var panel = $('sidePanel');
    var title = $('spTitle');
    var content = $('spContent');
    if (!panel || !content) return;
    panel.classList.add('open');
    _applySidebarLayout();

    var views = {
      explorer:      { title: 'EXPLORER',       render: _renderExplorerView },
      pages:         { title: 'PAGES',           render: _renderPagesView },
      commands:      { title: 'COMMANDS',        render: _renderCommandsView },
      notifications: { title: 'NOTIFICATIONS',   render: _renderNotificationsView },
      activity:      { title: 'ACTIVITY',        render: _renderActivityView },
      archive:       { title: 'ARCHIVE',         render: _renderArchiveView },
      settings:      { title: 'SETTINGS',        render: _renderSettingsView }
    };
    var v = views[view];
    if (!v) return;
    if (title) title.textContent = v.title;
    v.render(content);
  }

  function _closeSidePanel() {
    var panel = $('sidePanel');
    if (panel) panel.classList.remove('open');
    _activeSideView = null;
    document.querySelectorAll('.ab-btn[data-view]').forEach(function(b) { b.classList.remove('active'); });
  }

  /* ── Explorer View: document tree ────────────────────── */
  function _renderExplorerView(container) {
    if (_explorerCache) {
      container.innerHTML = _explorerCache;
      _bindExplorerEvents(container);
      return;
    }
    container.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.82em">Loading documents...</div>';
    fetch('/api/agent-docs').then(function(r) { return r.json(); }).then(function(agents) {
      var arr = Array.isArray(agents) ? agents : (agents.agents || []);
      var html = '';
      arr.forEach(function(ag) {
        if (!ag.docs || !ag.docs.length) return;
        var agId = ag.agent_id || ag.id;
        var color = agentColor(agId);
        var agTier = _agentTierMap[agId] || '';
        var isGated = agTier === 'backend' && _appDeployMode !== 'cloud';
        html += '<div class="sp-tree-agent" data-agent="' + esc(agId) + '"' + (isGated ? ' style="opacity:0.6"' : '') + '>';
        html += '<span class="sp-tree-chevron">&#x25BE;</span>';
        html += '<span style="color:' + color + '">' + esc(ag.name || agId) + '</span>';
        if (isGated) html += '<span class="v-badge-cloud" style="margin-left:6px">Cloud</span>';
        html += '<span style="margin-left:auto;font-size:0.75em;color:var(--muted)">' + ag.docs.length + '</span>';
        html += '</div>';
        html += '<div class="sp-tree-files">';
        ag.docs.forEach(function(d) {
          var exists = d.exists !== false;
          var name = d.name || d;
          html += '<div class="sp-tree-file" data-agent="' + esc(ag.agent_id || ag.id) + '" data-doc="' + esc(name) + '">';
          html += '<span class="sp-dot ' + (exists ? 'exists' : 'missing') + '"></span>';
          html += esc(typeof name === 'string' ? name.replace('.md', '') : name);
          html += '</div>';
        });
        html += '</div>';
      });
      if (!html) html = '<div style="padding:12px;color:var(--muted);font-size:0.82em">No agent documents found.</div>';
      _explorerCache = html;
      container.innerHTML = html;
      _bindExplorerEvents(container);
    }).catch(function(e) {
      container.innerHTML = '<div style="padding:12px;color:var(--red)">Error: ' + esc(e.message) + '</div>';
    });
  }

  function _bindExplorerEvents(container) {
    container.querySelectorAll('.sp-tree-agent').forEach(function(el) {
      el.addEventListener('click', function() { el.classList.toggle('collapsed'); });
    });
    container.querySelectorAll('.sp-tree-file').forEach(function(el) {
      el.addEventListener('click', function(e) {
        e.stopPropagation();
        var agentId = el.dataset.agent;
        var docName = el.dataset.doc;
        if (agentId && docName) window._openDocTab(agentId, docName);
        /* Close panel if unpinned */
        if (!_sidebarPinned) _closeSidePanel();
      });
    });
  }

  /* ── Pages View: nav items ───────────────────────────── */
  function _renderPagesView(container) {
    var navItems = [
      { route: 'dashboard', icon: '&#9733;',       label: 'Home' },
      { route: 'boards',    icon: '&#x1F4CB;',     label: 'Boards',    badge: 'sp-badge-boards' },
      { route: 'sessions',  icon: '&#x1F4AC;',     label: 'Sessions',  badge: 'sp-badge-sessions' },
      { sep: true },
      { route: 'personas',  icon: '&#x1F52E;',     label: 'Personas',  badge: 'sp-badge-personas' },
      { route: 'scenarios', icon: '&#x1F3AF;',     label: 'Scenarios', badge: 'sp-badge-scenarios' },
      { route: 'plans',     icon: '&#x1F4D0;',     label: 'Plans',     badge: 'sp-badge-plans' },
      { route: 'metrics',   icon: '&#x1F4CA;',     label: 'Metrics' },
      { sep: true },
      { route: 'meetings',  icon: '&#x1F4DD;',     label: 'Meetings' },
      { route: 'councils',  icon: '&#x1F9E0;',     label: 'Councils',  badge: 'sp-badge-councils' },
      { route: 'documents', icon: '&#x1F4C4;',     label: 'Documents' },
      { sep: true },
      { route: 'settings',  icon: '&#x2699;',      label: 'Settings' },
      { route: 'backup',    icon: '&#x1F4BE;',     label: 'Backup' }
    ];

    var html = '';
    navItems.forEach(function(item) {
      if (item.sep) { html += '<div class="sp-sep"></div>'; return; }
      var active = currentPage === item.route ? ' active' : '';
      var locked = (_featureGates[item.route] === false) ? ' nav-locked' : '';
      html += '<div class="sp-nav-item' + active + locked + '" data-route="' + item.route + '">';
      html += '<span class="sp-nav-icon">' + item.icon + '</span>';
      html += '<span>' + esc(item.label) + '</span>';
      if (item.badge) html += '<span class="sp-nav-badge" id="' + item.badge + '" style="display:none"></span>';
      html += '</div>';
    });

    /* Tier indicator at bottom */
    html += '<div style="margin-top:auto;padding:8px 10px;font-size:0.65em;color:var(--muted)">__USER_TIER__</div>';

    container.innerHTML = html;

    /* Bind clicks */
    container.querySelectorAll('.sp-nav-item[data-route]').forEach(function(el) {
      el.addEventListener('click', function() {
        if (el.classList.contains('nav-locked')) return;
        var route = el.dataset.route;
        window.location.hash = '#/' + route;
        navigate(route);
        if (!_sidebarPinned) _closeSidePanel();
      });
    });

    /* Refresh badges for this view */
    pollBadges();
    pollSuggestionBadges();
  }

  /* ── Commands View: grouped slash commands ────────────── */
  function _renderCommandsView(container) {
    if (!chatCommands.length) {
      container.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.82em">Loading commands...</div>';
      /* Wait for chat meta to load */
      setTimeout(function() { _renderCommandsView(container); }, 500);
      return;
    }
    var groupOrder = ['navigation', 'tasks', 'collaboration', 'system', 'simulations', 'metrics', 'setup'];
    var groupLabels = {
      navigation: 'Core',
      tasks: 'Tasks',
      collaboration: 'Agents',
      system: 'System',
      simulations: 'Simulations',
      metrics: 'Data',
      setup: 'Advanced'
    };
    var grouped = {};
    chatCommands.forEach(function(c) {
      var g = c.topic || 'other';
      if (!grouped[g]) grouped[g] = [];
      grouped[g].push(c);
    });
    var html = '';
    groupOrder.forEach(function(g) {
      if (!grouped[g] || !grouped[g].length) return;
      var label = groupLabels[g] || g;
      /* Put simulations/metrics/setup at the bottom with "Advanced" label */
      if (g === 'simulations' || g === 'metrics' || g === 'setup') label = 'Advanced';
      html += '<div class="sp-cmd-group">' + esc(label) + '</div>';
      grouped[g].forEach(function(c) {
        html += '<div class="sp-cmd-item" data-cmd="' + esc(c.cmd) + '">';
        html += '<span class="sp-cmd-name">' + esc(c.cmd) + '</span>';
        html += '<span class="sp-cmd-desc">' + esc(c.desc || '') + '</span>';
        html += '</div>';
      });
    });
    /* Any remaining groups */
    Object.keys(grouped).forEach(function(g) {
      if (groupOrder.indexOf(g) !== -1) return;
      html += '<div class="sp-cmd-group">' + esc(g) + '</div>';
      grouped[g].forEach(function(c) {
        html += '<div class="sp-cmd-item" data-cmd="' + esc(c.cmd) + '">';
        html += '<span class="sp-cmd-name">' + esc(c.cmd) + '</span>';
        html += '<span class="sp-cmd-desc">' + esc(c.desc || '') + '</span>';
        html += '</div>';
      });
    });
    container.innerHTML = html;
    /* Bind clicks: insert command into chat input */
    container.querySelectorAll('.sp-cmd-item').forEach(function(el) {
      el.addEventListener('click', function() {
        var inp = $('chatInput');
        if (inp) {
          inp.value = el.dataset.cmd + ' ';
          inp.focus();
        }
        if (!_sidebarPinned) _closeSidePanel();
      });
    });
  }

  /* ── Notifications View ──────────────────────────────── */
  function _renderNotificationsView(container) {
    container.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.82em">Loading notifications...</div>';
    fetch('/api/notifications').then(function(r) { return r.json(); }).then(function(d) {
      var items = d.items || d.notifications || [];
      if (!items.length) {
        container.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.82em;text-align:center">No notifications</div>';
        return;
      }
      var html = '';
      items.forEach(function(n) {
        html += '<div style="padding:6px 10px;border-bottom:1px solid var(--border);font-size:0.85em">';
        html += '<div style="color:var(--text)">' + esc(n.title || n.message || '') + '</div>';
        if (n.agent_id) html += '<div style="font-size:0.78em;color:var(--muted)">' + esc(n.agent_id) + '</div>';
        html += '</div>';
      });
      container.innerHTML = html;
    }).catch(function() {
      container.innerHTML = '<div style="padding:12px;color:var(--muted)">Error loading notifications</div>';
    });
  }

  /* ── Activity View (agent actions + conversations) ────── */
  function _renderActivityView(container) {
    container.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.82em">Loading activity...</div>';
    /* Quick actions at top */
    var actionsHtml = '<div style="padding:8px 10px;border-bottom:1px solid var(--border);display:flex;gap:6px;flex-wrap:wrap">';
    actionsHtml += '<button class="v-btn v-btn-sm" onclick="window._openActivityView()">Full View</button>';
    actionsHtml += '<button class="v-btn v-btn-sm" onclick="window._openArchiveView()">Archive</button>';
    actionsHtml += '</div>';

    fetch('/api/activity-log').then(function(r) { return r.json(); }).then(function(d) {
      var items = d.activities || [];
      var html = actionsHtml;
      if (!items.length) {
        html += '<div style="padding:16px;color:var(--muted);font-size:0.82em;text-align:center">No recent activity</div>';
        container.innerHTML = html;
        return;
      }
      /* Check-in dropdowns per agent */
      var agentsSeen = {};
      items.forEach(function(act) {
        var aid = act.agent_id;
        if (!aid || agentsSeen[aid]) return;
        agentsSeen[aid] = true;
        var color = agentColor(aid);
        var name = _agentNameMap[aid] || aid;
        html += '<div style="padding:6px 10px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;cursor:pointer" onclick="window._openCheckinDropdown(\'' + esc(aid) + '\')">';
        html += '<span style="width:6px;height:6px;border-radius:50%;background:' + color + '"></span>';
        html += '<span style="font-size:0.82em;font-weight:600;color:' + color + '">' + esc(name) + '</span>';
        html += '<span style="font-size:0.7em;color:var(--muted);margin-left:auto">Check-in &#x25BE;</span>';
        html += '</div>';
      });
      /* Recent actions */
      html += '<div style="padding:6px 10px;font-size:0.72em;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:0.5px;margin-top:4px">Recent</div>';
      items.slice(0, 15).forEach(function(act) {
        var color = agentColor(act.agent_id);
        var icon = act.type === 'delegation' ? '&#x2699;' : '&#x1F514;';
        html += '<div style="padding:5px 10px;border-bottom:1px solid rgba(48,54,61,0.3);font-size:0.82em;cursor:pointer" onclick="window._openActivityView()">';
        html += '<div style="display:flex;align-items:center;gap:6px">';
        html += '<span style="font-size:0.8em">' + icon + '</span>';
        html += '<span style="color:' + color + ';font-weight:600;font-size:0.9em">' + esc(_agentNameMap[act.agent_id] || act.agent_id) + '</span>';
        html += '</div>';
        html += '<div style="color:var(--muted);font-size:0.85em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px">' + esc(act.title) + '</div>';
        html += '</div>';
      });
      container.innerHTML = html;
    }).catch(function() {
      container.innerHTML = actionsHtml + '<div style="padding:12px;color:var(--muted)">Error loading activity</div>';
    });
  }

  /* ── Settings View ───────────────────────────────────── */
  function _renderSettingsView(container) {
    var html = '';
    html += '<div class="sp-nav-item" data-route="settings"><span class="sp-nav-icon">&#x2699;</span><span>General Settings</span></div>';
    html += '<div class="sp-nav-item" data-route="settings/api-keys"><span class="sp-nav-icon">&#x1F510;</span><span>API Keys</span></div>';
    html += '<div class="sp-nav-item" data-route="settings/suggestions"><span class="sp-nav-icon">&#x1F9E0;</span><span>Agent Suggestions</span></div>';
    html += '<div class="sp-nav-item" data-route="settings/storage"><span class="sp-nav-icon">&#x1F4E6;</span><span>Storage Provider</span></div>';
    html += '<div class="sp-nav-item" data-route="settings/theme"><span class="sp-nav-icon">&#x1F3A8;</span><span>Theme &amp; Display</span></div>';
    html += '<div class="sp-nav-item" data-route="settings/tokens"><span class="sp-nav-icon">&#x1F4B0;</span><span>Token Budget</span></div>';
    html += '<div class="sp-nav-item" data-route="settings/personas"><span class="sp-nav-icon">&#x1F465;</span><span>Personas</span></div>';
    html += '<div class="sp-nav-item" data-route="settings/integrations"><span class="sp-nav-icon">&#x1F50C;</span><span>Integrations</span></div>';
    html += '<div class="sp-nav-item" data-route="backup"><span class="sp-nav-icon">&#x1F4BE;</span><span>Backup &amp; Restore</span></div>';
    container.innerHTML = html;
    container.querySelectorAll('.sp-nav-item[data-route]').forEach(function(el) {
      el.addEventListener('click', function() {
        var route = el.dataset.route;
        window.location.hash = '#/' + route;
        navigate(route);
        if (!_sidebarPinned) _closeSidePanel();
      });
    });
  }

  /* ── Archive View (sidebar) ─────────────────────────────── */
  function _renderArchiveView(container) {
    container.innerHTML = '<div style="padding:12px;color:var(--muted);font-size:0.82em">Loading archive...</div>';
    fetch('/api/archive').then(function(r) { return r.json(); }).then(function(d) {
      var items = d.items || [];
      var html = '<div style="padding:8px 10px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center">';
      html += '<span style="font-size:0.78em;color:var(--muted)">' + items.length + ' archived items</span>';
      html += '<button class="v-btn v-btn-sm" onclick="window._openArchiveView()">Full View</button>';
      html += '</div>';
      if (!items.length) {
        html += '<div style="padding:20px;text-align:center;color:var(--muted);font-size:0.82em">No archived items.<br><span style="font-size:0.9em">Items are retained for 60 days.</span></div>';
      }
      items.slice(0, 15).forEach(function(item, idx) {
        html += '<div style="padding:6px 10px;border-bottom:1px solid rgba(48,54,61,0.3);font-size:0.82em">';
        html += '<div style="display:flex;align-items:center;gap:6px">';
        html += '<span style="font-size:0.8em">&#x1F4E6;</span>';
        html += '<span style="color:var(--text);font-weight:600">' + esc(item.type || 'item') + '</span>';
        html += '<span style="margin-left:auto;font-size:0.7em;color:var(--muted)">' + esc(item.expires_at || '') + '</span>';
        html += '</div>';
        html += '<div style="color:var(--muted);font-size:0.85em;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc(item.title || item.id) + '</div>';
        html += '</div>';
      });
      container.innerHTML = html;
    }).catch(function() {
      container.innerHTML = '<div style="padding:12px;color:var(--muted)">Error loading archive</div>';
    });
  }

  /* ── Pop-out side view to center tab ──────────────────── */
  window._popoutSideView = function() {
    if (!_activeSideView) return;
    var view = _activeSideView;
    var viewLabels = {
      explorer: 'Explorer',
      pages: 'Pages',
      commands: 'Commands',
      notifications: 'Notifications',
      activity: 'Activity',
      settings: 'Settings'
    };
    var viewIcons = {
      explorer: '&#x1F4C1;',
      pages: '&#x25A6;',
      commands: '&#x2318;',
      notifications: '&#x1F514;',
      activity: '&#x1F4CA;',
      settings: '&#x2699;'
    };
    var viewRenders = {
      explorer: _renderExplorerView,
      pages: _renderPagesView,
      commands: _renderCommandsView,
      notifications: _renderNotificationsView,
      activity: _renderActivityView,
      settings: _renderSettingsView
    };
    var label = viewLabels[view] || view;
    var icon = viewIcons[view] || '&#x1F4C4;';
    /* Open as a tab */
    var tab = _openTab({
      label: label,
      icon: icon,
      type: 'view',
      viewKey: view,
      closable: true
    });
    var pane = $('tabPane-' + tab.id);
    if (pane && viewRenders[view]) {
      pane.style.padding = '12px 20px';
      viewRenders[view](pane);
    }
    /* Close the side panel */
    _closeSidePanel();
  };

  /* ── Init ─────────────────────────────────────────────── */
  /* Make navigate global for onclick handlers in rendered content */
  window.navigate = navigate;
  loadChatMeta();
  _initActivityBar();
  _initPin();
  applyFeatureGates();
  pollBadges();
  pollSuggestionBadges();
  checkTierBanner();
  /* Auto-open Explorer panel on load so sidebar has content immediately */
  _activeSideView = 'explorer';
  document.querySelectorAll('.ab-btn[data-view]').forEach(function(b) { b.classList.toggle('active', b.dataset.view === 'explorer'); });
  _openSidePanel('explorer');
  setInterval(applyFeatureGates, 60000);
  setInterval(pollBadges, 30000);
  setInterval(pollSuggestionBadges, 30000);

  /* ── Version polling for reload banner ──────────────── */
  var _appBuild = null;
  function _pollVersion() {
    fetch('/api/version').then(function(r) { return r.json(); }).then(function(v) {
      if (!_appBuild) { _appBuild = v.build; return; }
      if (v.build !== _appBuild) {
        var banner = document.createElement('div');
        banner.id = 'versionBanner';
        banner.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;background:linear-gradient(90deg,#1f6feb,#a371f7);color:#fff;text-align:center;padding:8px 16px;font-size:0.85em;font-weight:600;display:flex;align-items:center;justify-content:center;gap:12px';
        banner.innerHTML = '&#x1F504; A new version is available.' +
          '<button onclick="location.reload()" style="background:#fff;color:#1f6feb;border:none;border-radius:6px;padding:4px 14px;font-weight:600;cursor:pointer;font-size:0.9em">Reload</button>' +
          '<button onclick="this.parentElement.remove()" style="background:transparent;color:#fff;border:none;cursor:pointer;font-size:1.1em;opacity:0.7">&times;</button>';
        if (!document.getElementById('versionBanner')) document.body.prepend(banner);
      }
    }).catch(function() {});
  }
  _pollVersion();
  setInterval(_pollVersion, 60000);

  if (!window.location.hash) window.location.hash = '#/dashboard';
  navigate(getRoute());
  /* Check first-run on initial load only */
  if (getRoute() === 'dashboard') checkFirstRun();
})();
</script>
</body>
</html>
"""
