/**
 * Home23 Dashboard — Vanilla JS
 *
 * Current dashboard agent view on Home tab (ReginaCosmo layout).
 * COSMO 2.3 embedded via iframe on COSMO tab.
 * Secondary agent tabs created on demand.
 */

// ── Config ──

const REFRESH_MS = 30000;
const HOME_THOUGHT_ROTATE_MS = 16000;
const GOOD_LIFE_API_TIMEOUT_MS = 12000;
let agents = [];
// Back-compat variable name: this is the agent that owns the current dashboard.
let primaryAgent = null;
let homePrimaryAgent = null;
let currentTab = 'home';
let dashboardScopeRegistry = null;
let cosmo23Url = '';
let evobrewUrl = '';
let cosmo23Loaded = false;
let cosmoOnline = false;
let intelRefreshInterval = null;
let homeThoughtRotationTimer = null;
let homeTileLayout = [];
let homeTileLayoutSignature = '';
let homeTileCustomRefreshers = new Map();
let homeTileCustomState = new Map();
let homeTilesRefreshPromise = null;
let tileActionDialogState = null;
let homeTileBroadcast = null;
const goodLifeSurfaceState = new Map();
const goodLifeFleetState = new Map();
let goodLifeOverlayState = {
  scope: 'home',
  tab: 'issues',
  selectedProblemId: null,
};
let workersState = {
  workers: [],
  templates: [],
  runs: [],
  receipt: null,
  lastLoadedAt: null,
};

function currentAgentLabel(fallback = 'This agent') {
  return primaryAgent?.displayName || primaryAgent?.name || fallback;
}

const DASHBOARD_SCOPE_FALLBACK = {
  home: {
    kind: 'dashboard',
    chip: 'This Agent',
    summaryTemplate: 'Home is the live surface for {{dashboardAgent}}. Tiles, pulse, chat, and feed data belong to this dashboard agent.',
  },
  intelligence: {
    kind: 'dashboard',
    chip: 'This Agent',
    summaryTemplate: "Intelligence is scoped to {{dashboardAgent}}. It reflects this dashboard agent's internal state and live system observations.",
  },
  workers: {
    kind: 'mixed',
    chip: 'Workers',
    summaryTemplate: 'Workers are reusable house capabilities. They run through {{dashboardAgent}}\'s connector, keep their own workspaces, and feed receipts back into house-agent memory.',
  },
  query: {
    kind: 'dashboard',
    chip: 'This Agent',
    summaryTemplate: "Query targets {{dashboardAgent}}'s brain by default. PGS and query defaults resolve against the current dashboard agent unless you override them.",
  },
  'brain-map': {
    kind: 'dashboard',
    chip: 'This Agent',
    summaryTemplate: "Brain Map opens {{dashboardAgent}}'s graph by default. It uses the current dashboard brain route when resolving the graph view.",
  },
  about: {
    kind: 'shared',
    chip: 'Shared',
    summaryTemplate: 'About is a shared system surface. It describes the Home23 install rather than one specific agent.',
  },
  settings: {
    kind: 'mixed',
    chip: 'Mixed',
    summaryTemplate: 'Settings mixes house-wide and agent-scoped configuration. Use the Settings page scope controls to see which areas target {{dashboardAgent}} versus the whole house.',
  },
  cosmo23: {
    kind: 'external',
    chip: 'External',
    summaryTemplate: 'cosmo23 is an external shared research surface. It is linked from this dashboard but not owned by one Home23 agent.',
  },
  evobrew: {
    kind: 'external',
    chip: 'External',
    summaryTemplate: 'evobrew is an external shared surface. The dashboard deep-links it with the current agent, but the service itself is house-managed.',
  },
  agent: {
    kind: 'peer',
    chip: 'Other Agent',
    summaryTemplate: 'This panel shows {{peerAgent}} from inside {{dashboardAgent}}\'s dashboard. It is a peer-agent view, not the owner of the current dashboard shell.',
  },
};

function getDashboardScopeMeta(tabKey) {
  const key = tabKey && tabKey.startsWith('agent-') ? 'agent' : (tabKey || currentTab);
  const registry = dashboardScopeRegistry?.tabs || {};
  return registry[key] || DASHBOARD_SCOPE_FALLBACK[key] || DASHBOARD_SCOPE_FALLBACK.home;
}

function renderDashboardScopeText(meta, tabKey = currentTab) {
  const peerName = tabKey && tabKey.startsWith('agent-')
    ? agents.find(a => a.name === tabKey.replace('agent-', ''))?.displayName || tabKey.replace('agent-', '')
    : '';
  const replacements = {
    dashboardAgent: currentAgentLabel('this dashboard agent'),
    primaryAgent: homePrimaryAgent?.displayName || homePrimaryAgent?.name || currentAgentLabel('the Home23 primary agent'),
    peerAgent: peerName || 'the other agent',
  };
  return String(meta?.summaryTemplate || '').replace(/\{\{(\w+)\}\}/g, (_, key) => replacements[key] || '');
}

function refreshDashboardScopeUI() {
  const isCosmoTab = currentTab === 'cosmo23';
  document.body.classList.toggle('h23-external-focus', isCosmoTab);
  if (!isCosmoTab) setCosmoHomeDrawerOpen(false);

  document.querySelectorAll('.h23-tab[data-tab], .h23-tab[data-scope-tab]').forEach(tab => {
    const tabKey = tab.dataset.scopeTab || tab.dataset.tab;
    const meta = getDashboardScopeMeta(tabKey);
    if (!tab.dataset.tabLabel) tab.dataset.tabLabel = tab.textContent.trim();
    const label = tab.dataset.tabLabel;
    const showChip = meta.kind === 'external' || meta.kind === 'peer';
    tab.innerHTML = `<span class="h23-tab-label">${label}</span>${showChip ? `<span class="h23-tab-scope-chip scope-${meta.kind}">${meta.chip}</span>` : ''}`;
    tab.title = renderDashboardScopeText(meta, tabKey);
  });

  const scopeMeta = getDashboardScopeMeta(currentTab);
  const kicker = document.getElementById('dashboard-scope-kicker');
  const summary = document.getElementById('dashboard-scope-summary');
  if (kicker) {
    const scopeLabel = scopeMeta.kind === 'dashboard'
      ? 'This Dashboard Agent'
      : scopeMeta.kind === 'peer'
        ? 'Peer Agent Surface'
        : scopeMeta.kind === 'mixed'
          ? 'Mixed Surface'
          : scopeMeta.kind === 'external'
            ? 'External Surface'
            : 'Shared Surface';
    kicker.textContent = `${scopeLabel} · ${scopeMeta.chip}`;
  }
  if (summary) {
    summary.textContent = renderDashboardScopeText(scopeMeta, currentTab);
  }
}

async function loadDashboardScopeRegistry() {
  try {
    const res = await fetch('/home23/api/scope');
    if (!res.ok) return;
    dashboardScopeRegistry = await res.json();
  } catch { /* best effort */ }
}

function refreshDashboardIdentityUI() {
  const chip = document.getElementById('dashboard-identity-chip');
  const headerAgent = document.getElementById('header-agent-name');
  if (!chip || !primaryAgent) return;

  const currentName = primaryAgent.displayName || primaryAgent.name || 'Agent';
  const homePrimaryName = homePrimaryAgent?.displayName || homePrimaryAgent?.name || currentName;
  const isHomePrimary = !!primaryAgent.isPrimary || primaryAgent.name === homePrimaryAgent?.name;
  if (headerAgent) headerAgent.textContent = currentName;
  chip.textContent = isHomePrimary ? 'PRIMARY AGENT' : 'SECONDARY AGENT';
  chip.title = isHomePrimary
    ? `${currentName} is the current dashboard and the Home23 primary agent.`
    : `${currentName} owns this dashboard. ${homePrimaryName} is the Home23 primary agent.`;
  document.title = `Home23 — ${currentName}`;
}

// ── Engine Pulse State ──
const enginePulse = {
  state: 'unknown',    // awake, sleeping, thinking
  phase: '',           // current activity description
  energy: 0,
  cycle: 0,
  lastEventTime: null, // Date of last engine event
  lastThought: null,   // timestamp of last thought
};

// ── Init ──

async function init() {
  updateClocks();
  setInterval(updateClocks, 10000);
  initParticles();
  await loadDashboardScopeRegistry();
  await loadAgents();
  renderAgentTabs();
  refreshDashboardScopeUI();
  setupTabHandlers();
  setupHomeLayoutHandlers();
  setupTileActionHandlers();
  setupHomeTileBroadcast();
  setupWorkersSurface();
  await loadHomeLayoutConfig({ force: true });
  connectEnginePulse();
  loadHomeTiles().catch(() => { /* initial home load is best-effort */ });
  loadWorkersSurface().catch(() => { /* workers connector may be offline during early boot */ });
  startHomeThoughtRotation();
  startAutoRefresh();
  updateCosmoIndicator();
  setInterval(updateCosmoIndicator, REFRESH_MS);

  // Update pulse "ago" timer every second
  setInterval(updatePulseAgo, 1000);

  // Check for Home23 updates
  checkUpdateNotification();

  // Poll notifications (pending thought-actions from cognitive cycles)
  updateNotificationBadge();
  setInterval(updateNotificationBadge, 15000);

  // Poll autonomous actions (⚡ executions from ACT: tag)
  updateActionsBadge();
  setInterval(updateActionsBadge, 15000);

  // Live problems badge — polls every 20s. The engine verifies on its own
  // cadence (~90s), so 20s is plenty fresh for the dashboard.
  updateProblemsBadge();
  setInterval(updateProblemsBadge, 20000);

  // Signals badge — wins, resolutions, positive observations. Polls 30s.
  updateSignalsBadge();
  setInterval(updateSignalsBadge, 30000);

  // Brain storage badge — polls every 30s. Shows disk node count and flags
  // mismatch between disk-side snapshot and in-memory state.
  updateBrainStorageBadge();
  setInterval(updateBrainStorageBadge, 30000);

  // Pulse tile (Jerry's voice). Tile text updates when a new remark lands;
  // rotating stat under it cycles every 8s.
  updatePulseTile();
  setInterval(updatePulseTile, 20000);
  startPulseRotation();

  // Workers are connector-backed and cheap to refresh. Keep the user-facing
  // status current without pulling on the full engine loop.
  setInterval(() => {
    if (currentTab === 'workers') loadWorkersSurface().catch(() => {});
  }, 30000);
}

// ── Pulse tile (Jerry's voice + rotating stats) ──

let _pulseLatest = null;
let _pulseStatsIdx = 0;
let _pulseRotateTimer = null;
let _pulseLastRemarkId = null;

async function updatePulseTile() {
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/pulse/latest`);
    if (!r.ok) return;
    const data = await r.json();
    _pulseLatest = data.remark;
    renderPulseTile();
  } catch { /* silent */ }
}

function renderPulseTile() {
  const textEl = document.getElementById('pulse-remark-text');
  const ageEl = document.getElementById('pulse-remark-age');
  if (!textEl) return;

  if (!_pulseLatest || !_pulseLatest.text) {
    textEl.textContent = `${currentAgentLabel()} has not spoken yet. Waiting for the first pulse cycle.`;
    textEl.style.color = 'var(--text-muted)';
    textEl.style.fontStyle = 'italic';
    if (ageEl) ageEl.textContent = '';
    return;
  }

  textEl.textContent = _pulseLatest.text;
  textEl.style.color = 'var(--text-primary)';
  textEl.style.fontStyle = 'normal';

  if (ageEl) {
    const age = timeSince(new Date(_pulseLatest.ts));
    ageEl.textContent = `cycle ${_pulseLatest.cycle ?? '?'} · ${age}`;
  }

  // If remark changed, reset the rotating stats to start
  if (_pulseLatest.id !== _pulseLastRemarkId) {
    _pulseLastRemarkId = _pulseLatest.id;
    _pulseStatsIdx = 0;
    rotatePulseStat();
  }
}

function rotatePulseStat() {
  const el = document.getElementById('pulse-rotating-stat');
  if (!el || !_pulseLatest) return;

  const stats = _pulseLatest.stats || [];
  if (stats.length === 0) {
    el.textContent = '';
    return;
  }

  const stat = stats[_pulseStatsIdx % stats.length];
  el.innerHTML = `<span style="font-size:13px;margin-right:6px;">${stat.icon || '•'}</span><span style="color:var(--text-secondary);">${escapeHtml(String(stat.label))}:</span> <span style="color:var(--text-primary);font-weight:500;">${escapeHtml(String(stat.value))}</span>`;
  _pulseStatsIdx++;
}

function startPulseRotation() {
  if (_pulseRotateTimer) return;
  rotatePulseStat();
  _pulseRotateTimer = setInterval(rotatePulseStat, 8000);
}

async function openPulseHistoryPanel() {
  // Reuse the actions overlay DOM — hijack it for pulse history
  const overlay = document.getElementById('actions-overlay');
  const list = document.getElementById('actions-list');
  const title = overlay?.querySelector('.h23-brainlog-title');
  if (!overlay || !list) return;
  if (title) title.textContent = `💬 Pulse History — ${currentAgentLabel('Agent')}'s Remarks`;
  overlay.style.display = 'flex';
  list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">Loading...</div>';
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/pulse/history?limit=40`);
    const data = await r.json();
    const remarks = data.remarks || [];
    if (remarks.length === 0) {
      list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">No remarks yet.</div>';
      return;
    }
    list.innerHTML = remarks.map(r => {
      const ts = r.ts ? new Date(r.ts).toLocaleString() : '';
      const briefCount =
        (r.brief?.notable?.length || 0) +
        (r.brief?.signals?.length || 0) +
        (r.brief?.liveProblems?.open?.length || 0) +
        (r.brief?.liveProblems?.chronic?.length || 0) +
        (r.brief?.liveProblems?.resolvedJustNow?.length || 0);
      const briefSummary = briefCount > 0 ? `${briefCount} signal${briefCount === 1 ? '' : 's'} fed this remark` : 'quiet context';
      return `
        <div style="padding:12px 14px;margin-bottom:8px;background:rgba(255,255,255,0.02);border-left:3px solid #5ac8fa;">
          <div style="font-size:12px;color:rgba(255,255,255,0.5);margin-bottom:6px;">
            cycle ${r.cycle ?? '?'} · ${r.model || 'unknown model'} · ${ts}
          </div>
          <div style="color:#fff;font-size:14px;line-height:1.5;margin-bottom:6px;">${escapeHtmlNotif(r.text || '')}</div>
          <div style="font-size:11px;color:rgba(255,255,255,0.4);">${briefSummary}</div>
        </div>`;
    }).join('');
  } catch (err) {
    list.innerHTML = `<div style="color:#ff6b6b;padding:20px;">Failed to load: ${err.message}</div>`;
  }
}

async function updateActionsBadge() {
  try {
    const r = await fetch(`${dashboardBaseUrl()}/home23/api/settings/agency/recent?limit=100`);
    if (!r.ok) return;
    const data = await r.json();
    const actions = data.actions || [];
    // Count outcome events in the last hour
    const oneHourAgo = Date.now() - 3600 * 1000;
    const recent = actions.filter(a => a.phase === 'outcome' && a.ts && new Date(a.ts).getTime() >= oneHourAgo);
    const el = document.getElementById('pulse-actions');
    const sep = document.getElementById('pulse-actions-sep');
    const badge = document.getElementById('pulse-actions-badge');
    if (!el || !badge) return;
    if (recent.length > 0) {
      el.style.display = '';
      if (sep) sep.style.display = '';
      badge.textContent = `⚡ ${recent.length}`;
      const rejected = recent.filter(a => a.status === 'rejected').length;
      badge.style.color = rejected > 0 ? '#ffb347' : '#30d158';
    } else {
      el.style.display = 'none';
      if (sep) sep.style.display = 'none';
    }
  } catch { /* silent */ }
}

async function openActionsPanel() {
  const overlay = document.getElementById('actions-overlay');
  const list = document.getElementById('actions-list');
  if (!overlay || !list) return;
  overlay.style.display = 'flex';
  list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">Loading...</div>';
  try {
    const r = await fetch(`${dashboardBaseUrl()}/home23/api/settings/agency/recent?limit=200`);
    const data = await r.json();
    const actions = data.actions || [];
    if (actions.length === 0) {
      list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">No actions yet. Cognitive cycles emit <code>ACT:</code> tags which the dispatcher executes — when they do, they\'ll show up here.</div>';
      return;
    }
    // Pair intent + outcome by matching action+role+cycle+target
    const chronological = [...actions].reverse();
    const pairs = [];
    const open = new Map();
    for (const ev of chronological) {
      const k = `${ev.action}|${ev.role}|${ev.cycle}|${ev.target || ''}`;
      if (ev.phase === 'intent') open.set(k, ev);
      else if (ev.phase === 'outcome') {
        pairs.push({ intent: open.get(k), outcome: ev });
        open.delete(k);
      }
    }
    for (const [, intent] of open) pairs.push({ intent, outcome: null });
    pairs.reverse();

    list.innerHTML = pairs.slice(0, 80).map(({ intent, outcome }) => {
      const status = outcome?.status || 'in_flight';
      const col = status === 'success' ? '#30d158'
        : status === 'dry_run' ? '#5ac8fa'
        : status === 'rejected' ? '#ff6b6b'
        : 'rgba(255,255,255,0.5)';
      const ts = outcome?.ts || intent?.ts;
      const time = ts ? new Date(ts).toLocaleString() : '';
      const reason = intent?.reason ? `<div style="font-size:12px;color:rgba(255,255,255,0.6);margin-top:4px;">${escapeHtmlNotif(intent.reason)}</div>` : '';
      const detail = outcome?.detail ? `<div style="font-size:12px;color:${col};margin-top:4px;">${escapeHtmlNotif(outcome.detail)}</div>` : '';
      return `
        <div style="padding:10px 12px;margin-bottom:8px;background:rgba(255,255,255,0.02);border-left:3px solid ${col};">
          <div style="display:flex;justify-content:space-between;gap:12px;">
            <div style="flex:1;">
              <div style="font-size:12px;color:rgba(255,255,255,0.5);margin-bottom:4px;">
                <span style="color:${col};font-weight:600;text-transform:uppercase;">${status}</span>
                · <code style="color:#5ac8fa;">${escapeHtmlNotif(intent?.action || '?')}</code>${intent?.target ? ' → ' + escapeHtmlNotif(intent.target) : ''}
                · cycle ${intent?.cycle || '?'}
                · ${intent?.role || '?'}
                · ${time}
              </div>
              ${reason}
              ${detail}
            </div>
          </div>
        </div>`;
    }).join('');
  } catch (err) {
    list.innerHTML = `<div style="color:#ff6b6b;padding:20px;">Failed to load: ${err.message}</div>`;
  }
}

function closeActionsPanel() {
  const overlay = document.getElementById('actions-overlay');
  if (overlay) overlay.style.display = 'none';
}

// ── Live Problems (verifier-backed ground truth) ──
let _liveProblems = { problems: [], snapshot: null };
let _problemEditingId = null;

async function updateProblemsBadge() {
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/live-problems`);
    if (!r.ok) return;
    const data = await r.json();
    _liveProblems = data;
    const el = document.getElementById('pulse-problems');
    const sep = document.getElementById('pulse-problems-sep');
    const badge = document.getElementById('pulse-problems-badge');
    if (!el || !badge) return;
    if (!data.available) {
      el.style.display = 'none';
      if (sep) sep.style.display = 'none';
      return;
    }
    const s = data.snapshot || { counts: { open: 0, chronic: 0, resolved: 0 }, resolvedJustNow: [] };
    const openCount = s.counts.open + s.counts.chronic;
    const resolvedCount = (s.resolvedJustNow || []).length;
    el.style.display = '';
    if (sep) sep.style.display = '';
    if (s.counts.chronic > 0) {
      badge.textContent = `🩺 ${openCount} (${s.counts.chronic} chronic)`;
      badge.style.color = '#ff6b6b';
    } else if (openCount > 0) {
      badge.textContent = `🩺 ${openCount}`;
      badge.style.color = '#ffb347';
    } else if (resolvedCount > 0) {
      badge.textContent = `🩺 all clear ✓`;
      badge.style.color = '#30d158';
    } else {
      badge.textContent = `🩺 ok`;
      badge.style.color = 'rgba(255,255,255,0.45)';
    }
  } catch { /* silent */ }
}

async function openProblemsPanel() {
  const overlay = document.getElementById('problems-overlay');
  if (!overlay) return;
  overlay.style.display = 'flex';
  await renderProblemsList();
}

function closeProblemsPanel() {
  const overlay = document.getElementById('problems-overlay');
  if (overlay) overlay.style.display = 'none';
}

async function renderProblemsList() {
  const list = document.getElementById('problems-list');
  if (!list) return;
  list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">Loading...</div>';
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/live-problems`);
    const data = await r.json();
    _liveProblems = data;
    if (!data.available) {
      list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">Live-problems not available (engine not running or not wired).</div>';
      return;
    }
    const problems = (data.problems || []).slice().sort((a, b) => {
      const rank = { chronic: 0, open: 1, unverifiable: 2, resolved: 3 };
      return (rank[a.state] ?? 9) - (rank[b.state] ?? 9);
    });
    if (problems.length === 0) {
      list.innerHTML = '<div style="color:rgba(255,255,255,0.5);padding:20px;">No problems tracked. Add one below, or the engine will seed defaults on next start.</div>';
      return;
    }
    list.innerHTML = `${renderProblemsOperatorSummary(data, problems)}${problems.map(p => renderProblemCard(p)).join('')}`;
  } catch (err) {
    list.innerHTML = `<div style="color:#ff6b6b;padding:20px;">Failed to load: ${err.message}</div>`;
  }
}

function problemLastAttempt(problem = {}) {
  const attempts = Array.isArray(problem.remediationLog) ? problem.remediationLog : [];
  return attempts.length ? attempts[attempts.length - 1] : null;
}

function problemRepairText(problem = {}) {
  if (problem.state === 'resolved') {
    if (problem.fixRecipe?.summary) return problem.fixRecipe.summary;
    if (problem.lastResult?.detail) return `verified clear: ${problem.lastResult.detail}`;
    return 'verified clear';
  }
  const latest = problemLastAttempt(problem);
  if (latest?.detail) return latest.detail;
  if (latest?.type) return `${latest.type} ${latest.outcome || 'recorded'}`;
  const next = goodLifeNextRemediation(problem);
  if (next.type) return `next step: ${next.type}${next.text ? ` - ${next.text}` : ''}`;
  return next.text || 'no remediation plan recorded';
}

function problemUserText(problem = {}) {
  if (problem.state === 'resolved') return 'nothing; this issue is resolved';
  if (problem.escalated) return 'manual review needed; autonomous remediation has escalated this issue';
  if (goodLifeNeedsUser(problem)) {
    const next = goodLifeNextRemediation(problem);
    return next.text || 'manual/user intervention is the next remediation step';
  }
  return 'not needed yet; autonomous remediation can continue';
}

function renderProblemsOperatorSummary(data, problems) {
  const snapshot = data?.snapshot || {};
  const counts = snapshot.counts || {};
  const open = Number(counts.open || 0);
  const chronic = Number(counts.chronic || 0);
  const unverifiable = Number(counts.unverifiable || 0);
  const interventionRequired = Number(counts.interventionRequired || 0);
  const active = problems.filter((p) => ['open', 'chronic', 'unverifiable'].includes(p.state));
  const primary = active.find(goodLifeNeedsUser) || active[0] || null;
  const headline = interventionRequired > 0
    ? `${interventionRequired} issue${interventionRequired === 1 ? '' : 's'} need jtr`
    : active.length > 0
      ? `${active.length} issue${active.length === 1 ? '' : 's'} under autonomous repair`
      : 'No active verified issues';
  const severity = interventionRequired > 0 ? 'needs-user' : active.length > 0 ? 'repairing' : 'clear';
  return `
    <div class="h23-problems-operator ${severity}">
      <div>
        <label>Operator Status</label>
        <strong>${escapeHtml(headline)}</strong>
        <span>${escapeHtml(`${open} open / ${chronic} chronic / ${unverifiable} unverifiable`)}</span>
      </div>
      <div>
        <label>Current Issue</label>
        <strong>${escapeHtml(primary?.claim || 'registry is clear')}</strong>
        <span>${escapeHtml(primary?.lastResult?.detail || primary?.detail || 'no failing verifier result')}</span>
      </div>
      <div>
        <label>Fix Path</label>
        <strong>${escapeHtml(primary ? problemRepairText(primary) : 'no repair path active')}</strong>
        <span>${escapeHtml(primary ? `remediation step ${Number(primary.stepIndex || 0)} / ${(primary.remediation || []).length}` : 'no active remediation')}</span>
      </div>
      <div>
        <label>Needed From You</label>
        <strong>${escapeHtml(primary ? problemUserText(primary) : 'nothing right now')}</strong>
        <span>${escapeHtml(primary?.id || 'all tracked verifiers are clear or resolved')}</span>
      </div>
    </div>
  `;
}

function renderProblemCard(p) {
  const stateColor = {
    open: '#ffb347', chronic: '#ff6b6b', resolved: '#30d158', unverifiable: '#888',
  }[p.state] || '#888';
  const stateLabel = p.state.toUpperCase();
  const ageMin = p.openedAt ? Math.max(0, Math.round((Date.now() - Date.parse(p.openedAt)) / 60000)) : null;
  const last = p.lastResult ? `${p.lastResult.ok ? 'ok' : 'fail'}: ${escapeHtml(p.lastResult.detail || '')}` : 'not yet checked';
  const lastChecked = p.lastCheckedAt ? ` · checked ${timeSinceSafe(p.lastCheckedAt)}` : '';
  const recentRem = (p.remediationLog || []).slice(-3).reverse();
  const stepsLabel = `step ${(p.stepIndex || 0)}/${(p.remediation || []).length}`;
  const needsUser = goodLifeNeedsUser(p);
  const currentLabel = p.state === 'resolved' ? 'Verifier result' : 'What broke';
  const repairLabel = p.state === 'resolved' ? 'Resolution' : 'What Home23 is doing';
  const originTag = p.seedOrigin && p.seedOrigin !== 'system'
    ? ` <span style="color:rgba(255,255,255,0.4);font-size:10px;background:rgba(255,255,255,0.04);padding:1px 5px;border-radius:3px;text-transform:uppercase;letter-spacing:0.5px;">${escapeHtml(p.seedOrigin)}</span>`
    : '';
  // Active dispatch banner — Tier-2 agent is working on this right now.
  let dispatchBanner = '';
  if (p.dispatchedAt && !p.lastResult?.ok) {
    const step = (p.remediation || [])[p.stepIndex || 0];
    const budget = step?.args?.budgetHours ?? 12;
    const elapsed = (Date.now() - Date.parse(p.dispatchedAt)) / 3600000;
    const pct = Math.min(100, Math.round((elapsed / budget) * 100));
    dispatchBanner = `<div style="background:rgba(90,200,250,0.08);border:1px solid rgba(90,200,250,0.2);border-radius:6px;padding:7px 10px;margin:6px 0;font-size:12px;color:#5ac8fa;display:flex;align-items:center;gap:10px;">
      <span>🔍 Agent working</span>
      <span style="color:rgba(255,255,255,0.65);">${elapsed.toFixed(1)}h / ${budget}h</span>
      <div style="flex:1;height:3px;background:rgba(255,255,255,0.08);border-radius:2px;overflow:hidden;"><div style="width:${pct}%;height:100%;background:#5ac8fa;"></div></div>
      ${p.dispatchedTurnId ? `<code style="font-size:10px;color:rgba(255,255,255,0.4);">${escapeHtml(p.dispatchedTurnId)}</code>` : ''}
    </div>`;
  }
  return `<div style="background:rgba(255,255,255,0.03);border:1px solid ${stateColor}33;border-left:3px solid ${stateColor};border-radius:8px;padding:12px 14px;margin-bottom:10px;">
    <div style="display:flex;align-items:center;gap:10px;margin-bottom:6px;">
      <span style="color:${stateColor};font-weight:600;font-size:12px;letter-spacing:0.5px;">${stateLabel}${ageMin !== null && p.state !== 'resolved' ? ' · ' + ageMin + 'm' : ''}</span>
      <span style="color:#fff;font-size:14px;flex:1;">${escapeHtml(p.claim)}${originTag}</span>
      ${needsUser ? '<span class="h23-goodlife-needs-user">needs you</span>' : ''}
      <code style="background:rgba(255,255,255,0.05);padding:2px 6px;border-radius:4px;font-size:11px;color:rgba(255,255,255,0.5);">${escapeHtml(p.id)}</code>
      <button onclick="openProblemEditor('${escapeAttr(p.id)}')" style="background:transparent;border:1px solid rgba(255,255,255,0.15);color:rgba(255,255,255,0.7);padding:3px 10px;border-radius:4px;cursor:pointer;font-size:11px;">edit</button>
    </div>
    ${dispatchBanner}
    ${renderProblemUserAction(p)}
    <div class="h23-problem-operator-grid">
      <div><label>${escapeHtml(currentLabel)}</label><span>${escapeHtml(p.lastResult?.detail || p.detail || 'verifier has not run yet')}</span></div>
      <div><label>${escapeHtml(repairLabel)}</label><span>${escapeHtml(problemRepairText(p))}</span></div>
      <div><label>Needed from you</label><span>${escapeHtml(problemUserText(p))}</span></div>
    </div>
    <div style="font-size:12px;color:rgba(255,255,255,0.6);margin-bottom:6px;">
      <span>verifier: <code style="background:rgba(255,255,255,0.04);padding:1px 4px;border-radius:3px;">${escapeHtml(p.verifier?.type || '—')}</code></span>
      <span style="margin-left:12px;">last: ${last}${lastChecked}</span>
      <span style="margin-left:12px;">remediation: ${stepsLabel}${p.escalated ? ' · <span style="color:#ff6b6b;">escalated</span>' : ''}</span>
    </div>
    ${recentRem.length > 0 ? `<div style="font-size:11px;color:rgba(255,255,255,0.45);margin-top:4px;">recent attempts: ${recentRem.map(r => `<span style="margin-right:10px;">${escapeHtml(r.type)}=${escapeHtml(r.outcome)}</span>`).join('')}</div>` : ''}
  </div>`;
}

function renderProblemUserAction(problem = {}) {
  if (!goodLifeNeedsUser(problem)) return '';
  const next = goodLifeNextRemediation(problem);
  const canRecordHandled = problem.escalated || next.requiresUser;
  const stepText = problem.escalated
    ? 'manual review'
    : next.type
    ? `${next.type}${next.total ? ` step ${next.index + 1} of ${next.total}` : ''}`
    : 'manual intervention';
  const actionText = problem.escalated ? problemUserText(problem) : (next.text || problemUserText(problem));
  return `
    <div class="h23-problem-user-action">
      <div>
        <label>Action Needed</label>
        <strong>${escapeHtml(stepText)}</strong>
        <span>${escapeHtml(actionText)}</span>
      </div>
      <div class="h23-problem-user-action-controls">
        <button type="button" onclick="openProblemEditor('${escapeAttr(problem.id)}')">Inspect Plan</button>
        ${canRecordHandled ? `<button type="button" onclick="recordProblemUserIntervention('${escapeAttr(problem.id)}')">Mark Handled + Re-check</button>` : ''}
        <button type="button" onclick="tickProblemsNow()">Re-check</button>
      </div>
    </div>
  `;
}

function escapeHtml(s) {
  return String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
}
function escapeAttr(s) { return escapeHtml(s).replace(/`/g, '&#96;'); }
function timeSinceSafe(iso) {
  try { const ms = Date.now() - Date.parse(iso); const s = Math.round(ms / 1000); if (s < 60) return `${s}s ago`; const m = Math.round(s / 60); if (m < 60) return `${m}m ago`; return `${Math.round(m / 60)}h ago`; } catch { return '?'; }
}

async function tickProblemsNow() {
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/live-problems/tick`, { method: 'POST' });
    if (r.ok) await renderProblemsList();
  } catch { /* silent */ }
}

async function recordProblemUserIntervention(id) {
  if (!id) return;
  try {
    const detail = window.prompt('What did you handle for this issue?', 'Manual intervention completed.');
    if (detail === null) return;
    const res = await fetch(`${dashboardBaseUrl()}/api/live-problems/${encodeURIComponent(id)}/user-intervention`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        actor: 'good-life-operator',
        note: detail || 'Manual intervention completed.',
      }),
    });
    if (!res.ok) {
      const data = await res.json().catch(() => ({}));
      throw new Error(data.error || `HTTP ${res.status}`);
    }
    await tickProblemsNow();
    await renderProblemsList();
  } catch (err) {
    const list = document.getElementById('problems-list');
    if (list) {
      list.insertAdjacentHTML('afterbegin', `<div style="color:#ff6b6b;padding:8px 12px;">Failed to record intervention: ${escapeHtml(err.message)}</div>`);
    }
  }
}

function openProblemEditor(id) {
  _problemEditingId = id;
  const title = document.getElementById('problem-editor-title');
  const pid = document.getElementById('pe-id');
  const claim = document.getElementById('pe-claim');
  const verifier = document.getElementById('pe-verifier');
  const rem = document.getElementById('pe-remediation');
  const del = document.getElementById('pe-delete');
  const status = document.getElementById('pe-status');
  if (status) status.textContent = '';
  if (id) {
    const p = (_liveProblems.problems || []).find(x => x.id === id);
    if (!p) return;
    title.textContent = `Edit: ${p.id}`;
    pid.value = p.id; pid.disabled = true;
    claim.value = p.claim || '';
    verifier.value = JSON.stringify(p.verifier || {}, null, 2);
    rem.value = JSON.stringify(p.remediation || [], null, 2);
    del.style.display = '';
  } else {
    title.textContent = 'Add Problem';
    pid.value = ''; pid.disabled = false;
    claim.value = '';
    verifier.value = '{\n  "type": "file_mtime",\n  "args": { "path": "~/.health_log.jsonl", "maxAgeMin": 360 }\n}';
    rem.value = '[\n  { "type": "notify_jtr", "args": { "text": "Something\'s wrong — check." }, "cooldownMin": 360 }\n]';
    del.style.display = 'none';
  }
  document.getElementById('problem-editor-overlay').style.display = 'flex';
}

function closeProblemEditor() {
  const ov = document.getElementById('problem-editor-overlay');
  if (ov) ov.style.display = 'none';
  _problemEditingId = null;
}

async function saveProblemEdit() {
  const pid = document.getElementById('pe-id').value.trim();
  const claim = document.getElementById('pe-claim').value.trim();
  const verifierText = document.getElementById('pe-verifier').value.trim();
  const remText = document.getElementById('pe-remediation').value.trim();
  const status = document.getElementById('pe-status');
  if (!pid || !claim) { status.textContent = 'id + claim required'; status.style.color = '#ff6b6b'; return; }
  let verifier, remediation;
  try { verifier = verifierText ? JSON.parse(verifierText) : null; } catch (e) { status.textContent = 'verifier JSON invalid: ' + e.message; status.style.color = '#ff6b6b'; return; }
  try { remediation = remText ? JSON.parse(remText) : []; } catch (e) { status.textContent = 'remediation JSON invalid: ' + e.message; status.style.color = '#ff6b6b'; return; }
  try {
    const method = _problemEditingId ? 'PUT' : 'POST';
    const url = _problemEditingId
      ? `${dashboardBaseUrl()}/api/live-problems/${encodeURIComponent(_problemEditingId)}`
      : `${dashboardBaseUrl()}/api/live-problems`;
    const res = await fetch(url, {
      method,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id: pid, claim, verifier, remediation, seedOrigin: 'user' }),
    });
    const data = await res.json();
    if (!res.ok) { status.textContent = data.error || `HTTP ${res.status}`; status.style.color = '#ff6b6b'; return; }
    status.textContent = 'saved'; status.style.color = '#30d158';
    await renderProblemsList();
    setTimeout(closeProblemEditor, 600);
  } catch (err) {
    status.textContent = 'save failed: ' + err.message; status.style.color = '#ff6b6b';
  }
}

async function deleteProblemFromEditor() {
  if (!_problemEditingId) return;
  if (!confirm(`Delete problem "${_problemEditingId}"? If it's a seeded invariant it will come back on next engine start.`)) return;
  try {
    await fetch(`${dashboardBaseUrl()}/api/live-problems/${encodeURIComponent(_problemEditingId)}`, { method: 'DELETE' });
    await renderProblemsList();
    closeProblemEditor();
  } catch { /* silent */ }
}

// ── Brain Storage (disk vs memory truth) ──
let _brainStorage = null;

async function updateBrainStorageBadge() {
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/brain/storage`);
    if (!r.ok) return;
    const data = await r.json();
    _brainStorage = data;
    const el = document.getElementById('pulse-brain');
    const sep = document.getElementById('pulse-brain-sep');
    const badge = document.getElementById('pulse-brain-badge');
    if (!el || !badge) return;

    const snapNodes = data.snapshot?.nodeCount;
    if (snapNodes == null) {
      el.style.display = 'none';
      if (sep) sep.style.display = 'none';
      return;
    }
    el.style.display = '';
    if (sep) sep.style.display = '';

    const ageMs = data.snapshot?.savedAt ? (Date.now() - new Date(data.snapshot.savedAt).getTime()) : null;
    const ageStr = ageMs == null ? '?' : (ageMs < 60000 ? `${Math.round(ageMs/1000)}s` : `${Math.round(ageMs/60000)}m`);

    if (data.mismatch) {
      badge.textContent = `🧠 ${snapNodes} ⚠️ mismatch`;
      badge.style.color = '#ff6b6b';
    } else {
      badge.textContent = `🧠 ${snapNodes.toLocaleString()} · saved ${ageStr} ago`;
      badge.style.color = 'rgba(255,255,255,0.55)';
    }
  } catch { /* silent */ }
}

async function openBrainStoragePanel() {
  const overlay = document.getElementById('brain-storage-overlay');
  if (!overlay) return;
  overlay.style.display = 'flex';
  await renderBrainStoragePanel();
}

function closeBrainStoragePanel() {
  const overlay = document.getElementById('brain-storage-overlay');
  if (overlay) overlay.style.display = 'none';
}

async function renderBrainStoragePanel() {
  const content = document.getElementById('brain-storage-content');
  if (!content) return;
  content.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">Loading...</div>';
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/brain/storage`);
    const data = await r.json();
    _brainStorage = data;

    const snap = data.snapshot;
    const mem = data.inMemory;
    const hw = data.highWater;
    const files = data.files || {};
    const backups = data.backups || [];

    const mb = (b) => { if (b == null) return '—'; if (b < 1024) return `${b} B`; if (b < 1048576) return `${(b / 1024).toFixed(1)} KB`; return `${(b / 1048576).toFixed(1)} MB`; };
    const ago = (iso) => { if (!iso) return '—'; const ms = Date.now() - new Date(iso).getTime(); if (ms < 60000) return `${Math.round(ms/1000)}s ago`; if (ms < 3600000) return `${Math.round(ms/60000)}m ago`; return `${Math.round(ms/3600000)}h ago`; };

    const mismatchWarn = data.mismatch
      ? `<div style="background:rgba(255,107,107,0.15);border:1px solid rgba(255,107,107,0.4);border-radius:6px;padding:10px 14px;margin-bottom:14px;color:#ff6b6b;">⚠️ MISMATCH: disk says ${snap?.nodeCount} nodes, memory says ${mem?.nodes}. Something is wrong — do NOT restart the engine until investigated.</div>`
      : '';

    content.innerHTML = `
      ${mismatchWarn}
      <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:14px;">
        <div style="background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);border-radius:8px;padding:12px 14px;">
          <div style="font-size:11px;color:rgba(255,255,255,0.5);text-transform:uppercase;letter-spacing:1px;margin-bottom:6px;">On disk (snapshot)</div>
          <div style="font-size:22px;color:#fff;">${(snap?.nodeCount ?? '—').toLocaleString()} nodes</div>
          <div style="font-size:13px;color:rgba(255,255,255,0.6);margin-top:2px;">${(snap?.edgeCount ?? '—').toLocaleString()} edges · cycle ${snap?.cycle ?? '—'}</div>
          <div style="font-size:11px;color:rgba(255,255,255,0.4);margin-top:6px;">saved ${ago(snap?.savedAt)} · source: ${snap?.memorySource || '—'}</div>
        </div>
        <div style="background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);border-radius:8px;padding:12px 14px;">
          <div style="font-size:11px;color:rgba(255,255,255,0.5);text-transform:uppercase;letter-spacing:1px;margin-bottom:6px;">Last verified (engine)</div>
          <div style="font-size:22px;color:#fff;">${snap?.nodeCount != null ? snap.nodeCount.toLocaleString() : '—'} nodes</div>
          <div style="font-size:13px;color:rgba(255,255,255,0.6);margin-top:2px;">${snap?.edgeCount != null ? snap.edgeCount.toLocaleString() + ' edges' : ''}</div>
        </div>
      </div>
      <div style="background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);border-radius:8px;padding:12px 14px;margin-bottom:14px;">
        <div style="font-size:11px;color:rgba(255,255,255,0.5);text-transform:uppercase;letter-spacing:1px;margin-bottom:8px;">Files</div>
        <div style="font-family:var(--font-mono,monospace);font-size:12px;color:rgba(255,255,255,0.75);line-height:1.6;">
          <div>state.json.gz            · ${mb(files.state?.bytes)}</div>
          <div>memory-nodes.jsonl.gz    · ${mb(files.nodesSidecar?.bytes)}</div>
          <div>memory-edges.jsonl.gz    · ${mb(files.edgesSidecar?.bytes)}</div>
          <div>brain-snapshot.json      · ${mb(files.snapshot?.bytes)}</div>
        </div>
      </div>
      ${hw ? `<div style="background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);border-radius:8px;padding:12px 14px;margin-bottom:14px;">
        <div style="font-size:11px;color:rgba(255,255,255,0.5);text-transform:uppercase;letter-spacing:1px;margin-bottom:6px;">High-water mark</div>
        <div style="font-size:14px;color:#fff;">${hw.maxNodeCount.toLocaleString()} nodes</div>
        <div style="font-size:11px;color:rgba(255,255,255,0.4);margin-top:4px;">last hit ${ago(hw.lastSeen)}. Drop-detector opens a live problem if current falls &gt;10% below.</div>
      </div>` : ''}
      <div style="background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);border-radius:8px;padding:12px 14px;">
        <div style="font-size:11px;color:rgba(255,255,255,0.5);text-transform:uppercase;letter-spacing:1px;margin-bottom:8px;">Rolling backups (${backups.length})</div>
        ${backups.length === 0 ? '<div style="font-size:12px;color:rgba(255,255,255,0.45);">No backups yet — first one gets created after ~1 hour of successful saves.</div>' : `<div style="font-family:var(--font-mono,monospace);font-size:12px;color:rgba(255,255,255,0.7);line-height:1.5;">${backups.map(b => `<div>${b.name}</div>`).join('')}</div>`}
      </div>
    `;
  } catch (err) {
    content.innerHTML = `<div style="color:#ff6b6b;padding:20px;">Failed to load: ${err.message}</div>`;
  }
}

// ── Notifications (thought-action queue) ──

async function updateNotificationBadge() {
  // NOTIFY stream now drains off-engine via the promoter worker into the
  // live-problems registry — not shown as a primary pulse-bar surface anymore.
  // Panel still reachable via openNotificationsPanel() for debugging.
  const el = document.getElementById('pulse-notifs');
  if (el) el.style.display = 'none';
}

// ── Signals (wins, resolutions, positive observations) ──

async function updateSignalsBadge() {
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/signals?limit=50&sinceHours=48`);
    if (!r.ok) return;
    const data = await r.json();
    const signals = data.signals || [];
    const el = document.getElementById('pulse-signals');
    const sep = document.getElementById('pulse-signals-sep');
    const badge = document.getElementById('pulse-signals-badge');
    if (!el || !badge) return;
    if (signals.length === 0) {
      el.style.display = 'none';
      if (sep) sep.style.display = 'none';
      return;
    }
    el.style.display = '';
    if (sep) sep.style.display = '';
    badge.textContent = `✨ ${signals.length}`;
    badge.style.color = '#30d158';
  } catch { /* silent */ }
}

async function openSignalsPanel() {
  const overlay = document.getElementById('signals-overlay');
  const list = document.getElementById('signals-list');
  if (!overlay || !list) return;
  overlay.style.display = 'flex';
  list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">Loading...</div>';
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/signals?limit=200&sinceHours=48`);
    const data = await r.json();
    const signals = data.signals || [];
    if (signals.length === 0) {
      list.innerHTML = '<div style="color:rgba(255,255,255,0.5);padding:20px;">No signals yet. Wins and positive observations show up here as the system resolves problems and observes healthy patterns.</div>';
      return;
    }
    list.innerHTML = signals.map(s => renderSignalCard(s)).join('');
  } catch (err) {
    list.innerHTML = `<div style="color:#ff6b6b;padding:20px;">Failed to load: ${err.message}</div>`;
  }
}

function closeSignalsPanel() {
  const overlay = document.getElementById('signals-overlay');
  if (overlay) overlay.style.display = 'none';
}

function renderSignalCard(s) {
  const typeMeta = {
    resolved:             { icon: '✓', color: '#30d158', label: 'RESOLVED' },
    autonomous_fix:       { icon: '🔧', color: '#5ac8fa', label: 'AUTO-FIX' },
    observation:          { icon: '💡', color: '#ffb347', label: 'OBSERVATION' },
    action_success:       { icon: '⚡', color: '#bf5af2', label: 'ACTION' },
    registry_suggestion:  { icon: '📋', color: '#ff9f0a', label: 'REGISTRY SUGGESTION' },
  }[s.type] || { icon: '•', color: '#888', label: (s.type || 'SIGNAL').toUpperCase() };
  const ts = s.ts ? new Date(s.ts).toLocaleString() : '—';
  const evidenceBits = [];
  if (s.evidence?.problemId) evidenceBits.push(`problem: <code style="font-size:11px;background:rgba(255,255,255,0.05);padding:1px 4px;border-radius:3px;">${escapeHtml(s.evidence.problemId)}</code>`);
  if (s.evidence?.fixRecipe) evidenceBits.push(`fix: ${escapeHtml(s.evidence.fixRecipe)}`);
  if (s.evidence?.verifierDetail) evidenceBits.push(`check: ${escapeHtml(s.evidence.verifierDetail)}`);
  if (s.evidence?.category && s.evidence?.target) evidenceBits.push(`<code style="font-size:11px;background:rgba(255,255,255,0.05);padding:1px 4px;border-radius:3px;">${escapeHtml(s.evidence.category)}:${escapeHtml(s.evidence.target)}</code>`);
  if (typeof s.evidence?.rejectionCount === 'number') evidenceBits.push(`${s.evidence.rejectionCount} rejections`);
  return `<div style="background:rgba(255,255,255,0.03);border:1px solid ${typeMeta.color}33;border-left:3px solid ${typeMeta.color};border-radius:8px;padding:10px 14px;margin-bottom:8px;">
    <div style="display:flex;align-items:baseline;gap:10px;margin-bottom:4px;">
      <span style="color:${typeMeta.color};font-size:16px;">${typeMeta.icon}</span>
      <span style="color:${typeMeta.color};font-weight:600;font-size:11px;letter-spacing:0.5px;">${typeMeta.label}</span>
      <span style="color:rgba(255,255,255,0.45);font-size:12px;">${escapeHtml(s.source || '—')}</span>
      ${typeof s.cycle === 'number' ? `<span style="color:rgba(255,255,255,0.35);font-size:11px;">cycle ${s.cycle}</span>` : ''}
      <span style="color:rgba(255,255,255,0.35);font-size:11px;margin-left:auto;">${ts}</span>
    </div>
    ${s.title ? `<div style="color:#fff;font-size:13px;margin-bottom:3px;">${escapeHtml(s.title)}</div>` : ''}
    ${s.message && s.message !== s.title ? `<div style="color:rgba(255,255,255,0.7);font-size:12px;line-height:1.4;">${escapeHtml(s.message)}</div>` : ''}
    ${evidenceBits.length ? `<div style="color:rgba(255,255,255,0.5);font-size:11px;margin-top:5px;">${evidenceBits.join(' · ')}</div>` : ''}
  </div>`;
}

async function openNotificationsPanel() {
  const overlay = document.getElementById('notifications-overlay');
  const list = document.getElementById('notifications-list');
  if (!overlay || !list) return;
  overlay.style.display = 'flex';
  list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">Loading...</div>';
  try {
    const r = await fetch(`${dashboardBaseUrl()}/api/notifications`);
    const data = await r.json();
    if (!data.items || data.items.length === 0) {
      list.innerHTML = '<div style="color:rgba(255,255,255,0.6);padding:20px;">No notifications yet. Cognitive cycles will queue action proposals here.</div>';
      return;
    }
    list.innerHTML = data.items.map(n => {
      const ts = new Date(n.ts).toLocaleString();
      const roleIcon = { curiosity: '❓', analyst: '🔬', critic: '⚠️', curator: '📋', proposal: '⚡' }[n.source] || '🧠';
      const opacity = n.acknowledged ? '0.4' : '1';
      const bgColor = n.acknowledged ? 'transparent' : 'rgba(0,122,255,0.05)';
      return `
        <div style="padding:10px 12px;margin-bottom:8px;background:${bgColor};border-left:3px solid ${n.acknowledged ? 'rgba(255,255,255,0.1)' : '#5ac8fa'};opacity:${opacity};">
          <div style="display:flex;justify-content:space-between;align-items:flex-start;gap:12px;">
            <div style="flex:1;">
              <div style="font-size:12px;color:rgba(255,255,255,0.5);margin-bottom:4px;">
                ${roleIcon} ${n.source} · cycle ${n.cycle} · ${ts}
              </div>
              <div style="color:#fff;">${escapeHtmlNotif(n.message)}</div>
            </div>
            ${n.acknowledged ? '<span style="color:rgba(255,255,255,0.4);font-size:11px;">✓ ack</span>' : `<button onclick="ackNotification('${n.id}')" style="background:rgba(255,255,255,0.1);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:12px;">Ack</button>`}
          </div>
        </div>`;
    }).join('');
  } catch (err) {
    list.innerHTML = `<div style="color:#ff6b6b;padding:20px;">Failed to load: ${err.message}</div>`;
  }
}

function escapeHtmlNotif(s) {
  return String(s || '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
}

function closeNotificationsPanel() {
  const overlay = document.getElementById('notifications-overlay');
  if (overlay) overlay.style.display = 'none';
}

async function ackNotification(id) {
  try {
    await fetch(`${dashboardBaseUrl()}/api/notifications/${id}/ack`, { method: 'POST' });
    openNotificationsPanel();
    updateNotificationBadge();
  } catch {}
}

async function acknowledgeAllNotifications() {
  try {
    await fetch(`${dashboardBaseUrl()}/api/notifications/ack-all`, { method: 'POST' });
    openNotificationsPanel();
    updateNotificationBadge();
  } catch {}
}

function dashboardBaseUrl() {
  return `http://${window.location.hostname}:${window.location.port || 5002}`;
}

// ── Engine Pulse (Live Activity Indicator) ──

function connectEnginePulse() {
  // Connect directly to engine's WebSocket (port 5001) for real-time events
  const enginePort = primaryAgent ? primaryAgent.enginePort || 5001 : 5001;
  const wsUrl = `ws://${window.location.hostname}:${enginePort}`;
  let ws;
  let reconnectTimer = null;

  function connect() {
    ws = new WebSocket(wsUrl);

    ws.onopen = () => {
      const dot = document.getElementById('pulse-dot');
      if (dot && !dot.className.includes('awake') && !dot.className.includes('sleeping')) {
        dot.className = 'h23-pulse-dot awake';
      }
      if (!enginePulse.state || enginePulse.state === 'unknown') {
        enginePulse.state = 'awake';
        renderPulse();
      }
      setEngineOnlineStatus(enginePulse.state);
    };

    ws.onmessage = (e) => {
      try {
        const data = JSON.parse(e.data);
        if (data.type === 'connected') return; // welcome message
        enginePulse.lastEventTime = new Date();
        handleEngineEvent(data);
        renderPulse();
      } catch { /* ignore parse errors */ }
    };

    ws.onclose = () => {
      const dot = document.getElementById('pulse-dot');
      if (dot) dot.className = 'h23-pulse-dot';
      setEngineOfflineStatus();
      // Reconnect after 5 seconds
      if (!reconnectTimer) {
        reconnectTimer = setTimeout(() => {
          reconnectTimer = null;
          connect();
        }, 5000);
      }
    };

    ws.onerror = () => {
      // onclose will handle reconnect
    };
  }

  connect();
}

function handleEngineEvent(data) {
  switch (data.type) {
    case 'cycle_start':
      enginePulse.cycle = data.cycle || enginePulse.cycle;
      enginePulse.state = data.mode === 'sleeping' ? 'sleeping' : 'awake';
      enginePulse.phase = 'starting cycle';
      if (data.cognitiveState) {
        enginePulse.energy = data.cognitiveState.energy || enginePulse.energy;
      }
      break;

    case 'thought_generated':
      enginePulse.state = data.role === 'sleep' ? 'sleeping' : 'thinking';
      enginePulse.phase = data.role === 'sleep'
        ? data.thought?.substring(0, 60) || 'resting'
        : `thinking (${data.role || 'focus'})`;
      enginePulse.cycle = data.cycle || enginePulse.cycle;
      enginePulse.lastThought = new Date();
      break;

    case 'sleep_triggered':
      enginePulse.state = 'sleeping';
      enginePulse.phase = 'entering sleep';
      enginePulse.energy = data.energy || enginePulse.energy;
      break;

    case 'wake_triggered':
      enginePulse.state = 'awake';
      enginePulse.phase = 'waking up';
      enginePulse.energy = data.energyRestored || enginePulse.energy;
      break;

    case 'coordinator_review':
      enginePulse.phase = 'strategic review';
      break;

    case 'executive_decision':
      enginePulse.phase = `executive: ${(data.action || '').toLowerCase()}`;
      break;

    case 'agent_spawned':
      enginePulse.phase = `spawning ${data.agentType || 'agent'}`;
      break;

    case 'agent_completed':
      enginePulse.phase = `${data.agentType || 'agent'} completed`;
      break;

    case 'dream_rewiring':
      enginePulse.state = 'sleeping';
      enginePulse.phase = 'dreaming (rewiring)';
      break;

    case 'cognitive_state_changed':
    case 'cognitive_state_update':
      if (data.energy !== undefined) enginePulse.energy = data.energy;
      if (data.mode) {
        enginePulse.state = data.mode === 'sleeping' ? 'sleeping' : 'awake';
      }
      if (data.newValue && data.metric === 'mode') {
        enginePulse.state = data.newValue === 'sleeping' ? 'sleeping' : 'awake';
      }
      break;

    case 'cycle_complete':
      enginePulse.phase = 'cycle complete';
      break;

    case 'node_created':
      enginePulse.phase = 'creating memory';
      break;
  }
}

// Cached pulse DOM elements (populated on first render)
let _pulseEls = null;
let _pulseRafPending = false;

function renderPulse() {
  // Throttle to one render per animation frame
  if (_pulseRafPending) return;
  _pulseRafPending = true;
  requestAnimationFrame(_renderPulseNow);
}

function _renderPulseNow() {
  _pulseRafPending = false;
  if (!_pulseEls) {
    _pulseEls = {
      dot: document.getElementById('pulse-dot'),
      state: document.getElementById('pulse-state'),
      phase: document.getElementById('pulse-phase'),
      energy: document.getElementById('pulse-energy'),
      cycle: document.getElementById('pulse-cycle'),
    };
  }
  if (!_pulseEls.dot) return;

  _pulseEls.dot.className = 'h23-pulse-dot ' + (enginePulse.state || '');
  _pulseEls.state.textContent = enginePulse.state || '—';
  _pulseEls.phase.textContent = enginePulse.phase || '—';
  _pulseEls.energy.textContent = `⚡ ${Math.round((enginePulse.energy || 0) * 100)}%`;
  _pulseEls.cycle.textContent = `cycle ${enginePulse.cycle || '—'}`;
}

function updatePulseAgo() {
  const ref = enginePulse.lastThought || enginePulse.lastEventTime;
  setText('pulse-ago', ref ? timeSince(ref) : '—');
}

function setEngineOnlineStatus(temporalState = 'awake') {
  const dot = document.getElementById('engine-dot');
  if (dot) dot.className = 'status-dot alive';
  setText('engine-status-text', temporalState === 'sleeping' ? 'ENGINE · SLEEPING' : 'ENGINE');
  setText('sidebar-status-line', temporalState === 'sleeping' ? 'Engine sleeping.' : 'Engine online.');
}

function setEngineOfflineStatus() {
  const dot = document.getElementById('engine-dot');
  if (dot) dot.className = 'status-dot dead';
  setText('engine-status-text', 'ENGINE offline');
  setText('sidebar-status-line', 'Engine offline.');
}

async function fetchEngineHealth(agent) {
  const enginePort = agent ? agent.enginePort || 5001 : 5001;
  return apiFetch(`http://${window.location.hostname}:${enginePort}/health`, { timeoutMs: 10000 });
}

function seedPulseFromSummary(summary, engineHealth = null) {
  if (!summary && !engineHealth) return;

  if (summary?.cycleCount && (!enginePulse.cycle || enginePulse.cycle < summary.cycleCount)) {
    enginePulse.cycle = summary.cycleCount;
  }

  if (summary?.lastThoughtAt && !enginePulse.lastThought) {
    enginePulse.lastThought = new Date(summary.lastThoughtAt);
  }

  if ((!enginePulse.state || enginePulse.state === 'unknown') && summary?.temporalState) {
    enginePulse.state = summary.temporalState;
  } else if ((!enginePulse.state || enginePulse.state === 'unknown') && engineHealth) {
    enginePulse.state = 'awake';
  }

  if (!enginePulse.phase && summary?.lastThoughtRole) {
    enginePulse.phase = summary.lastThoughtRole === 'sleep'
      ? 'resting'
      : `thinking (${summary.lastThoughtRole})`;
  }

  renderPulse();
}

// ── Clock ──

function updateClocks() {
  const agentTz = window.__agentTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
  const now = new Date();
  const fmt = (tz) => now.toLocaleTimeString('en-US', { timeZone: tz, hour: '2-digit', minute: '2-digit', hour12: true });
  const fmt24 = (tz) => now.toLocaleTimeString('en-GB', { timeZone: tz, hour: '2-digit', minute: '2-digit' });

  const tz1Time = document.getElementById('tz1-time');
  if (tz1Time) tz1Time.textContent = fmt(agentTz);
  const tz1Label = document.getElementById('tz1-label');
  if (tz1Label) tz1Label.textContent = agentTz.split('/').pop().replace(/_/g, ' ');

  const secondaryTz = window.__secondaryTimezone;
  const tz2Container = document.getElementById('tz2-container');
  if (secondaryTz && tz2Container) {
    tz2Container.style.display = 'flex';
    const tz2Time = document.getElementById('tz2-time');
    if (tz2Time) tz2Time.textContent = fmt24(secondaryTz);
    const tz2Label = document.getElementById('tz2-label');
    if (tz2Label) tz2Label.textContent = secondaryTz.split('/').pop().replace(/_/g, ' ');
  }
}

// ── Particles ──

function initParticles() {
  if (typeof particlesJS === 'undefined') return;
  particlesJS('particles-js', {
    particles: {
      number: { value: 40, density: { enable: true, value_area: 1000 } },
      color: { value: ['#ffffff', '#007AFF', '#00C7BE', '#30D158'] },
      shape: { type: 'circle' },
      opacity: { value: 0.3, random: true, anim: { enable: true, speed: 1, opacity_min: 0.1, sync: false } },
      size: { value: 3, random: true, anim: { enable: true, speed: 2, size_min: 1, sync: false } },
      line_linked: { enable: true, distance: 200, color: '#ffffff', opacity: 0.15, width: 1 },
      move: { enable: true, speed: 0.8, direction: 'none', random: true, straight: false, out_mode: 'out', bounce: false }
    },
    interactivity: {
      detect_on: 'canvas',
      events: { onhover: { enable: true, mode: 'bubble' }, onclick: { enable: false }, resize: true },
      modes: { bubble: { distance: 200, size: 6, duration: 2, opacity: 0.6, speed: 3 } }
    },
    retina_detect: true
  });
}

// ── Load Agents ──

async function loadAgents() {
  try {
    const res = await fetch('/home23/agents.json');
    if (res.ok) agents = await res.json();
  } catch { /* ignore */ }

  // Fallback: current dashboard is the only agent
  if (agents.length === 0) {
    agents = [{
      name: 'agent',
      displayName: 'Agent',
      dashboardPort: window.location.port || 5002,
      enginePort: 5001
    }];
  }

  // Current dashboard agent = the one whose dashboard we're on
  const currentPort = parseInt(window.location.port) || 5002;
  primaryAgent = agents.find(a => a.dashboardPort === currentPort) || agents[0];
  homePrimaryAgent = agents.find(a => a.isPrimary) || primaryAgent;
  refreshDashboardIdentityUI();

  // Set agent name in thoughts tile
  const primaryAgentName = document.getElementById('primary-agent-name');
  if (primaryAgentName) {
    primaryAgentName.textContent = primaryAgent.displayName || primaryAgent.name;
  }

  // Load config and construct host-relative URLs
  const host = window.location.hostname;
  try {
    const cfgRes = await fetch('/home23/config.json');
    if (cfgRes.ok) {
      const cfg = await cfgRes.json();
      cosmo23Url = `http://${host}:${cfg.cosmo23Port}`;
      evobrewUrl = `http://${host}:${cfg.evobrewPort}`;

      // Wire evobrew button
      const evobrewBtn = document.getElementById('evobrew-btn');
      if (evobrewBtn && evobrewUrl) {
        evobrewBtn.href = `${evobrewUrl}/?agent=${primaryAgent.name}`;
        evobrewBtn.target = '_blank';
      }
    }
  } catch { /* config offline */ }

  // Wire settings button
  const settingsBtn = document.getElementById('settings-btn');
  if (settingsBtn) {
    settingsBtn.addEventListener('click', () => {
      window.location.href = '/home23/settings';
    });
  }

  const sidebarUpdateButton = document.getElementById('sidebar-update-action');
  if (sidebarUpdateButton && sidebarUpdateButton.dataset.bound !== 'true') {
    sidebarUpdateButton.addEventListener('click', () => {
      checkUpdateNotification({ showEvenIfCurrent: true }).catch(() => {});
    });
    sidebarUpdateButton.dataset.bound = 'true';
  }

  // Wire COSMO tab button
  const cosmoBtn = document.getElementById('cosmo23-btn');
  if (cosmoBtn) {
    cosmoBtn.addEventListener('click', (e) => {
      e.preventDefault();
      // Deactivate all data-tab buttons
      document.querySelectorAll('.h23-tab[data-tab]').forEach(t => t.classList.remove('active'));
      cosmoBtn.classList.add('active');
      currentTab = 'cosmo23';
      refreshDashboardScopeUI();
      setCosmoHomeDrawerOpen(false);
      showCosmoFrame();
    });
  }

  // Wire COSMO indicator click -> switch to COSMO tab
  const indicator = document.getElementById('cosmo23-indicator');
  if (indicator) {
    indicator.addEventListener('click', () => {
      if (cosmoBtn) cosmoBtn.click();
    });
  }

  // Wire COSMO iframe refresh button
  const refreshBtn = document.getElementById('cosmo23-refresh-btn');
  if (refreshBtn) {
    refreshBtn.addEventListener('click', () => refreshCosmoFrame());
  }

  const homeToggleBtn = document.getElementById('cosmo23-home-toggle-btn');
  if (homeToggleBtn) {
    homeToggleBtn.addEventListener('click', () => {
      setCosmoHomeDrawerOpen(!document.body.classList.contains('h23-external-drawer-open'));
    });
  }

  // Wire Intelligence tab synthesis button
  setupIntelSynthButton();
}

// ── COSMO iframe ──

function showCosmoFrame() {
  // Hide all panels
  document.querySelectorAll('.h23-panel').forEach(p => p.classList.remove('active'));
  const frame = document.getElementById('cosmo23-frame');
  const wrap = document.getElementById('cosmo23-frame-wrap');
  if (wrap) wrap.style.display = 'block';

  if (cosmoOnline) {
    // Online — show iframe, hide offline overlay
    hideCosmoOfflineOverlay();
    if (!cosmo23Loaded && cosmo23Url) {
      frame.src = cosmo23Url;
      cosmo23Loaded = true;
    }
  } else {
    // Offline — show actionable overlay instead of blank iframe
    showCosmoOfflineOverlay();
  }
}

function hideCosmoFrame() {
  const wrap = document.getElementById('cosmo23-frame-wrap');
  if (wrap) wrap.style.display = 'none';
  setCosmoHomeDrawerOpen(false);
}

function setCosmoHomeDrawerOpen(open) {
  const enabled = currentTab === 'cosmo23' && !!open;
  document.body.classList.toggle('h23-external-drawer-open', enabled);
  const button = document.getElementById('cosmo23-home-toggle-btn');
  if (button) {
    button.setAttribute('aria-expanded', String(enabled));
    button.textContent = enabled ? 'Close Home23' : 'Home23';
  }
}

function refreshCosmoFrame() {
  const frame = document.getElementById('cosmo23-frame');
  if (frame && cosmo23Url) {
    frame.src = cosmo23Url;
    cosmo23Loaded = true;
  }
}

function showCosmoOfflineOverlay() {
  let overlay = document.getElementById('cosmo23-offline-overlay');
  if (!overlay) {
    overlay = document.createElement('div');
    overlay.id = 'cosmo23-offline-overlay';
    overlay.style.cssText = 'position:absolute; inset:0; z-index:5; display:flex; flex-direction:column; align-items:center; justify-content:center; background:rgba(10,10,18,0.95); gap:16px;';
    overlay.innerHTML = `
      <div style="font-size:36px; opacity:0.4;">&#x1F52C;</div>
      <div style="font-size:16px; color:#ccc; font-weight:500;">COSMO 2.3 is offline</div>
      <div id="cosmo23-offline-detail" style="font-size:13px; color:#888; max-width:400px; text-align:center;">The research engine process is not running.</div>
      <button id="cosmo23-restart-btn" style="margin-top:8px; padding:8px 24px; background:rgba(99,102,241,0.25); border:1px solid rgba(99,102,241,0.5); color:#a5b4fc; border-radius:8px; font-size:14px; cursor:pointer; transition:all 0.15s;">Start COSMO 2.3</button>
      <div id="cosmo23-restart-status" style="font-size:12px; color:#888; min-height:18px;"></div>
    `;
    const wrap = document.getElementById('cosmo23-frame-wrap');
    if (wrap) wrap.appendChild(overlay);

    // Wire restart button
    overlay.querySelector('#cosmo23-restart-btn').addEventListener('click', restartCosmo23);
  }
  overlay.style.display = 'flex';
  // Hide iframe behind overlay
  const frame = document.getElementById('cosmo23-frame');
  if (frame) frame.style.visibility = 'hidden';
}

function hideCosmoOfflineOverlay() {
  const overlay = document.getElementById('cosmo23-offline-overlay');
  if (overlay) overlay.style.display = 'none';
  const frame = document.getElementById('cosmo23-frame');
  if (frame) frame.style.visibility = 'visible';
}

async function restartCosmo23() {
  const btn = document.getElementById('cosmo23-restart-btn');
  const status = document.getElementById('cosmo23-restart-status');
  if (btn) { btn.disabled = true; btn.textContent = 'Starting...'; }
  if (status) status.textContent = '';
  try {
    const res = await fetch('/home23/api/settings/cosmo23/restart', { method: 'POST' });
    const data = await res.json();
    if (data.ok) {
      if (status) status.textContent = 'Started. Connecting...';
      // Give it a moment to bind the port, then recheck
      setTimeout(async () => {
        await updateCosmoIndicator();
        if (cosmoOnline) {
          hideCosmoOfflineOverlay();
          cosmo23Loaded = false;
          const frame = document.getElementById('cosmo23-frame');
          if (frame && cosmo23Url) { frame.src = cosmo23Url; cosmo23Loaded = true; }
        } else {
          if (status) status.textContent = 'Process started but not yet responding. Try refreshing in a few seconds.';
          if (btn) { btn.disabled = false; btn.textContent = 'Retry'; }
        }
      }, 3000);
    } else {
      if (status) status.textContent = `Error: ${data.error || 'unknown'}`;
      if (btn) { btn.disabled = false; btn.textContent = 'Retry'; }
    }
  } catch (err) {
    if (status) status.textContent = `Failed: ${err.message}`;
    if (btn) { btn.disabled = false; btn.textContent = 'Retry'; }
  }
}

// ── COSMO status indicator ──

async function updateCosmoIndicator() {
  if (!cosmo23Url) return;
  const dot = document.getElementById('cosmo23-ind-dot');
  const text = document.getElementById('cosmo23-ind-text');
  if (!dot || !text) return;
  try {
    const res = await fetch(`${cosmo23Url}/api/status`, { signal: AbortSignal.timeout(10000) });
    const status = await res.json();
    cosmoOnline = true;
    if (status.running && status.activeContext) {
      dot.className = 'h23-cosmo-indicator-dot running';
      text.textContent = `COSMO: running — ${status.activeContext.runName || 'research'}`;
    } else {
      dot.className = 'h23-cosmo-indicator-dot';
      text.textContent = 'COSMO: idle';
    }
    // If we just came back online and the tab is showing, refresh
    if (currentTab === 'cosmo23') hideCosmoOfflineOverlay();
  } catch {
    cosmoOnline = false;
    dot.className = 'h23-cosmo-indicator-dot error';
    text.textContent = 'COSMO: offline';
    // If viewing the COSMO tab right now, show the overlay
    if (currentTab === 'cosmo23') showCosmoOfflineOverlay();
  }
}

// ── Tabs ──

function renderAgentTabs() {
  const container = document.getElementById('agent-tabs');
  // Only show tabs for other agents; Home belongs to the current dashboard agent.
  const others = agents.filter(a => a.name !== primaryAgent.name);
  container.innerHTML = others.map(a =>
    `<button class="h23-tab" data-tab="agent-${a.name}" data-tab-label="${a.displayName || a.name}">${a.displayName || a.name}</button>`
  ).join('');
}

function setupTabHandlers() {
  document.querySelectorAll('.h23-tab[data-tab]').forEach(tab => {
    tab.addEventListener('click', () => {
      // Deactivate all tabs (including cosmo button)
      document.querySelectorAll('.h23-tab[data-tab]').forEach(t => t.classList.remove('active'));
      const cosmoBtn = document.getElementById('cosmo23-btn');
      if (cosmoBtn) cosmoBtn.classList.remove('active');

      // Hide cosmo frame
      hideCosmoFrame();

      // Hide all panels
      document.querySelectorAll('.h23-panel').forEach(p => p.classList.remove('active'));

      tab.classList.add('active');
      currentTab = tab.dataset.tab;
      refreshDashboardScopeUI();

      let panel = document.getElementById(`panel-${currentTab}`);
      if (!panel && currentTab.startsWith('agent-')) {
        const name = currentTab.replace('agent-', '');
        panel = createAgentPanel(name);
        document.querySelector('.h23-main').appendChild(panel);
        loadAgentPanel(name);
      }
      if (panel) panel.classList.add('active');

      // Brain Map tab: initialize on first visit
      if (currentTab === 'brain-map') {
        if (typeof initBrainMap === 'function') initBrainMap();
      }

      // Query tab: initialize on first visit (resolves current dashboard agent brain via cosmo23).
      if (currentTab === 'query') {
        if (typeof initQueryTab === 'function') initQueryTab();
      }

      if (currentTab === 'workers') {
        loadWorkersSurface().catch(() => {});
      }

      // Intelligence tab: load content and start refresh
      if (currentTab === 'intelligence') {
        loadIntelligence();
        if (!intelRefreshInterval) {
          intelRefreshInterval = setInterval(loadIntelligence, 30_000);
        }
      } else {
        if (intelRefreshInterval) {
          clearInterval(intelRefreshInterval);
          intelRefreshInterval = null;
        }
      }
    });
  });
}

// ── Workers ──

function workerApiUrl(path = '') {
  const url = new URL(`/home23/api/workers${path}`, window.location.origin);
  if (primaryAgent?.name) url.searchParams.set('agent', primaryAgent.name);
  return `${url.pathname}${url.search}`;
}

const WORKER_CAPABILITY_PROFILES = {
  systems: {
    title: 'Home23 Systems',
    headline: 'Use this when Home23 feels slow, stale, broken, or confusing.',
    useWhen: [
      'A dashboard, endpoint, PM2 process, or live problem needs a grounded check.',
      'You want evidence before restarting anything.',
      'You need a specialist pass that house agents can remember.'
    ],
    can: [
      'Inspect Home23 PM2 status',
      'Probe dashboard and engine endpoints',
      'Read scoped service logs',
      'Verify health, freshness, and receipts'
    ],
    guardrails: [
      'No global PM2 stop/delete',
      'No destructive git or file cleanup',
      'No claimed fix without a verifier'
    ],
    starterTasks: [
      {
        label: 'Check Home23 health',
        detail: 'Verify the dashboard, engine state endpoint, and worker connector are responding. Do not change files or restart processes.'
      },
      {
        label: 'Why is it slow?',
        detail: 'Diagnose whether Home23 is under CPU, memory, process, or endpoint pressure. Use current host evidence and do not make changes.'
      },
      {
        label: 'Inspect live problem',
        detail: 'Inspect the current Home23 live problem, identify the verifier it depends on, and report the next concrete repair step without changing files.'
      },
      {
        label: 'Verify a fix',
        detail: 'Re-run the relevant Home23 verifier for the described issue and produce a receipt with pass/fail evidence. Do not change files unless explicitly requested.'
      }
    ]
  },
  freshness: {
    title: 'Freshness',
    headline: 'Use this when data may look recent but could be stale underneath.',
    useWhen: [
      'A metric, sensor, snapshot, or receipt may be stale.',
      'A file was written recently but its payload has older dates.',
      'A house agent needs to know whether evidence is current enough to trust.'
    ],
    can: [
      'Compare wrapper timestamps with semantic dates',
      'Check endpoint freshness',
      'Classify data as fresh, stale, historical-only, or unknown',
      'Identify the next freshness verifier'
    ],
    guardrails: [
      'Read-only by default',
      'No historical data as operational truth',
      'No freshness claim from mtime alone'
    ],
    starterTasks: [
      {
        label: 'Check data freshness',
        detail: 'Check whether the named Home23 data source is current enough to trust. Compare file timestamps, payload dates, receipts, and endpoint state.'
      },
      {
        label: 'Find stale signals',
        detail: 'Inspect current Home23 state for stale metrics, stale snapshots, or contradictory freshness evidence. Do not change files.'
      },
      {
        label: 'Verify latest receipt',
        detail: 'Inspect the latest relevant receipt or state snapshot and report whether it represents current truth or older belief.'
      }
    ]
  },
  memory: {
    title: 'Memory',
    headline: 'Use this when Home23 may be remembering old conclusions as current truth.',
    useWhen: [
      'A problem keeps being rediscovered.',
      'Old memory may outrank current state.',
      'Resolved work needs a receipt or suppression clue.'
    ],
    can: [
      'Compare old beliefs with current snapshots',
      'Find duplicate loops',
      'Identify missing goal-resolution receipts',
      'Prepare memory-curator handoffs'
    ],
    guardrails: [
      'No silent memory rewrites',
      'Current state before narrative memory',
      'Exact evidence required'
    ],
    starterTasks: [
      {
        label: 'Audit stale belief',
        detail: 'Audit whether the described belief is current, stale, resolved, duplicated, or unknown. Prefer state snapshots and resolution receipts over old narrative memory.'
      },
      {
        label: 'Find rediscovery loop',
        detail: 'Look for repeated Home23 memory or agenda entries that indicate a rediscovery loop. Return evidence and a memory-curator handoff.'
      },
      {
        label: 'Check resolution receipt',
        detail: 'Check whether the described completed work has a durable resolution receipt that should suppress future rediscovery.'
      }
    ]
  },
  parity: {
    title: 'Parity',
    headline: 'Use this when a native or web surface needs to match Home23 behavior.',
    useWhen: [
      'A Mac, iOS, tvOS, or web surface is missing a Home23 capability.',
      'A feature needs portable client instructions.',
      'A contract needs to explain product intent, not just endpoints.'
    ],
    can: [
      'Compare source and target surfaces',
      'Extract endpoint and response contracts',
      'Separate portable behavior from local setup',
      'Produce implementation handoffs'
    ],
    guardrails: [
      'No intent from git diff alone',
      'No site-specific defaults in portable contracts',
      'Contract before client code'
    ],
    starterTasks: [
      {
        label: 'Create parity handoff',
        detail: 'Compare the source Home23 feature with the target client surface and produce a portable parity handoff with routes, models, UX expectations, and smoke tests.'
      },
      {
        label: 'Check client gap',
        detail: 'Inspect whether the target client already implements the described Home23 feature. Return missing files, contract gaps, and next implementation steps.'
      },
      {
        label: 'Review contract',
        detail: 'Review the described API or UI contract for portability, selected-agent routing, and native-client readiness.'
      }
    ]
  },
  release: {
    title: 'Release',
    headline: 'Use this before shipping a Home23 app, package, or service change.',
    useWhen: [
      'A build or upload needs preflight evidence.',
      'Version, build, changelog, or artifact state may be wrong.',
      'A release needs a checklist before execution.'
    ],
    can: [
      'Check version/build metadata',
      'Inspect recent commits and release notes',
      'Run targeted preflight commands',
      'Produce release handoffs'
    ],
    guardrails: [
      'No publishing without explicit request',
      'No version bump without scope',
      'Exact artifact evidence required'
    ],
    starterTasks: [
      {
        label: 'Release preflight',
        detail: 'Run a release readiness preflight for the described target. Check version/build metadata, recent commits, required tests, and likely blockers.'
      },
      {
        label: 'Build checklist',
        detail: 'Produce a portable build and upload checklist for the described app or package without changing versions or publishing artifacts.'
      },
      {
        label: 'Changelog summary',
        detail: 'Summarize the release-relevant changes since the named commit or tag and identify any missing verification.'
      }
    ]
  },
  feeder: {
    title: 'Feeder',
    headline: 'Use this when documents are not flowing into memory correctly.',
    useWhen: [
      'Ingestion counts, watch paths, or compiler queues look wrong.',
      'Files are pending, quarantined, stale, or not compiled.',
      'A document source needs diagnosis before changing settings.'
    ],
    can: [
      'Inspect feeder status and manifests',
      'Compare watch paths to processed files',
      'Check compiler, converter, and quarantine state',
      'Report ingestion freshness'
    ],
    guardrails: [
      'No moving user files by default',
      'No watcher equals compiled assumption',
      'Exact paths and counts when available'
    ],
    starterTasks: [
      {
        label: 'Check ingestion health',
        detail: 'Inspect Home23 ingestion health for the selected agent. Report watch paths, pending files, processed count, compiled count, quarantine state, and freshness.'
      },
      {
        label: 'Find stuck documents',
        detail: 'Look for documents that are pending, quarantined, stale, or not compiled. Do not move or delete files.'
      },
      {
        label: 'Verify feeder settings',
        detail: 'Check whether feeder settings and live feeder status agree. Return mismatches and the next repair step.'
      }
    ]
  }
};

function getWorkerCapability(worker) {
  return WORKER_CAPABILITY_PROFILES[worker?.name] || {
    title: worker?.displayName || worker?.name || 'Worker',
    headline: worker?.purpose || 'Reusable specialist for bounded Home23 work.',
    useWhen: ['A house agent needs a focused, reusable pass without spinning up a full engine.'],
    can: ['Work in its own workspace', 'Produce a receipt', 'Return evidence for house-agent memory'],
    guardrails: ['Bounded task scope', 'Receipt before claims', 'No hidden full engine'],
    starterTasks: [
      {
        label: 'Run focused check',
        detail: `Use ${worker?.displayName || worker?.name || 'this worker'} for a bounded check. Return evidence, verifier status, and a one-sentence summary.`
      }
    ]
  };
}

async function workerApi(path = '', options = {}) {
  const res = await fetch(workerApiUrl(path), {
    ...options,
    headers: {
      accept: 'application/json',
      ...(options.body ? { 'content-type': 'application/json' } : {}),
      ...(options.headers || {}),
    },
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `Worker API HTTP ${res.status}`);
  return data;
}

function setupWorkersSurface() {
  document.getElementById('workers-refresh-btn')?.addEventListener('click', () => {
    loadWorkersSurface().catch((err) => renderWorkersError(err));
  });
  document.getElementById('worker-run-btn')?.addEventListener('click', () => {
    runWorkerFromDashboard().catch((err) => {
      const status = document.getElementById('worker-run-status');
      if (status) status.textContent = err.message;
    });
  });
  document.getElementById('worker-run-select')?.addEventListener('change', renderWorkerIntents);
  document.getElementById('worker-intents')?.addEventListener('click', (event) => {
    const button = event.target.closest('[data-worker-starter]');
    if (!button) return;
    const promptEl = document.getElementById('worker-run-prompt');
    if (promptEl) {
      promptEl.value = button.dataset.workerStarter || '';
      promptEl.focus();
    }
  });
  document.getElementById('workers-runs')?.addEventListener('click', (event) => {
    const button = event.target.closest('[data-worker-run-id]');
    if (!button) return;
    openWorkerReceipt(button.dataset.workerRunId).catch((err) => renderWorkerReceiptError(err));
  });
  document.getElementById('workers-receipt')?.addEventListener('click', (event) => {
    const button = event.target.closest('[data-worker-promote-memory]');
    if (!button) return;
    promoteWorkerMemory(button.dataset.workerPromoteMemory).catch((err) => renderWorkerReceiptError(err));
  });
}

async function loadWorkersSurface() {
  const [workersData, templatesData, runsData] = await Promise.all([
    workerApi(''),
    workerApi('/templates'),
    workerApi('/runs'),
  ]);
  workersState = {
    ...workersState,
    workers: workersData.workers || [],
    templates: templatesData.templates || [],
    runs: runsData.runs || [],
    lastLoadedAt: new Date(),
  };
  renderWorkersSurface();
}

function renderWorkersError(err) {
  const roster = document.getElementById('workers-roster');
  const runs = document.getElementById('workers-runs');
  if (roster) roster.innerHTML = `<div class="h23-workers-empty">Worker connector unavailable: ${escapeHtml(err.message)}</div>`;
  if (runs) runs.innerHTML = '<div class="h23-workers-empty">Recent runs are unavailable.</div>';
}

function renderWorkersSurface() {
  renderWorkerCapabilities();
  renderWorkerStats();
  renderWorkerRunSelect();
  renderWorkerIntents();
  renderWorkerRoster();
  renderWorkerRuns();
}

function renderWorkerCapabilities() {
  const container = document.getElementById('workers-capabilities');
  if (!container) return;
  if (workersState.workers.length === 0) {
    container.innerHTML = `
      <div class="h23-worker-capability-card wide">
        <div class="h23-worker-capability-kicker">Nothing installed yet</div>
        <h3>Create the first worker in the Worker Library.</h3>
        <p>Workers are reusable specialists that keep their own workspace and return receipts to the house agents.</p>
      </div>
    `;
    return;
  }

  container.innerHTML = workersState.workers.map((worker) => {
    const profile = getWorkerCapability(worker);
    return `
      <article class="h23-worker-capability-card">
        <div class="h23-worker-capability-kicker">${escapeHtml(worker.displayName || worker.name)}</div>
        <h3>${escapeHtml(profile.headline)}</h3>
        <div class="h23-worker-capability-columns">
          <div>
            <strong>Use when</strong>
            <ul>${profile.useWhen.map(item => `<li>${escapeHtml(item)}</li>`).join('')}</ul>
          </div>
          <div>
            <strong>Can check</strong>
            <ul>${profile.can.map(item => `<li>${escapeHtml(item)}</li>`).join('')}</ul>
          </div>
        </div>
        <div class="h23-worker-guardrails">${profile.guardrails.map(item => `<span>${escapeHtml(item)}</span>`).join('')}</div>
      </article>
    `;
  }).join('');
}

function renderWorkerStats() {
  const stats = document.getElementById('workers-stats');
  if (!stats) return;
  const passCount = workersState.runs.filter(run => run.verifierStatus === 'pass').length;
  const memoryCount = workersState.receipt?.memoryCandidates?.length || 0;
  stats.innerHTML = [
    ['Installed Specialists', workersState.workers.length],
    ['Completed Checks', workersState.runs.length],
    ['Verified Passes', passCount],
    ['Memory Handoffs', memoryCount],
  ].map(([label, value]) => `<div class="h23-worker-stat"><span>${escapeHtml(value)}</span><label>${escapeHtml(label)}</label></div>`).join('');
}

function renderWorkerRunSelect() {
  const select = document.getElementById('worker-run-select');
  if (!select) return;
  const current = select.value;
  if (workersState.workers.length === 0) {
    select.innerHTML = '<option value="">No workers available</option>';
    return;
  }
  select.innerHTML = workersState.workers.map((worker) => {
    const selected = current === worker.name ? ' selected' : '';
    return `<option value="${escapeHtml(worker.name)}"${selected}>${escapeHtml(worker.displayName || worker.name)}</option>`;
  }).join('');
}

function getSelectedWorkerForRun() {
  const select = document.getElementById('worker-run-select');
  const name = select?.value || workersState.workers[0]?.name;
  return workersState.workers.find(worker => worker.name === name) || workersState.workers[0] || null;
}

function renderWorkerIntents() {
  const container = document.getElementById('worker-intents');
  if (!container) return;
  const worker = getSelectedWorkerForRun();
  if (!worker) {
    container.innerHTML = '<div class="h23-workers-empty">Install a worker before running checks.</div>';
    return;
  }
  const profile = getWorkerCapability(worker);
  container.innerHTML = profile.starterTasks.map(task => `
    <button class="h23-worker-intent" type="button" data-worker-starter="${escapeAttr(task.detail)}">
      <strong>${escapeHtml(task.label)}</strong>
      <span>${escapeHtml(task.detail)}</span>
    </button>
  `).join('');
}

function renderWorkerRoster() {
  const container = document.getElementById('workers-roster');
  if (!container) return;
  if (workersState.workers.length === 0) {
    container.innerHTML = '<div class="h23-workers-empty">No workers have been installed yet. Open the Worker Library to add the first specialist.</div>';
    return;
  }
  container.innerHTML = workersState.workers.map((worker) => `
    <div class="h23-worker-row">
      <div>
        <div class="h23-worker-name">${escapeHtml(worker.displayName || worker.name)}</div>
        <div class="h23-worker-meta">${escapeHtml(worker.name)} · ${escapeHtml(worker.ownerAgent || 'house')} · ${escapeHtml(worker.class || 'worker')}</div>
        <div class="h23-worker-purpose">${escapeHtml(worker.purpose || '')}</div>
        <div class="h23-worker-human">${escapeHtml(getWorkerCapability(worker).headline)}</div>
      </div>
      <span class="h23-worker-pill">${escapeHtml(worker.class || 'worker')}</span>
    </div>
  `).join('');
}

function formatWorkerTime(value) {
  if (!value) return '—';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  return date.toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
}

function workerStatusClass(value) {
  if (value === 'fixed' || value === 'pass') return 'pass';
  if (value === 'failed' || value === 'fail') return 'fail';
  if (value === 'blocked' || value === 'cancelled' || value === 'stale') return 'blocked';
  return 'neutral';
}

function renderWorkerRuns() {
  const container = document.getElementById('workers-runs');
  if (!container) return;
  const runs = workersState.runs.slice(0, 20);
  if (runs.length === 0) {
    container.innerHTML = '<div class="h23-workers-empty">No worker runs yet.</div>';
    return;
  }
  container.innerHTML = runs.map((run) => `
    <button class="h23-worker-run-row" type="button" data-worker-run-id="${escapeHtml(run.runId)}">
      <span class="h23-worker-run-main">
        <strong>${escapeHtml(run.worker)}</strong>
        <span>${escapeHtml(run.summary || run.runId)}</span>
        <small>owner ${escapeHtml(run.ownerAgent || 'house')}</small>
      </span>
      <span class="h23-worker-run-side">
        <span class="h23-worker-status ${workerStatusClass(run.status)}">${escapeHtml(run.status || 'running')}</span>
        <span class="h23-worker-status ${workerStatusClass(run.verifierStatus)}">${escapeHtml(run.verifierStatus || 'unknown')}</span>
        <time>${escapeHtml(formatWorkerTime(run.finishedAt || run.startedAt))}</time>
      </span>
    </button>
  `).join('');
}

async function runWorkerFromDashboard() {
  const select = document.getElementById('worker-run-select');
  const promptEl = document.getElementById('worker-run-prompt');
  const button = document.getElementById('worker-run-btn');
  const status = document.getElementById('worker-run-status');
  const worker = select?.value;
  const prompt = promptEl?.value?.trim();
  if (!worker) throw new Error('Select a specialist first.');
  if (!prompt) throw new Error('Pick a starter action or describe what you want checked.');

  if (button) button.disabled = true;
  if (status) status.textContent = 'Running...';
  try {
    const data = await workerApi(`/${encodeURIComponent(worker)}/runs`, {
      method: 'POST',
      body: JSON.stringify({
        prompt,
        requestedBy: 'human',
        requester: 'home23-dashboard',
        ownerAgent: primaryAgent?.name,
      }),
    });
    if (status) status.textContent = `Check complete: ${data.receipt?.status || data.runId}`;
    if (promptEl) promptEl.value = '';
    workersState.receipt = data.receipt || null;
    await loadWorkersSurface();
    if (data.runId) await openWorkerReceipt(data.runId);
  } finally {
    if (button) button.disabled = false;
  }
}

async function openWorkerReceipt(runId) {
  const receipt = await workerApi(`/runs/${encodeURIComponent(runId)}/receipt`);
  workersState.receipt = receipt;
  renderWorkerReceipt(receipt);
  renderWorkerStats();
}

function renderWorkerReceiptError(err) {
  const container = document.getElementById('workers-receipt');
  if (container) container.innerHTML = `<div class="h23-workers-empty">Receipt unavailable: ${escapeHtml(err.message)}</div>`;
}

function renderWorkerReceipt(receipt) {
  const container = document.getElementById('workers-receipt');
  if (!container) return;
  const evidence = (receipt.evidence || []).map(item => `
    <li><span class="h23-worker-status ${workerStatusClass(item.status)}">${escapeHtml(item.status || 'unknown')}</span> ${escapeHtml(item.type)} — ${escapeHtml(item.detail)}</li>
  `).join('');
  const artifacts = (receipt.artifacts || []).map(item => `<li><code>${escapeHtml(item)}</code></li>`).join('');
  const memory = (receipt.memoryCandidates || []).map(item => `<li>${escapeHtml(item.text)} <span>${escapeHtml(Math.round((item.confidence || 0) * 100))}%</span></li>`).join('');
  container.innerHTML = `
    <div class="h23-worker-receipt-head">
      <div>
        <div class="h23-worker-name">${escapeHtml(receipt.worker)} · ${escapeHtml(receipt.runId)}</div>
        <div class="h23-worker-meta">${escapeHtml(formatWorkerTime(receipt.startedAt))} → ${escapeHtml(formatWorkerTime(receipt.finishedAt))}</div>
      </div>
      <div class="h23-worker-receipt-badges">
        <span class="h23-worker-status ${workerStatusClass(receipt.status)}">${escapeHtml(receipt.status)}</span>
        <span class="h23-worker-status ${workerStatusClass(receipt.verifierStatus)}">${escapeHtml(receipt.verifierStatus)}</span>
      </div>
    </div>
    <p><strong>What happened:</strong> ${escapeHtml(receipt.summary || '')}</p>
    ${receipt.rootCause ? `<div class="h23-worker-root-cause">${escapeHtml(receipt.rootCause)}</div>` : ''}
    <div class="h23-worker-receipt-block">
      <h4>What was checked</h4>
      <ul>${evidence || '<li>No evidence recorded.</li>'}</ul>
    </div>
    <div class="h23-worker-receipt-block">
      <h4>Files produced</h4>
      <ul>${artifacts || '<li>No artifacts recorded.</li>'}</ul>
    </div>
    <div class="h23-worker-receipt-block">
      <h4>What Jerry can learn</h4>
      <ul>${memory || '<li>No memory candidates proposed.</li>'}</ul>
      <button class="h23-worker-btn secondary" type="button" data-worker-promote-memory="${escapeHtml(receipt.runId)}">Send to Memory Curator</button>
      <span class="h23-workers-status" id="worker-memory-status"></span>
    </div>
  `;
}

async function promoteWorkerMemory(runId) {
  const data = await workerApi(`/runs/${encodeURIComponent(runId)}/promote-memory`, { method: 'POST', body: JSON.stringify({}) });
  const status = document.getElementById('worker-memory-status');
  if (status) status.textContent = `${data.candidates || 0} candidate(s) ready for the memory curator.`;
}

function isGoodLifeWorkerSource(source) {
  const type = String(source?.type || '');
  return type === 'good-life-agenda' || type === 'live-problem' || type.startsWith('good-life');
}

function goodLifeWorkerRunsForScope(scope = 'home') {
  const owner = goodLifeAgentForScope(scope)?.name || primaryAgent?.name || '';
  return (workersState.runs || [])
    .filter((run) => {
      const sourceLinked = isGoodLifeWorkerSource(run.source);
      const requestedByGoodLife = String(run.requestedBy || '').startsWith('good-life');
      if (!sourceLinked && !requestedByGoodLife) return false;
      return !owner || !run.ownerAgent || run.ownerAgent === owner;
    })
    .slice(0, 8);
}

function formatWorkerRunSource(run) {
  const source = run?.source || {};
  const type = source.type === 'good-life-agenda'
    ? 'agenda'
    : source.type === 'live-problem'
      ? 'issue'
      : source.type || run?.requestedBy || 'worker';
  return [type, source.id].filter(Boolean).join(': ');
}

async function openGoodLifeWorkerReceipt(runId) {
  if (!runId) return;
  try {
    await openWorkerReceipt(runId);
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `Opened worker receipt ${runId}.`);
  } catch (err) {
    setText('goodlife-overlay-action-status', `Worker receipt unavailable: ${err.message}`);
  }
}

function renderGoodLifeWorkerReceiptDetail() {
  const receipt = workersState.receipt;
  if (!receipt || !isGoodLifeWorkerSource(receipt.source)) return '';
  const evidence = (receipt.evidence || []).slice(0, 4).map((item) => (
    `<li><span class="h23-worker-status ${workerStatusClass(item.status)}">${escapeHtml(item.status || 'unknown')}</span> ${escapeHtml(item.type || 'evidence')} - ${escapeHtml(item.detail || '')}</li>`
  )).join('');
  return `
    <div class="h23-goodlife-worker-receipt-detail">
      <div class="h23-goodlife-detail-head">
        <span class="h23-worker-status ${workerStatusClass(receipt.status)}">${escapeHtml(receipt.status || 'recorded')}</span>
        <strong>${escapeHtml(receipt.worker || 'worker')} · ${escapeHtml(receipt.runId || '')}</strong>
      </div>
      <p>${escapeHtml(receipt.summary || '')}</p>
      <ul>${evidence || '<li>No evidence recorded.</li>'}</ul>
    </div>
  `;
}

function goodLifeIssueWorkerName(problem = {}) {
  const plan = Array.isArray(problem.remediation) ? problem.remediation : [];
  const workerStep = plan.find((step) => step?.type === 'dispatch_to_worker' && step.args?.worker);
  return workerStep?.args?.worker || 'systems';
}

function goodLifeIssueLatestWorkerRun(problem = {}) {
  if (!problem?.id) return null;
  const matchesProblem = (run) => (
    run?.source?.type === 'live-problem'
    && run.source.id === problem.id
  );
  const runs = (workersState.runs || [])
    .filter(matchesProblem)
    .sort((a, b) => Date.parse(b.finishedAt || b.startedAt || b.createdAt || 0) - Date.parse(a.finishedAt || a.startedAt || a.createdAt || 0));
  return runs[0] || null;
}

function renderGoodLifeIssueWorkerReceipt(problem = {}) {
  if (!problem?.id) return '';
  const receipt = workersState.receipt;
  if (receipt?.source?.type === 'live-problem' && receipt.source.id === problem.id) {
    return renderGoodLifeWorkerReceiptDetail();
  }
  const latest = goodLifeIssueLatestWorkerRun(problem);
  if (!latest) return '';
  return `
    <section><h4>Latest Worker Receipt</h4>
      <div class="h23-goodlife-worker-receipt-detail">
        <div class="h23-goodlife-detail-head">
          <span class="h23-worker-status ${workerStatusClass(latest.status)}">${escapeHtml(latest.status || 'recorded')}</span>
          <strong>${escapeHtml(latest.worker || 'worker')} · ${escapeHtml(latest.runId || '')}</strong>
        </div>
        <p>${escapeHtml(latest.summary || 'Worker receipt recorded.')}</p>
        <div class="h23-goodlife-mini-actions">
          <span class="h23-worker-status ${workerStatusClass(latest.verifierStatus)}">${escapeHtml(latest.verifierStatus || 'unknown')}</span>
          <button type="button" onclick="openGoodLifeWorkerReceipt('${escapeAttr(latest.runId || '')}')">Open Receipt</button>
        </div>
      </div>
    </section>
  `;
}

function goodLifeIssueRecommendedStep(problem = {}) {
  const lastOk = problem.lastResult?.ok === true;
  if (problem.state === 'resolved' || lastOk) return 'Verifier is passing; no operator repair action is needed.';
  const latest = goodLifeIssueLatestWorkerRun(problem);
  const needsUser = goodLifeNeedsUser(problem);
  if (latest?.status === 'running') return `${latest.worker || goodLifeIssueWorkerName(problem)} worker is running; wait for its receipt before marking handled.`;
  if (latest?.summary) return latest.summary;
  if (needsUser) return `Run the ${goodLifeIssueWorkerName(problem)} worker, inspect the receipt, then mark handled only after the verifier passes or the manual action is done.`;
  const next = goodLifeNextRemediation(problem);
  return next.type ? `Let autonomous remediation continue with ${next.type}.` : 'Re-run the verifier, then dispatch a worker if the issue still fails.';
}

function renderGoodLifeIssueInterventionConsole(problem = {}) {
  if (!problem?.id) return '';
  const latest = goodLifeIssueLatestWorkerRun(problem);
  const workerName = goodLifeIssueWorkerName(problem);
  const needsUser = goodLifeNeedsUser(problem);
  const last = problem.lastResult || {};
  const next = goodLifeNextRemediation(problem);
  const verifierText = last.detail || 'not checked yet';
  const receiptText = latest
    ? `${latest.status || latest.verifierStatus || 'recorded'}${latest.runId ? ` - ${latest.runId}` : ''}`
    : 'no worker receipt yet';
  return `
    <section class="h23-goodlife-intervention-console">
      <h4>Operator Intervention</h4>
      <div class="h23-goodlife-intervention-grid">
        <div>
          <label>Recommended next step</label>
          <p>${escapeHtml(goodLifeIssueRecommendedStep(problem))}</p>
        </div>
        <div>
          <label>Verifier gate</label>
          <p>${escapeHtml(verifierText)}</p>
        </div>
        <div>
          <label>Worker lane</label>
          <p>${escapeHtml(workerName)}${latest?.worker ? ` - latest ${escapeHtml(latest.worker)}` : ''}</p>
        </div>
        <div>
          <label>Current receipt</label>
          <p>${escapeHtml(receiptText)}</p>
        </div>
      </div>
      <div class="h23-goodlife-intervention-actions">
        <button class="h23-goodlife-plain-btn" type="button" onclick="testGoodLifeVerifier('${escapeAttr(problem.id)}')">Check Verifier</button>
        <button class="h23-goodlife-plain-btn" type="button" onclick="runGoodLifeWorkerCheck('${escapeAttr(problem.id)}')">Ask ${escapeHtml(workerName)} Worker</button>
        ${latest?.runId ? `<button class="h23-goodlife-plain-btn" type="button" onclick="openGoodLifeWorkerReceipt('${escapeAttr(latest.runId)}')">Open Receipt</button>` : ''}
        <button class="h23-goodlife-plain-btn" type="button" onclick="openGoodLifeWorkers()">Worker Desk</button>
        <button class="h23-goodlife-plain-btn" type="button" onclick="reverifyGoodLifeOperator()">Run Engine Check</button>
        ${needsUser ? `<button class="h23-goodlife-plain-btn" type="button" onclick="recordGoodLifeUserIntervention('${escapeAttr(problem.id)}')">Record Handled</button>` : ''}
      </div>
      <div class="h23-goodlife-intervention-note">
        <strong>${escapeHtml(next.type || 'manual')}</strong>
        <span>${escapeHtml(next.text || 'verifier remains the gate')}</span>
      </div>
    </section>
  `;
}

function formatGoodLifeWorkerResult(receipt, fallback) {
  if (!receipt) return fallback || 'receipt recorded';
  const status = receipt.status || receipt.verifierStatus || 'recorded';
  const summary = receipt.summary ? ` - ${receipt.summary}` : '';
  const runId = receipt.runId ? ` (${receipt.runId})` : '';
  return `${status}${runId}${summary}`;
}

// ── Home Tiles (current dashboard agent) ──

function layoutHasTile(tileId) {
  return homeTileLayout.some((item) => item.tileId === tileId);
}

function fallbackHomeLayout() {
  return [
    { tileId: 'thought-feed', size: 'third', tile: { id: 'thought-feed', kind: 'core' } },
    { tileId: 'vibe', size: 'third', tile: { id: 'vibe', kind: 'core' } },
    { tileId: 'chat', size: 'third', tile: { id: 'chat', kind: 'core' } },
    { tileId: 'outside-weather', size: 'half', tile: { id: 'outside-weather', kind: 'custom' } },
    { tileId: 'sauna-control', size: 'half', tile: { id: 'sauna-control', kind: 'custom' } },
    { tileId: 'system-summary', size: 'full', tile: { id: 'system-summary', kind: 'core' } },
    { tileId: 'good-life', size: 'full', tile: { id: 'good-life', kind: 'core' } },
    { tileId: 'brain-log', size: 'half', tile: { id: 'brain-log', kind: 'core' } },
    { tileId: 'dream-log', size: 'half', tile: { id: 'dream-log', kind: 'core' } },
    { tileId: 'feeder', size: 'full', tile: { id: 'feeder', kind: 'core' } },
  ];
}

function getVisibleCustomTiles() {
  return homeTileLayout.filter((item) => item?.tile?.kind === 'custom');
}

function getHomeTile(tileId) {
  return homeTileLayout.find((item) => item.tileId === tileId)?.tile || null;
}

function renderThoughtFeedTile() {
  return `
    <div class="h23-tile h23-tile-thoughts h23-tile-pulse" style="cursor:pointer;" onclick="openPulseHistoryPanel()" title="Tap to see pulse history">
      <div class="h23-tile-header" style="display:flex;align-items:center;gap:8px;">
        💬 <span id="primary-agent-name">${escapeHtml(primaryAgent?.displayName || primaryAgent?.name || 'Agent')}</span>
        <span id="pulse-remark-age" style="margin-left:auto;font-size:11px;color:var(--text-muted);font-weight:400;"></span>
      </div>
      <div id="pulse-remark-body" style="display:flex;flex-direction:column;gap:10px;">
        <div id="pulse-remark-text" style="font-size:14px;line-height:1.55;color:var(--text-primary);">Loading…</div>
        <div id="pulse-rotating-stat" style="font-size:12px;color:var(--text-muted);border-top:1px solid var(--glass-border);padding-top:8px;min-height:20px;"></div>
      </div>
    </div>
  `;
}

function renderVibeTile() {
  return `
    <div class="h23-tile h23-tile-vibe">
      <div class="h23-tile-header"><span id="vibe-trigger">🎨 Vibe</span></div>
      <div class="h23-vibe-image" id="home-vibe-image">
        <span class="h23-vibe-placeholder">Generating...</span>
      </div>
      <div class="h23-vibe-caption" id="home-vibe-caption"></div>
      <div class="h23-vibe-actions">
        <a class="h23-vibe-action" id="home-vibe-gallery-link" href="/home23/vibe-gallery">Gallery</a>
      </div>
    </div>
  `;
}

function renderChatTile() {
  return `
    <div class="h23-tile h23-tile-chat" id="tile-chat">
      <div class="h23-chat-header">
        <button class="h23-chat-agent-pill" id="chat-agent-pill" type="button" title="Switch agent">
          <span class="h23-chat-agent-avatar" id="chat-agent-avatar">…</span>
          <span class="h23-chat-agent-name" id="chat-agent-name">Loading…</span>
        </button>
        <button class="h23-chat-model-pill" id="chat-model-pill" type="button" title="Change model">
          <span id="chat-model-pill-label">model</span>
        </button>
        <div class="h23-chat-actions">
          <button class="h23-chat-expand-btn" id="chat-expand-btn" type="button" title="Expand">&#8599;</button>
          <button class="h23-chat-expand-btn" id="chat-more-btn" type="button" title="More" aria-haspopup="menu" aria-expanded="false">&#8230;</button>
        </div>
        <!-- Hidden selects keep existing populate/select logic working without
             cluttering the tile header. ⋯ menu exposes model/agent change. -->
        <select class="h23-chat-agent-select" id="chat-agent-select" hidden>
          <option>Loading...</option>
        </select>
        <select class="h23-chat-model-select" id="chat-model-select" hidden>
          <option>model</option>
        </select>
      </div>
      <div class="h23-chat-conv-panel" id="chat-conv-panel">
        <div style="padding:10px 14px;border-bottom:1px solid var(--glass-border);display:flex;justify-content:space-between;align-items:center;">
          <span style="font-size:12px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.5px;">History</span>
          <button class="h23-chat-expand-btn" type="button" onclick="toggleConversationList()" title="Close" style="width:24px;height:24px;font-size:12px;">&#10005;</button>
        </div>
        <div class="h23-chat-conv-list" id="chat-conv-list"></div>
      </div>
      <!-- Slot: shared message-list + input subtree is cloned here from
           #chat-shared-template at init, then moved in/out of the overlay
           on expand/collapse via appendChild. -->
      <div class="h23-chat-slot" id="chat-slot-tile" data-slot="tile"></div>
    </div>
  `;
}

function renderSystemSummaryTile() {
  return `
    <div class="h23-tile h23-tile-system-summary">
      <div class="h23-tile-header"><span class="icon">⚡</span> System Summary</div>
      <div class="h23-system-bar" id="system-bar">
        <div class="h23-system-bar-item"><label>Uptime</label><div class="value" id="sys-uptime">—</div></div>
        <div class="h23-system-bar-item"><label>Thoughts</label><div class="value" id="sys-thoughts">—</div></div>
        <div class="h23-system-bar-item"><label>Nodes</label><div class="value" id="sys-nodes">—</div></div>
        <div class="h23-system-bar-item"><label>Last</label><div class="value" id="sys-last">—</div></div>
      </div>
      <div class="h23-system-summary-excerpt" id="sys-excerpt"></div>
    </div>
  `;
}

function goodLifeDomId(base, scope = 'home') {
  return scope === 'home' ? base : `${base}-${scope}`;
}

function renderGoodLifeTile(scope = 'home', title = 'Good Life') {
  const id = (base) => goodLifeDomId(base, scope);
  const fleetHtml = scope === 'home'
    ? '<div class="h23-goodlife-fleet" id="goodlife-fleet-summary"></div>'
    : '';
  return `
    <div class="h23-tile h23-tile-goodlife">
      <div class="h23-tile-header">
        <span><span class="icon">⊙</span> ${escapeHtml(title)}</span>
        <button class="h23-goodlife-detail-btn" type="button" onclick="openGoodLifeOperator('${escapeAttr(scope)}')">Details</button>
      </div>
      <div class="h23-goodlife-head">
        <div>
          <div class="h23-goodlife-policy" id="${id('goodlife-policy')}">Loading...</div>
          <div class="h23-goodlife-summary" id="${id('goodlife-summary')}"></div>
        </div>
        <div class="h23-goodlife-status unknown" id="${id('goodlife-status')}">UNKNOWN</div>
      </div>
      <div class="h23-goodlife-brief" id="${id('goodlife-brief')}"></div>
      ${fleetHtml}
      <div class="h23-goodlife-grid">
        <section class="h23-goodlife-section">
          <div class="h23-goodlife-section-title">Why</div>
          <div class="h23-goodlife-answer" id="${id('goodlife-answer')}"></div>
        </section>
        <section class="h23-goodlife-section">
          <div class="h23-goodlife-section-title">Live Problems</div>
          <div id="${id('goodlife-problems')}"></div>
        </section>
        <section class="h23-goodlife-section">
          <div class="h23-goodlife-section-title">Action Card</div>
          <div class="h23-goodlife-action" id="${id('goodlife-action')}"></div>
        </section>
      </div>
      <div class="h23-goodlife-lanes" id="${id('goodlife-lanes')}"></div>
      <div class="h23-goodlife-meta" id="${id('goodlife-meta')}"></div>
    </div>
  `;
}

function renderBrainLogTile() {
  return `
    <div class="h23-tile h23-tile-brainlog h23-tile-log" onclick="openLogOverlay('brain')">
      <div class="h23-brainlog-header">
        <span class="h23-brainlog-title">🧠 BRAIN LOG</span>
        <span class="h23-brainlog-stamp" id="brainlog-stamp"></span>
      </div>
      <div class="h23-brain-log" id="home-brainlog">
        <p class="h23-muted">Loading...</p>
      </div>
    </div>
  `;
}

function renderDreamLogTile() {
  return `
    <div class="h23-tile h23-tile-brainlog h23-tile-log" onclick="openLogOverlay('dream')">
      <div class="h23-brainlog-header">
        <span class="h23-brainlog-title">💭 DREAM LOG</span>
        <span class="h23-brainlog-stamp" id="dreamlog-stamp"></span>
      </div>
      <div class="h23-dream-log" id="home-dreamlog">
        <p class="h23-muted">Loading...</p>
      </div>
    </div>
  `;
}

function renderFeederTile() {
  return `
    <div class="h23-tile h23-tile-feeder h23-tile-log" id="tile-feeder" onclick="openFeederOverlay()">
      <div class="h23-tile-header"><span class="icon">📥</span> Ingestion Compiler</div>
      <div id="home-feeder">
        <p class="h23-muted">Loading...</p>
      </div>
    </div>
  `;
}

function renderCustomTile(tile) {
  const safeId = tile.id;
  const refreshSeconds = Math.max(5, Math.round((tile.refreshMs || REFRESH_MS) / 1000));

  return `
    <div class="h23-tile h23-tile-custom" id="tile-custom-${safeId}" data-custom-tile-id="${safeId}">
      <div class="h23-tile-header"><span class="icon">${escapeHtml(tile.icon || '🧩')}</span> ${escapeHtml(tile.title || safeId)}</div>
      <div class="h23-custom-status" id="tile-custom-status-${safeId}">Loading...</div>
      <div class="h23-custom-value" id="tile-custom-value-${safeId}">—</div>
      <div class="h23-custom-subtitle" id="tile-custom-subtitle-${safeId}">Connecting to ${escapeHtml(tile.mode)}…</div>
      <div class="h23-custom-metrics" id="tile-custom-metrics-${safeId}"></div>
      <div class="h23-custom-actions" id="tile-custom-actions-${safeId}"></div>
      <div class="h23-custom-footer">
        <span id="tile-custom-cache-${safeId}">refresh ${refreshSeconds}s</span>
        <span id="tile-custom-updated-${safeId}"></span>
      </div>
    </div>
  `;
}

function renderHomeLayoutItem(item) {
  const sizeClass = `h23-home-size-${item.size || 'third'}`;
  let markup = '';

  switch (item.tileId) {
    case 'thought-feed':
      markup = renderThoughtFeedTile();
      break;
    case 'vibe':
      markup = renderVibeTile();
      break;
    case 'chat':
      markup = renderChatTile();
      break;
    case 'system-summary':
      markup = renderSystemSummaryTile();
      break;
    case 'good-life':
      markup = renderGoodLifeTile();
      break;
    case 'brain-log':
      markup = renderBrainLogTile();
      break;
    case 'dream-log':
      markup = renderDreamLogTile();
      break;
    case 'feeder':
      markup = renderFeederTile();
      break;
    default:
      markup = renderCustomTile(item.tile || {});
      break;
  }

  return `<section class="h23-home-item ${sizeClass}" data-home-tile-id="${escapeHtml(item.tileId)}">${markup}</section>`;
}

function renderHomeLayout(layout) {
  const host = document.getElementById('home-layout-grid');
  if (!host) return;

  homeTileLayout = Array.isArray(layout) ? layout : [];
  host.innerHTML = homeTileLayout.map(renderHomeLayoutItem).join('');

  const primaryNameEl = document.getElementById('primary-agent-name');
  if (primaryNameEl && primaryAgent) {
    primaryNameEl.textContent = primaryAgent.displayName || primaryAgent.name;
  }

  setupVibeActions();
  syncCustomTileRefreshers();

  if (layoutHasTile('chat') && typeof initChat === 'function') {
    Promise.resolve(initChat('tile')).catch(() => { /* best effort */ });
  } else if (typeof closeOverlay === 'function') {
    closeOverlay();
  }

  loadVisibleCustomTiles().catch(() => { /* best effort */ });
}

async function loadHomeLayoutConfig({ force = false } = {}) {
  const config = await apiFetch('/home23/api/tiles/config', { timeoutMs: 4000 });
  if (!config?.layout) {
    if (homeTileLayout.length === 0) {
      renderHomeLayout(fallbackHomeLayout());
      homeTileLayoutSignature = 'fallback';
      return true;
    }
    return false;
  }

  const signature = JSON.stringify(config.layout);
  if (!force && signature === homeTileLayoutSignature) {
    return false;
  }

  homeTileLayoutSignature = signature;
  renderHomeLayout(config.layout);
  return true;
}

function setupHomeLayoutHandlers() {
  const host = document.getElementById('home-layout-grid');
  if (!host || host.dataset.bound === 'true') return;

  host.addEventListener('click', (event) => {
    const actionBtn = event.target.closest('[data-tile-action-id]');
    if (!actionBtn) return;
    openTileActionDialog(actionBtn.dataset.tileId, actionBtn.dataset.tileActionId);
  });

  host.dataset.bound = 'true';
}

function setupHomeTileBroadcast() {
  if (typeof BroadcastChannel === 'undefined') return;
  homeTileBroadcast = new BroadcastChannel('home23-dashboard-tiles');
  homeTileBroadcast.addEventListener('message', async () => {
    await refreshHomeTiles().catch(() => { /* best effort */ });
  });
}

async function refreshHomeTiles({ includeLayout = true } = {}) {
  if (homeTilesRefreshPromise) return homeTilesRefreshPromise;

  homeTilesRefreshPromise = (async () => {
    if (includeLayout) await loadHomeLayoutConfig();
    await loadHomeTiles();
    await loadVisibleCustomTiles();
  })().finally(() => {
    homeTilesRefreshPromise = null;
  });

  return homeTilesRefreshPromise;
}

function syncCustomTileRefreshers() {
  const nextTiles = new Map(getVisibleCustomTiles().map((item) => [item.tile.id, item.tile.refreshMs || REFRESH_MS]));

  for (const [tileId, entry] of homeTileCustomRefreshers.entries()) {
    if (!nextTiles.has(tileId) || nextTiles.get(tileId) !== entry.refreshMs) {
      clearInterval(entry.timer);
      homeTileCustomRefreshers.delete(tileId);
      homeTileCustomState.delete(tileId);
    }
  }

  for (const [tileId, refreshMs] of nextTiles.entries()) {
    if (homeTileCustomRefreshers.has(tileId)) continue;
    const timer = setInterval(() => {
      loadCustomTileData(tileId).catch(() => { /* tile-local errors are rendered in-place */ });
    }, refreshMs);
    homeTileCustomRefreshers.set(tileId, { timer, refreshMs });
  }
}

async function loadVisibleCustomTiles() {
  const tiles = getVisibleCustomTiles();
  await Promise.all(tiles.map((item) => loadCustomTileData(item.tile.id).catch(() => null)));
}

function renderCustomTileMetrics(tileId, metrics) {
  const host = document.getElementById(`tile-custom-metrics-${tileId}`);
  if (!host) return;

  if (!Array.isArray(metrics) || metrics.length === 0) {
    host.innerHTML = '';
    return;
  }

  host.innerHTML = metrics.map((metric) => `
    <div class="h23-custom-metric">
      <span class="h23-custom-metric-label">${escapeHtml(metric.label || 'Metric')}</span>
      <span class="h23-custom-metric-value">${escapeHtml(metric.value ?? '—')}</span>
    </div>
  `).join('');
}

function renderCustomTileActions(tileId, actions) {
  const host = document.getElementById(`tile-custom-actions-${tileId}`);
  if (!host) return;

  if (!Array.isArray(actions) || actions.length === 0) {
    host.innerHTML = '';
    return;
  }

  host.innerHTML = actions.map((action) => `
    <button class="h23-custom-action-btn" type="button" data-tile-id="${escapeHtml(tileId)}" data-tile-action-id="${escapeHtml(action.id)}">
      ${escapeHtml(action.label || action.id)}
    </button>
  `).join('');
}

function renderCustomTileData(tileId, payload) {
  const tileEl = document.getElementById(`tile-custom-${tileId}`);
  if (!tileEl) return;

  const content = payload?.content || {};
  tileEl.classList.remove('is-error');
  setText(`tile-custom-status-${tileId}`, content.status ?? 'Live');
  setText(`tile-custom-value-${tileId}`, content.value ?? '—');
  setText(`tile-custom-subtitle-${tileId}`, content.subtitle ?? '');
  setText(`tile-custom-cache-${tileId}`, payload?.cache?.hit
    ? `cached · ${Math.round((payload.cache.refreshMs || REFRESH_MS) / 1000)}s ttl`
    : `refresh ${Math.round((payload?.cache?.refreshMs || getHomeTile(tileId)?.refreshMs || REFRESH_MS) / 1000)}s`);
  setText(`tile-custom-updated-${tileId}`, payload?.fetchedAt ? `Updated ${timeSince(new Date(payload.fetchedAt))}` : '');
  renderCustomTileMetrics(tileId, content.metrics || []);
  renderCustomTileActions(tileId, payload?.actions || []);

  homeTileCustomState.set(tileId, {
    ...(homeTileCustomState.get(tileId) || {}),
    payload,
  });
}

function renderCustomTileError(tileId, error) {
  const tileEl = document.getElementById(`tile-custom-${tileId}`);
  if (!tileEl) return;

  tileEl.classList.add('is-error');
  setText(`tile-custom-status-${tileId}`, 'Unavailable');
  setText(`tile-custom-value-${tileId}`, '—');
  setText(`tile-custom-subtitle-${tileId}`, error?.message || 'Tile request failed');
  setText(`tile-custom-cache-${tileId}`, 'retrying automatically');
  setText(`tile-custom-updated-${tileId}`, '');
  renderCustomTileMetrics(tileId, []);
  renderCustomTileActions(tileId, []);
}

async function loadCustomTileData(tileId) {
  try {
    const res = await fetch(`/home23/api/tiles/${encodeURIComponent(tileId)}/data`, {
      signal: AbortSignal.timeout(15_000),
    });
    const data = await res.json();
    if (!res.ok || !data.ok) {
      throw new Error(data.error || `Tile request failed (${res.status})`);
    }
    renderCustomTileData(tileId, data);
    return data;
  } catch (err) {
    renderCustomTileError(tileId, err);
    throw err;
  }
}

function buildTileActionFieldInput(field) {
  if (field.type === 'boolean') {
    return `
      <label class="h23-tile-action-checkbox">
        <input type="checkbox" data-tile-action-input="${escapeHtml(field.id)}" ${field.defaultValue ? 'checked' : ''}>
        <span>${escapeHtml(field.label)}</span>
      </label>
    `;
  }

  const inputType = field.type === 'number' ? 'number' : 'text';
  const value = field.defaultValue ?? '';
  return `
    <label>${escapeHtml(field.label)}</label>
    <input type="${inputType}" data-tile-action-input="${escapeHtml(field.id)}" value="${escapeHtml(value)}" ${field.required ? 'required' : ''}>
  `;
}

function openTileActionDialog(tileId, actionId) {
  const tile = getHomeTile(tileId);
  const runtimeState = homeTileCustomState.get(tileId);
  const action = runtimeState?.payload?.actions?.find((entry) => entry.id === actionId);
  if (!tile || !action) return;

  const requiresDialog = (action.fields && action.fields.length > 0) || action.confirmationText || action.method !== 'GET';
  if (!requiresDialog) {
    runTileAction(tileId, actionId).catch(() => {});
    return;
  }

  tileActionDialogState = { tileId, tile, action };
  setText('tile-action-title', `${tile.title} · ${action.label}`);
  setText('tile-action-confirmation', action.confirmationText || (action.method !== 'GET' ? 'Confirm this action.' : ''));

  const form = document.getElementById('tile-action-form');
  if (form) {
    form.innerHTML = (action.fields || []).map((field) => `
      <div class="h23-tile-action-field">
        ${buildTileActionFieldInput(field)}
      </div>
    `).join('');
  }

  setText('tile-action-status', '');
  document.getElementById('tile-action-overlay')?.classList.add('active');
}

function closeTileActionOverlay() {
  tileActionDialogState = null;
  document.getElementById('tile-action-overlay')?.classList.remove('active');
}

function collectTileActionDialogInput() {
  const inputs = document.querySelectorAll('[data-tile-action-input]');
  const values = {};
  inputs.forEach((input) => {
    const key = input.dataset.tileActionInput;
    if (!key) return;
    if (input.type === 'checkbox') {
      values[key] = input.checked;
    } else if (input.type === 'number') {
      values[key] = input.value === '' ? '' : Number(input.value);
    } else {
      values[key] = input.value;
    }
  });
  return values;
}

async function runTileAction(tileId, actionId, input = {}) {
  const actionState = homeTileCustomState.get(tileId)?.payload?.actions?.find((entry) => entry.id === actionId);
  const statusEl = document.getElementById('tile-action-status');
  const submitBtn = document.getElementById('tile-action-submit');

  if (statusEl) statusEl.textContent = 'Running action...';
  if (submitBtn) submitBtn.disabled = true;

  try {
    const res = await fetch(`/home23/api/tiles/${encodeURIComponent(tileId)}/actions/${encodeURIComponent(actionId)}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ input }),
      signal: AbortSignal.timeout(15_000),
    });
    const data = await res.json();
    if (!res.ok || !data.ok) {
      throw new Error(data.error || `Action failed (${res.status})`);
    }

    if (data.data) {
      renderCustomTileData(tileId, data.data);
    } else {
      await loadCustomTileData(tileId);
    }

    if (statusEl) statusEl.textContent = actionState?.method !== 'GET' ? 'Action completed.' : '';
    closeTileActionOverlay();
    return data;
  } catch (err) {
    if (statusEl) statusEl.textContent = err.message;
    throw err;
  } finally {
    if (submitBtn) submitBtn.disabled = false;
  }
}

function setupTileActionHandlers() {
  document.getElementById('tile-action-cancel')?.addEventListener('click', closeTileActionOverlay);
  document.getElementById('tile-action-form')?.addEventListener('submit', async (event) => {
    event.preventDefault();
    if (!tileActionDialogState) return;
    try {
      const input = collectTileActionDialogInput();
      await runTileAction(tileActionDialogState.tileId, tileActionDialogState.action.id, input);
    } catch {
      /* status is rendered in overlay */
    }
  });
  document.getElementById('tile-action-submit')?.addEventListener('click', async () => {
    if (!tileActionDialogState) return;
    try {
      const input = collectTileActionDialogInput();
      await runTileAction(tileActionDialogState.tileId, tileActionDialogState.action.id, input);
    } catch {
      /* status is rendered in overlay */
    }
  });
}

async function loadHomeTiles() {
  const base = apiBase(primaryAgent);
  const goodLifeFleetPromise = loadGoodLifeFleet({ updateTiles: false }).catch(() => null);

  loadVibeTile(primaryAgent, {
    imageId: 'home-vibe-image',
    captionId: 'home-vibe-caption',
    galleryHrefId: 'home-vibe-gallery-link',
  }).catch(() => { /* best-effort */ });

  const stateUrl = `${base || ''}/api/state`;
  const [engineHealth, summary, feederData, thoughtData, dreamData, systemState, goodLifeData] = await Promise.all([
    fetchEngineHealth(primaryAgent).catch(() => null),
    apiFetch(`${base}/api/home/summary`, { timeoutMs: 4000 }).catch(() => null),
    apiFetch('/home23/feeder-status', { timeoutMs: 4000 }).catch(() => null),
    apiFetch(`${base}/api/thoughts?limit=120`, { timeoutMs: 5000 }).catch(() => null),
    apiFetch(`${base}/api/dreams?limit=20&lite=1`, { timeoutMs: 3000 }).catch(() => null),
    fetch(stateUrl, { signal: AbortSignal.timeout(8_000) })
      .then((res) => (res.ok ? res.json() : null))
      .catch(() => null),
    apiFetch(`${base}/api/good-life`, { timeoutMs: GOOD_LIFE_API_TIMEOUT_MS }).catch(() => null),
  ]);

  if (summary) {
    updateSystemTile(summary);
    seedPulseFromSummary(summary, engineHealth);
    renderPulse();
    updatePulseAgo();
  }

  if (systemState || summary) {
    updateOverviewMetrics(systemState || summary, summary);
  }

  if (systemState) {
    updatePulseFromState(systemState);
  }

  if (engineHealth || summary) {
    setEngineOnlineStatus(enginePulse.state);
    if (engineHealth) setText('sys-uptime', formatDurationMs(engineHealth.uptime));
  } else if (!enginePulse.lastEventTime && (!enginePulse.state || enginePulse.state === 'unknown')) {
    setEngineOfflineStatus();
  }

  if (feederData) updateFeederTile(feederData);
  if (goodLifeData) updateGoodLifeTile(goodLifeData);
  goodLifeFleetPromise.then((rows) => {
    const homeGoodLifeFromFleet = Array.isArray(rows)
      ? rows.find((row) => row?.scope === 'home')?.data
      : null;
    if (!goodLifeSurfaceState.has('home') && homeGoodLifeFromFleet) updateGoodLifeTile(homeGoodLifeFromFleet);
    if (rows) renderGoodLifeFleetSummary();
  });

  if (thoughtData) {
    const thoughts = thoughtData.thoughts || thoughtData.journal || thoughtData || [];
    _cachedThoughts = thoughts;
    updateThoughtsTile(thoughts);
    updateBrainLog(thoughts.slice(-20));
  }

  if (dreamData) {
    const dreams = dreamData.dreams || dreamData || [];
    _cachedDreams = dreams;
    updateDreamLog(dreams);
  }
}

function goodLifeCssClass(value) {
  return String(value || 'unknown').toLowerCase().replace(/[^a-z0-9_-]+/g, '-');
}

const GOOD_LIFE_USER_INTERVENTION_TYPES = new Set([
  'notify_jtr',
  'request_user_input',
  'manual',
  'manual_intervention',
  'user_action',
]);

function goodLifeNextRemediation(problem = {}) {
  if (problem.nextRemediation) return problem.nextRemediation;
  const plan = Array.isArray(problem.remediation) ? problem.remediation : [];
  const index = Math.max(0, Number(problem.stepIndex || 0));
  const step = plan[index] || null;
  if (!step) {
    return {
      index,
      total: plan.length,
      type: null,
      requiresUser: false,
      text: plan.length ? 'remediation plan exhausted' : 'no remediation plan recorded',
    };
  }
  const type = String(step.type || '').trim();
  return {
    index,
    total: plan.length,
    type,
    requiresUser: GOOD_LIFE_USER_INTERVENTION_TYPES.has(type),
    text: step.args?.text || step.args?.message || step.args?.name || step.args?.target || type,
    cooldownMin: step.cooldownMin ?? null,
  };
}

function goodLifeNeedsUser(problem = {}) {
  return problem.escalated === true || problem.intervention?.required === true || goodLifeNextRemediation(problem).requiresUser === true;
}

function setGoodLifeStatus(scope, operator) {
  const el = document.getElementById(goodLifeDomId('goodlife-status', scope));
  if (!el) return;
  const status = operator?.status || 'unknown';
  const label = status === 'current' && operator?.safeToInherit
    ? 'CURRENT'
    : status.toUpperCase();
  el.textContent = label;
  el.className = `h23-goodlife-status ${goodLifeCssClass(status)}`;
}

function renderGoodLifeProblems(operator, data) {
  const live = operator?.liveProblems || data?.liveProblems?.snapshot || {};
  const counts = live.counts || {};
  const openRows = Array.isArray(live.open) ? live.open : [];
  const chronicRows = Array.isArray(live.chronic) ? live.chronic : [];
  const rows = [...openRows, ...chronicRows].slice(0, 5);
  const stats = `
    <div class="h23-goodlife-problem-stats">
      <span><strong>${Number(counts.open || 0)}</strong> open</span>
      <span><strong>${Number(counts.chronic || 0)}</strong> chronic</span>
      <span><strong>${Number(counts.unverifiable || 0)}</strong> unverifiable</span>
      <span><strong>${Number(counts.interventionRequired || 0)}</strong> need you</span>
    </div>
  `;

  if (rows.length === 0) {
    return `${stats}<div class="h23-goodlife-empty">No open or chronic problems</div>`;
  }

  const scope = data?._scope || 'home';
  return `${stats}<div class="h23-goodlife-problem-list">
    ${rows.map((row) => `
      <button class="h23-goodlife-problem-row" type="button" onclick="openGoodLifeOperator('${escapeAttr(scope)}', '${escapeAttr(row.id)}')">
        <span class="h23-goodlife-problem-state ${goodLifeCssClass(row.state)}">${escapeHtml(row.state)}</span>
        <span class="h23-goodlife-problem-id">${escapeHtml(row.id)}</span>
        <span class="h23-goodlife-problem-claim">${escapeHtml(row.issue || row.claim)}</span>
        ${goodLifeNeedsUser(row) ? '<span class="h23-goodlife-needs-user">needs you</span>' : ''}
      </button>
      ${row.detail ? `<div class="h23-goodlife-problem-detail">${escapeHtml(row.detail)}</div>` : ''}
    `).join('')}
  </div>`;
}

function goodLifeHasClearRegistryProjectionMismatch(operator) {
  const counts = operator?.liveProblems?.counts || {};
  const brief = operator?.operatorBrief || {};
  const liveOpen = Number(counts.open || 0) + Number(counts.chronic || 0) + Number(counts.interventionRequired || 0);
  const headline = String(brief.headline || '');
  return liveOpen === 0
    && (operator?.status === 'conflicted' || /projection disagrees/i.test(headline))
    && /registry is clear/i.test(headline);
}

function renderGoodLifeActionCard(operator, state) {
  if (goodLifeHasClearRegistryProjectionMismatch(operator)) {
    const rows = [
      ['Intent', 'reconcile'],
      ['Outcome', 'Good Life projection catches up to the clear live registry'],
      ['Stop', 'next evaluation agrees with live-problem registry'],
      ['Checks', 'registry open 0, chronic 0, need you 0'],
    ];
    return rows.map(([label, value]) => `
      <div class="h23-goodlife-action-row">
        <span>${escapeHtml(label)}</span>
        <strong>${escapeHtml(value)}</strong>
      </div>
    `).join('');
  }

  const card = operator?.actionCard || state?.policy?.actionCard || null;
  if (!card) return '<div class="h23-goodlife-empty">No routed action card</div>';

  const risk = [
    card.riskTier != null ? `risk ${card.riskTier}` : null,
    card.reversible === true ? 'reversible' : null,
    card.evidenceRequired === true ? 'evidence required' : null,
  ].filter(Boolean).join(', ');
  const rows = [
    ['Intent', card.intent],
    ['Outcome', card.expectedOutcome],
    ['Stop', card.stopCondition],
    ['Checks', risk],
  ].filter(([, value]) => value);

  return rows.map(([label, value]) => `
    <div class="h23-goodlife-action-row">
      <span>${escapeHtml(label)}</span>
      <strong>${escapeHtml(value)}</strong>
    </div>
  `).join('');
}

function renderGoodLifeBrief(operator, scope = 'home') {
  const brief = operator?.operatorBrief || null;
  if (!brief) return '';
  const latest = brief.latestResolution;
  const target = brief.target || {};
  const latestLine = latest?.id
    ? `<div class="h23-goodlife-brief-receipt">
        <span>Last resolution</span>
        <strong>${escapeHtml(latest.id)}</strong>
        ${latest.verifier ? `<small>${escapeHtml(latest.verifier)}</small>` : ''}
      </div>`
    : '';
  const action = target?.tab
    ? `<button class="h23-goodlife-brief-action" type="button" onclick="openGoodLifeOperator('${escapeAttr(scope)}', '${escapeAttr(target.id || '')}', '${escapeAttr(target.tab || '')}')">${escapeHtml(target.label || 'Open Details')}</button>`
    : '';
  return `
    <div class="h23-goodlife-brief-panel ${goodLifeCssClass(brief.severity)}">
      <div class="h23-goodlife-brief-status">${escapeHtml(brief.status || 'Status')}</div>
      <div class="h23-goodlife-brief-main">
        <strong>${escapeHtml(brief.headline || '')}</strong>
        <span>${escapeHtml(brief.why || '')}</span>
      </div>
      <div class="h23-goodlife-brief-next">
        <label>Next</label>
        <span>${escapeHtml(brief.next || '')}</span>
      </div>
      ${latestLine}
      ${action}
    </div>
  `;
}

function renderGoodLifeDigestList(operator) {
  const digest = operator?.operatorDigest || null;
  if (!digest) return '';
  const rows = [
    ['Issue', digest.issue],
    ['Now', digest.currentWork],
    ['Fixed', digest.latestFix],
    ['You', digest.userAction],
  ].filter(([, value]) => value);
  return `<div class="h23-goodlife-digest-list">
    ${rows.map(([label, value]) => `
      <div class="h23-goodlife-digest-row">
        <label>${escapeHtml(label)}</label>
        <span>${escapeHtml(value)}</span>
      </div>
    `).join('')}
  </div>`;
}

function renderGoodLifeRings(operator, scope = 'home') {
  const rings = Array.isArray(operator?.operatorRings) ? operator.operatorRings : [];
  if (!rings.length) return '';
  return `<div class="h23-goodlife-rings" aria-label="Good Life three-ring status">
    ${rings.map((ring) => {
    const action = ring.action || {};
    const tab = action.tab || 'issues';
    const id = action.id || '';
    const label = action.label || 'Open';
    return `<button class="h23-goodlife-ring ${goodLifeCssClass(ring.state || ring.label || 'unknown')}" type="button" onclick="openGoodLifeOperator('${escapeAttr(scope)}', '${escapeAttr(id)}', '${escapeAttr(tab)}')">
        <span>${escapeHtml(ring.name || '')}</span>
        <strong>${escapeHtml(ring.label || ring.state || 'unknown')}</strong>
        <small>${escapeHtml(ring.detail || '')}</small>
        <em>${escapeHtml(label)}</em>
      </button>`;
  }).join('')}
  </div>`;
}

function updateGoodLifeTile(data, scope = 'home') {
  const id = (base) => goodLifeDomId(base, scope);
  const state = data?.state || null;
  if (data) {
    data._scope = scope;
    goodLifeSurfaceState.set(scope, data);
    const agent = goodLifeAgentForScope(scope);
    if (agent) {
      goodLifeFleetState.set(scope, { agent, scope, data });
      renderGoodLifeFleetSummary();
    }
  }
  if (!state) {
    setText(id('goodlife-policy'), 'No Good Life state yet');
    setText(id('goodlife-summary'), '');
    setHtml(id('goodlife-brief'), '');
    setHtml(id('goodlife-answer'), '');
    setHtml(id('goodlife-problems'), '');
    setHtml(id('goodlife-action'), '');
    setHtml(id('goodlife-lanes'), '');
    setText(id('goodlife-meta'), '');
    setGoodLifeStatus(scope, { status: 'unknown', safeToInherit: false });
    return;
  }
  const operator = data?.operator || null;
  const policy = operator?.policy?.mode || state.policy?.mode || 'observe';
  const projectionMismatch = goodLifeHasClearRegistryProjectionMismatch(operator);
  setText(id('goodlife-policy'), projectionMismatch ? 'RECONCILE' : policy.toUpperCase());
  setText(
    id('goodlife-summary'),
    projectionMismatch
      ? `projection stale - ${operator?.operatorBrief?.headline || 'live registry is clear'}`
      : operator?.summary || state.summary || state.policy?.reason || ''
  );
  setGoodLifeStatus(scope, operator || { status: 'current', safeToInherit: true });
  setHtml(id('goodlife-brief'), renderGoodLifeBrief(operator, scope));

  const answerLines = operator?.operatorAnswer?.length
    ? operator.operatorAnswer
    : [state.summary || state.policy?.reason || ''];
  const digestHtml = renderGoodLifeDigestList(operator);
  const ringHtml = renderGoodLifeRings(operator, scope);
  setHtml(id('goodlife-answer'), (ringHtml || digestHtml)
    ? `${ringHtml}${digestHtml}`
    : answerLines.filter(Boolean).slice(0, 6).map((line) => (
    `<div class="h23-goodlife-answer-line">${escapeHtml(line)}</div>`
  )).join(''));
  setHtml(id('goodlife-problems'), renderGoodLifeProblems(operator, data));
  setHtml(id('goodlife-action'), renderGoodLifeActionCard(operator, state));

  const lanes = projectionMismatch
    ? [
        {
          name: 'live registry',
          status: 'clear',
          reasons: ['0 open live problems, 0 chronic, 0 need user intervention'],
          active: true,
        },
        {
          name: 'projection',
          status: 'stale',
          reasons: [operator?.operatorBrief?.why || 'Good Life state is older than the verifier receipts'],
          active: true,
        },
      ]
    : operator?.lanes || Object.entries(state.lanes || {}).map(([name, lane]) => ({
    name,
    status: lane?.status || 'unknown',
    reasons: lane?.reasons || [],
    active: false,
  }));
  const laneHtml = lanes.map((lane) => {
    const name = lane.name;
    const status = lane?.status || 'unknown';
    const title = Array.isArray(lane.reasons) && lane.reasons.length > 0 ? lane.reasons.join(' - ') : status;
    const active = lane.active ? ' active' : '';
    return `<span class="h23-goodlife-lane ${goodLifeCssClass(status)}${active}" title="${escapeHtml(title)}">${escapeHtml(name)} - ${escapeHtml(status)}</span>`;
  }).join('');
  setHtml(id('goodlife-lanes'), laneHtml);
  const latest = operator?.latestRegulatorAction;
  const workCounts = operator?.detail?.work?.obligations?.counts || {};
  const work = operator?.work || operator?.detail?.work?.summary || {};
  const activeWork = Number(workCounts.activeAgenda || 0) + Number(workCounts.activeGoals || 0);
  const latestActive = latest?.agendaId && goodLifeAgendaStatusIsActive(latest.agendaStatus);
  const latestStatus = latestActive && latest?.agendaStatus ? ` (${latest.agendaStatus})` : '';
  const action = latestActive
    ? `active work ${activeWork}; latest routed ${latest.agendaId}${latestStatus}`
    : `active work ${activeWork}; ${work.statusText || 'no active routed work'}`;
  const evaluatedAt = operator?.freshness?.evaluatedAt || state.evaluatedAt;
  const freshness = evaluatedAt ? `evaluated ${timeSince(new Date(evaluatedAt))}` : 'freshness unknown';
  setText(id('goodlife-meta'), `${freshness} - ${action}`);
}

function goodLifeAgentForScope(scope = 'home') {
  if (scope && scope.startsWith('agent-')) {
    const name = scope.slice('agent-'.length);
    return agents.find((agent) => agent.name === name) || null;
  }
  return primaryAgent;
}

function goodLifeLabelForScope(scope = 'home') {
  const agent = goodLifeAgentForScope(scope);
  return agent?.displayName || agent?.name || currentAgentLabel('This agent');
}

function goodLifeBaseForScope(scope = 'home') {
  const agent = goodLifeAgentForScope(scope);
  return agent ? apiBase(agent) : '';
}

function goodLifeOwnerAgentForScope(scope = 'home') {
  return goodLifeAgentForScope(scope)?.name || primaryAgent?.name || undefined;
}

async function loadGoodLifeForScope(scope = 'home') {
  const base = goodLifeBaseForScope(scope);
  const data = await apiFetch(`${base}/api/good-life`, { timeoutMs: GOOD_LIFE_API_TIMEOUT_MS }).catch(() => null);
  if (data) updateGoodLifeTile(data, scope);
  return data;
}

async function loadGoodLifeFleet({ updateTiles = true } = {}) {
  const configuredAgents = Array.isArray(agents) && agents.length
    ? agents
    : [primaryAgent].filter(Boolean);
  const rows = await Promise.all(configuredAgents.filter(Boolean).map(async (agent) => {
    const scope = agent.name === primaryAgent?.name ? 'home' : `agent-${agent.name}`;
    const base = apiBase(agent);
    const data = await apiFetch(`${base}/api/good-life`, { timeoutMs: GOOD_LIFE_API_TIMEOUT_MS }).catch(() => null);
    const row = { agent, scope, data };
    goodLifeFleetState.set(scope, row);
    if (data && updateTiles) updateGoodLifeTile(data, scope);
    return row;
  }));
  renderGoodLifeFleetSummary();
  return rows;
}

function goodLifeFleetAgentName(row = {}) {
  return row.agent?.displayName || row.agent?.name || goodLifeLabelForScope(row.scope || 'home');
}

function goodLifeFleetRank(row = {}) {
  const operator = row.data?.operator || {};
  const brief = operator.operatorBrief || {};
  const work = operator.work || operator.detail?.work?.summary || {};
  const counts = operator.liveProblems?.counts || {};
  if (!row.data) return 10;
  if (brief.needsUser || Number(counts.interventionRequired || 0) > 0 || work.status === 'needs-user') return 0;
  if (['critical', 'needs-user'].includes(brief.severity)) return 1;
  if (brief.severity === 'repairing' || Number(counts.open || 0) + Number(counts.chronic || 0) > 0) return 2;
  if (brief.severity === 'attention' || operator.status === 'conflicted' || operator.status === 'stale') return 3;
  if (work.status === 'review' || Number(work.agendaNeedingReview || 0) + Number(work.goalsNeedingReview || 0) > 0) return 4;
  if (brief.status === 'Resting') return 5;
  if (brief.severity === 'working' || work.status === 'working' || Number(work.activeTotal || 0) > 0) return 5;
  return 6;
}

function goodLifeFleetStatus(row = {}) {
  const operator = row.data?.operator || {};
  const brief = operator.operatorBrief || {};
  const work = operator.work || operator.detail?.work?.summary || {};
  const counts = operator.liveProblems?.counts || {};
  if (!row.data) {
    return { state: 'unknown', label: 'Unknown', text: 'Good Life API unavailable' };
  }
  if (brief.needsUser || Number(counts.interventionRequired || 0) > 0 || work.status === 'needs-user') {
    return { state: 'needs-user', label: 'Needs jtr', text: brief.next || work.statusText || 'manual decision required' };
  }
  if (brief.severity === 'repairing' || Number(counts.open || 0) + Number(counts.chronic || 0) > 0) {
    return { state: 'repairing', label: 'Repairing', text: brief.next || 'autonomous repair is running' };
  }
  if (brief.severity === 'attention' || operator.status === 'conflicted' || operator.status === 'stale') {
    return { state: 'attention', label: 'Attention', text: brief.next || brief.why || 'operator warning present' };
  }
  if (work.status === 'review' || Number(work.agendaNeedingReview || 0) + Number(work.goalsNeedingReview || 0) > 0) {
    return { state: 'review', label: 'Review', text: work.statusText || brief.next || 'operator review recommended' };
  }
  if (brief.status === 'Resting') {
    return { state: 'resting', label: 'Resting', text: brief.next || brief.why || 'sleep/wake lowering pressure' };
  }
  if (brief.severity === 'working' || work.status === 'working' || Number(work.activeTotal || 0) > 0) {
    return { state: 'working', label: 'Working', text: work.statusText || brief.next || 'autonomous work active' };
  }
  if (brief.status === 'Paused') {
    return { state: 'paused', label: 'Paused', text: goodLifePausedFleetText(operator) };
  }
  return { state: 'clear', label: 'Clear', text: brief.next || 'no user intervention needed' };
}

function goodLifePausedFleetText(operator = {}) {
  const brief = operator.operatorBrief || {};
  const activeLane = (operator.lanes || []).find((lane) => lane.active && String(lane.status || '').toLowerCase() !== 'healthy');
  const activeReason = Array.isArray(activeLane?.reasons) && activeLane.reasons.length
    ? activeLane.reasons[0]
    : null;
  const mode = operator.policy?.mode ? `${operator.policy.mode} requested` : null;
  const reset = operator.autonomyBudget?.resetText || '';
  const parts = [
    mode,
    activeReason,
    reset ? `budget ${reset}` : null,
  ].filter(Boolean);
  return parts.length ? parts.join(' - ') : (brief.next || brief.why || 'self-maintenance paused by budget');
}

function goodLifeFleetTarget(row = {}) {
  const operator = row.data?.operator || {};
  const target = operator.operatorBrief?.target || {};
  if (target.tab) {
    return {
      tab: target.tab,
      id: target.id || '',
    };
  }
  const status = goodLifeFleetStatus(row);
  if (status.state === 'working' || status.state === 'review') {
    return { tab: 'work', id: '' };
  }
  if (status.state === 'clear') {
    return { tab: 'resolutions', id: '' };
  }
  return { tab: 'issues', id: '' };
}

function renderGoodLifeFleetSummary() {
  const el = document.getElementById('goodlife-fleet-summary');
  if (!el) return;
  const rows = [...goodLifeFleetState.values()]
    .sort((a, b) => {
      const rank = goodLifeFleetRank(a) - goodLifeFleetRank(b);
      if (rank !== 0) return rank;
      return goodLifeFleetAgentName(a).localeCompare(goodLifeFleetAgentName(b));
    });
  if (!rows.length) {
    el.innerHTML = '<div class="h23-goodlife-fleet-empty">Loading agent fleet status...</div>';
    return;
  }
  const statuses = rows.map((row) => ({ row, status: goodLifeFleetStatus(row) }));
  const needsUser = statuses.filter((item) => item.status.state === 'needs-user');
  const repairing = statuses.filter((item) => item.status.state === 'repairing');
  const review = statuses.filter((item) => item.status.state === 'review');
  const resting = statuses.filter((item) => item.status.state === 'resting');
  const working = statuses.filter((item) => item.status.state === 'working');
  const paused = statuses.filter((item) => item.status.state === 'paused');
  const unknown = statuses.filter((item) => item.status.state === 'unknown');
  const headline = needsUser.length
    ? `${needsUser.length} agent${needsUser.length === 1 ? '' : 's'} need jtr`
    : repairing.length
      ? `${repairing.length} agent${repairing.length === 1 ? '' : 's'} repairing`
      : review.length
        ? `${review.length} agent${review.length === 1 ? '' : 's'} need review`
        : resting.length
          ? `${resting.length} agent${resting.length === 1 ? '' : 's'} resting autonomously`
          : working.length
          ? `${working.length} agent${working.length === 1 ? '' : 's'} working autonomously`
          : paused.length
            ? `${paused.length} agent${paused.length === 1 ? '' : 's'} paused by budget`
            : unknown.length
              ? `${unknown.length} agent${unknown.length === 1 ? '' : 's'} status unknown`
              : 'All agents clear or monitoring';
  el.innerHTML = `
    <div class="h23-goodlife-fleet-head">
      <span>Fleet</span>
      <strong>${escapeHtml(headline)}</strong>
    </div>
    <div class="h23-goodlife-fleet-list">
      ${statuses.map(({ row, status }) => {
    const target = goodLifeFleetTarget(row);
    return `<button class="h23-goodlife-fleet-row ${goodLifeCssClass(status.state)}" type="button" onclick="openGoodLifeOperator('${escapeAttr(row.scope || 'home')}', '${escapeAttr(target.id)}', '${escapeAttr(target.tab)}')">
        <span>${escapeHtml(goodLifeFleetAgentName(row))}</span>
        <strong>${escapeHtml(status.label)}</strong>
        <small>${escapeHtml(status.text)}</small>
      </button>`;
  }).join('')}
    </div>
  `;
}

function goodLifeProblemsFor(data, states) {
  const wanted = new Set(states);
  return (data?.liveProblems?.problems || [])
    .filter((problem) => wanted.has(problem.state))
    .sort((a, b) => {
      const interventionRank = Number(goodLifeNeedsUser(b)) - Number(goodLifeNeedsUser(a));
      if (interventionRank !== 0) return interventionRank;
      const rank = { chronic: 0, open: 1, unverifiable: 2, resolved: 3 };
      const stateRank = (rank[a.state] ?? 9) - (rank[b.state] ?? 9);
      if (stateRank !== 0) return stateRank;
      return Date.parse(b.updatedAt || b.lastCheckedAt || b.resolvedAt || b.openedAt || 0)
        - Date.parse(a.updatedAt || a.lastCheckedAt || a.resolvedAt || a.openedAt || 0);
    });
}

function goodLifeCountsText(counts = {}) {
  return `${Number(counts.open || 0)} open / ${Number(counts.chronic || 0)} chronic / ${Number(counts.unverifiable || 0)} unverifiable`;
}

function goodLifeAgendaStatusIsActive(status) {
  return ['candidate', 'surfaced', 'acknowledged'].includes(String(status || '').toLowerCase());
}

function goodLifeLatestRoutedText(latest = {}, activeWork = 0) {
  if (latest?.at && goodLifeAgendaStatusIsActive(latest.agendaStatus)) {
    const status = latest.agendaStatus ? ` - ${latest.agendaStatus}` : '';
    return `latest routed ${timeSince(new Date(latest.at))}${status}`;
  }
  return activeWork > 0 ? 'active routed work needs review' : 'no active routed work';
}

function renderGoodLifeJson(value) {
  if (!value) return '<span class="h23-goodlife-empty">None</span>';
  return `<pre class="h23-goodlife-json">${escapeHtml(JSON.stringify(value, null, 2))}</pre>`;
}

function renderGoodLifeProblemList(problems) {
  if (!problems.length) return '<div class="h23-goodlife-empty h23-goodlife-pad">Nothing in this lane</div>';
  const selectedId = goodLifeOverlayState.selectedProblemId;
  return problems.map((problem) => {
    const selected = problem.id === selectedId ? ' selected' : '';
    const age = problem.state === 'resolved'
      ? (problem.resolvedAt ? `resolved ${timeSince(new Date(problem.resolvedAt))}` : 'resolved')
      : (problem.openedAt ? `${Math.max(0, Math.round((Date.now() - Date.parse(problem.openedAt)) / 60000))}m` : '');
    const last = problem.lastResult?.detail || problem.fixRecipe?.summary || '';
    return `<button class="h23-goodlife-list-row${selected}" type="button" onclick="selectGoodLifeProblem('${escapeAttr(problem.id)}')">
      <span class="h23-goodlife-problem-state ${goodLifeCssClass(problem.state)}">${escapeHtml(problem.state || 'unknown')}</span>
      <strong>${escapeHtml(problem.id || '')}</strong>
      <span>${escapeHtml(problem.claim || '')}</span>
      <small>${escapeHtml([age, last].filter(Boolean).join(' - '))}</small>
    </button>`;
  }).join('');
}

function renderGoodLifeWorkList(data) {
  const actions = data?.operator?.detail?.work?.dailyActions || [];
  const obligations = data?.operator?.detail?.work?.obligations || {};
  const agenda = obligations.activeAgenda || [];
  const goals = obligations.activeGoals || [];
  const rows = [
    ...agenda.map((item) => ({
      badge: item.status || 'agenda',
      title: item.id || 'agenda',
      text: item.content || '',
      age: [
        item.ageMin != null ? `${item.ageMin}m` : '',
        effectiveGoodLifeAgendaWorkerRoute(item)?.worker ? `worker ${effectiveGoodLifeAgendaWorkerRoute(item).worker}` : '',
      ].filter(Boolean).join(' - '),
    })),
    ...goals.map((goal) => ({
      badge: goal.review?.recommended ? 'review' : (goal.status || 'goal'),
      title: goal.id || 'goal',
      text: goal.description || '',
      age: [
        goal.ageMin != null ? `${goal.ageMin}m` : '',
        goal.review?.recommended ? 'review' : '',
        goal.artifactStatus || '',
      ].filter(Boolean).join(' - '),
    })),
  ];
  if (!rows.length && !actions.length) return '<div class="h23-goodlife-empty h23-goodlife-pad">No routed work or active obligations</div>';
  const obligationHtml = rows.map((row) => `<div class="h23-goodlife-list-row static">
    <span class="h23-goodlife-problem-state ${goodLifeCssClass(row.badge)}">${escapeHtml(row.badge)}</span>
    <strong>${escapeHtml(row.title)}</strong>
    <span>${escapeHtml(row.text)}</span>
    <small>${escapeHtml(row.age)}</small>
  </div>`).join('');
  const actionHtml = actions.slice(0, 6).map((action) => `<div class="h23-goodlife-list-row static">
    <span class="h23-goodlife-problem-state ${goodLifeCssClass(action.mode)}">${escapeHtml(action.mode || 'work')}</span>
    <strong>${escapeHtml(action.agendaId || 'agenda')}</strong>
    <span>${escapeHtml(action.category || action.summary || '')}</span>
    <small>${action.at ? escapeHtml(timeSince(new Date(action.at))) : ''}</small>
  </div>`).join('');
  return obligationHtml + actionHtml;
}

function renderGoodLifeInsightsList(data) {
  const lanes = data?.operator?.lanes || [];
  return lanes.map((lane) => `<div class="h23-goodlife-list-row static">
    <span class="h23-goodlife-problem-state ${goodLifeCssClass(lane.status)}">${escapeHtml(lane.status)}</span>
    <strong>${escapeHtml(lane.name)}</strong>
    <span>${escapeHtml((lane.reasons || []).join(' - ') || lane.title || '')}</span>
    <small>${lane.active ? 'active' : ''}</small>
  </div>`).join('') || '<div class="h23-goodlife-empty h23-goodlife-pad">No lane evidence</div>';
}

function renderGoodLifeTop(data) {
  const operator = data?.operator || {};
  const counts = operator.liveProblems?.counts || {};
  const freshness = operator.freshness || {};
  const latest = operator.latestRegulatorAction || {};
  const workCounts = operator.detail?.work?.obligations?.counts || {};
  const work = operator.work || operator.detail?.work?.summary || {};
  const activeWork = Number(workCounts.activeAgenda || 0) + Number(workCounts.activeGoals || 0);
  const warnings = operator.consistency?.warnings || [];
  const digest = operator.operatorDigest || null;
  const handoff = operator.operatorHandoff || null;
  const answerLines = (operator.operatorAnswer || [])
    .filter(Boolean)
    .slice(0, 5);
  return `
    ${renderGoodLifeBrief(operator, data?._scope || goodLifeOverlayState.scope || 'home')}
    ${renderGoodLifeRings(operator, data?._scope || goodLifeOverlayState.scope || 'home')}
    ${handoff ? `<div class="h23-goodlife-handoff-panel ${goodLifeCssClass(handoff.needsUser ? 'needs-user' : operator.operatorBrief?.severity || operator.status)}">
      <div>
        <label>Situation</label>
        <p>${escapeHtml(handoff.situation || '')}</p>
      </div>
      <div>
        <label>Repair</label>
        <p>${escapeHtml(handoff.repair || '')}</p>
      </div>
      <div>
        <label>User Action</label>
        <p>${escapeHtml(handoff.userAction || '')}</p>
      </div>
      <div>
        <label>Evidence</label>
        ${(handoff.evidence || []).slice(0, 5).map((item) => `<p><strong>${escapeHtml(item.label || '')}:</strong> ${escapeHtml([item.value, item.detail].filter(Boolean).join(' - '))}</p>`).join('') || '<p>No evidence recorded</p>'}
      </div>
    </div>` : ''}
    ${digest ? `<div class="h23-goodlife-digest-grid">
      <div class="h23-goodlife-digest-card"><label>Issue</label><span>${escapeHtml(digest.issue || '')}</span></div>
      <div class="h23-goodlife-digest-card"><label>Now</label><span>${escapeHtml(digest.currentWork || '')}</span></div>
      <div class="h23-goodlife-digest-card"><label>Fixed</label><span>${escapeHtml(digest.latestFix || '')}</span></div>
      <div class="h23-goodlife-digest-card"><label>You</label><span>${escapeHtml(digest.userAction || '')}</span></div>
    </div>` : ''}
    <div class="h23-goodlife-top-grid">
      <div class="h23-goodlife-top-card">
        <label>Mode</label>
        <strong>${escapeHtml((operator.policy?.mode || 'unknown').toUpperCase())}</strong>
        <span>${escapeHtml(operator.policy?.reason || operator.summary || '')}</span>
      </div>
      <div class="h23-goodlife-top-card">
        <label>Issues</label>
        <strong>${escapeHtml(goodLifeCountsText(counts))}</strong>
        <span>${Number(counts.interventionRequired || 0) > 0 ? `${Number(counts.interventionRequired || 0)} need user intervention` : escapeHtml(warnings[0]?.message || 'projection current')}</span>
      </div>
      <div class="h23-goodlife-top-card">
        <label>Freshness</label>
        <strong>${escapeHtml(freshness.status || 'unknown')}</strong>
        <span>${freshness.evaluatedAt ? `evaluated ${escapeHtml(timeSince(new Date(freshness.evaluatedAt)))}` : 'no evaluation timestamp'}</span>
      </div>
      <div class="h23-goodlife-top-card">
        <label>Active Work</label>
        <strong>${activeWork}</strong>
        <span>${escapeHtml(latest?.at && goodLifeAgendaStatusIsActive(latest.agendaStatus)
          ? goodLifeLatestRoutedText(latest, activeWork)
          : (work.statusText || goodLifeLatestRoutedText(latest, activeWork)))}</span>
      </div>
    </div>
    ${answerLines.length ? `<div class="h23-goodlife-operator-answer-panel">
      <label>Operator Readout</label>
      ${answerLines.map((line) => `<div>${escapeHtml(line)}</div>`).join('')}
    </div>` : ''}
  `;
}

function renderGoodLifeTabs(data) {
  const detail = data?.operator?.detail || {};
  const counts = data?.operator?.liveProblems?.counts || {};
  const workCounts = detail.work?.obligations?.counts || {};
  const workTotal = Number(workCounts.activeAgenda || 0)
    + Number(workCounts.activeGoals || 0);
  const tabs = [
    ['issues', `Issues ${Number(counts.open || 0) + Number(counts.chronic || 0) + Number(counts.unverifiable || 0)}`],
    ['work', `Work ${workTotal}`],
    ['resolutions', `Resolutions ${detail.resolutions?.totalResolved || counts.resolved || 0}`],
    ['insights', 'Insights'],
  ];
  return tabs.map(([tab, label]) => `<button class="h23-goodlife-tab ${goodLifeOverlayState.tab === tab ? 'active' : ''}" type="button" onclick="switchGoodLifeTab('${tab}')">${escapeHtml(label)}</button>`).join('');
}

function renderGoodLifeIssueEmptyDetail(data) {
  const operator = data?.operator || {};
  const counts = operator.liveProblems?.counts || {};
  const detail = operator.detail || {};
  const workCounts = detail.work?.obligations?.counts || {};
  const activeWork = Number(workCounts.activeAgenda || 0) + Number(workCounts.activeGoals || 0);
  const resolved = Number(detail.resolutions?.totalResolved || counts.resolved || 0);
  const lanes = (operator.lanes || []).filter((lane) => lane.active || lane.status !== 'healthy').slice(0, 4);
  return `
    <div class="h23-goodlife-clear-state">
      <span class="h23-goodlife-problem-state healthy">clear</span>
      <div>
        <h3>No active Good Life issues</h3>
        <p>Live-problem registry is clear. Home23 does not need user intervention for this agent right now.</p>
      </div>
    </div>
    <div class="h23-goodlife-detail-grid">
      <div><label>Open Problems</label><p>${Number(counts.open || 0)} open / ${Number(counts.chronic || 0)} chronic</p><small>${Number(counts.unverifiable || 0)} unverifiable</small></div>
      <div><label>User Intervention</label><p>${Number(counts.interventionRequired || 0)} needed</p><small>${Number(counts.interventionRequired || 0) > 0 ? 'review the highlighted issue rows' : 'autonomous remediation is not blocked'}</small></div>
      <div><label>Recent Resolutions</label><p>${resolved}</p><small>open the Resolutions tab for evidence receipts and fix recipes</small></div>
      <div><label>Active Work</label><p>${activeWork}</p><small>open the Work tab for routed agenda and goal obligations</small></div>
    </div>
    <section><h4>Watched Lanes</h4>${lanes.length ? lanes.map((lane) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(lane.name)}</strong><span>${escapeHtml((lane.reasons || []).join(' - ') || lane.status || '')}</span><small>${escapeHtml(lane.status || '')}</small></div>`).join('') : '<div class="h23-goodlife-empty">All lanes currently report healthy or non-blocking status.</div>'}</section>
  `;
}

function renderGoodLifeIssueDetail(problem, data) {
  if (!problem) {
    return renderGoodLifeIssueEmptyDetail(data);
  }
  const last = problem.lastResult || {};
  const attempts = (problem.remediationLog || []).slice().reverse();
  const recipes = (problem.fixRecipeHistory || (problem.fixRecipe ? [problem.fixRecipe] : [])).slice().reverse();
  const next = goodLifeNextRemediation(problem);
  const needsUser = goodLifeNeedsUser(problem);
  const repairText = problemRepairText(problem);
  const userText = problemUserText(problem);
  const readiness = data?.operator?.interventionReadiness || {};
  return `
    <div class="h23-goodlife-detail-head">
      <span class="h23-goodlife-problem-state ${goodLifeCssClass(problem.state)}">${escapeHtml(problem.state)}</span>
      <strong>${escapeHtml(problem.id)}</strong>
      ${needsUser ? '<span class="h23-goodlife-needs-user">needs you</span>' : ''}
    </div>
    <h3>${escapeHtml(problem.claim || '')}</h3>
    <div class="h23-goodlife-issue-brief ${needsUser ? 'needs-user' : 'repairing'}">
      <div>
        <label>What is wrong</label>
        <p>${escapeHtml(last.detail || problem.detail || 'The verifier has not produced detail yet.')}</p>
      </div>
      <div>
        <label>What is happening</label>
        <p>${escapeHtml(repairText)}</p>
      </div>
      <div>
        <label>Needed from jtr</label>
        <p>${escapeHtml(userText)}</p>
      </div>
      <div>
        <label>Stop condition</label>
        <p>${escapeHtml(problem.state === 'resolved' ? 'verifier is passing' : 'verifier passes or remediation escalates')}</p>
      </div>
    </div>
    <div class="h23-goodlife-detail-actions">
      ${needsUser ? `<button class="h23-goodlife-plain-btn" type="button" onclick="recordGoodLifeUserIntervention('${escapeAttr(problem.id)}')">Mark Handled + Re-check</button>` : ''}
      <button class="h23-goodlife-plain-btn" type="button" onclick="testGoodLifeVerifier('${escapeAttr(problem.id)}')">Test Verifier</button>
      <button class="h23-goodlife-plain-btn" type="button" onclick="runGoodLifeWorkerCheck('${escapeAttr(problem.id)}')">Run Worker Check</button>
    </div>
    <div class="h23-goodlife-detail-grid">
      <div><label>Last verifier result</label><p>${escapeHtml(last.detail || 'not checked')}</p><small>${last.at ? escapeHtml(timeSince(new Date(last.at))) : ''}</small></div>
      <div><label>Lifecycle</label><p>${escapeHtml(problem.escalated ? 'escalated' : 'normal')} - step ${Number(problem.stepIndex || 0)} / ${(problem.remediation || []).length}</p><small>${problem.openedAt ? `opened ${escapeHtml(timeSince(new Date(problem.openedAt)))}` : ''}</small></div>
      <div><label>Next remediation</label><p>${escapeHtml(next.type || 'none')}</p><small>${escapeHtml(next.text || '')}</small></div>
      <div><label>User intervention</label><p>${needsUser ? 'needed' : 'not needed yet'}</p><small>${needsUser ? 'Home23 has reached a notify/manual step' : 'autonomous remediation can continue'}</small></div>
      <div><label>Decision gate</label><p>${readiness.identifiable ? 'identified enough to act' : 'verify before intervention'}</p><small>${escapeHtml(readiness.smallestRealAction || 'run verifier before changing state')}</small></div>
    </div>
    <section><h4>Known / Unknown</h4>${renderGoodLifeInterventionReadiness(readiness)}</section>
    ${renderGoodLifeIssueInterventionConsole(problem)}
    <section><h4>Verifier</h4>${renderGoodLifeJson(problem.verifier)}</section>
    <section><h4>Remediation Plan</h4>${renderGoodLifeJson(problem.remediation || [])}</section>
    <section><h4>Recent Attempts</h4>${attempts.length ? attempts.map((attempt) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(attempt.type || 'attempt')}</strong><span>${escapeHtml(attempt.outcome || '')}</span><small>${escapeHtml(attempt.detail || '')}</small></div>`).join('') : '<div class="h23-goodlife-empty">No attempts recorded</div>'}</section>
    <section><h4>Fix Recipes</h4>${recipes.length ? recipes.map((recipe) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(recipe.verifierStatus || recipe.dispatchOutcome || 'recipe')}</strong><span>${escapeHtml(recipe.summary || '')}</span><small>${recipe.at ? escapeHtml(timeSince(new Date(recipe.at))) : ''}</small></div>`).join('') : '<div class="h23-goodlife-empty">No fix recipe recorded</div>'}</section>
    ${renderGoodLifeIssueWorkerReceipt(problem)}
  `;
}

function renderGoodLifeInterventionReadiness(readiness) {
  if (!readiness?.schema) return '<div class="h23-goodlife-empty">No intervention-readiness receipt yet</div>';
  const known = (readiness.known || []).slice(0, 5).map((item) => `<li>${escapeHtml(item)}</li>`).join('');
  const unknown = (readiness.unknown || []).slice(0, 5).map((item) => `<li>${escapeHtml(item)}</li>`).join('');
  return `
    <div class="h23-goodlife-evidence-row ${readiness.identifiable ? 'healthy' : 'watch'}">
      <strong>${escapeHtml(readiness.decision?.kind || 'decision')}</strong>
      <span>${escapeHtml(readiness.decision?.subject || 'good-life-loop')}</span>
      <small>${escapeHtml(readiness.viewDiscipline || '')}</small>
    </div>
    <div class="h23-goodlife-two-col">
      <div><label>Known</label><ul>${known || '<li>none recorded</li>'}</ul></div>
      <div><label>Unknown</label><ul>${unknown || '<li>none blocking</li>'}</ul></div>
    </div>
  `;
}

function compactGoodLifeProblemForWorker(problem) {
  if (!problem) return {};
  return {
    id: problem.id,
    state: problem.state,
    claim: problem.claim,
    openedAt: problem.openedAt,
    updatedAt: problem.updatedAt,
    lastCheckedAt: problem.lastCheckedAt,
    lastResult: problem.lastResult,
    verifier: problem.verifier,
    remediation: problem.remediation,
    remediationLog: (problem.remediationLog || []).slice(-5),
    fixRecipe: problem.fixRecipe,
  };
}

function buildGoodLifeWorkerPrompt(problem) {
  const scopeLabel = goodLifeLabelForScope(goodLifeOverlayState.scope);
  const payload = compactGoodLifeProblemForWorker(problem);
  return [
    `Good Life operator intervention for ${scopeLabel}.`,
    '',
    'Inspect this live problem with current evidence. Diagnose the exact failing layer, re-run or dry-run the verifier when safe, and return a receipt with pass/fail evidence and the smallest concrete next action.',
    '',
    'Guardrails: do not use global PM2 stop/delete, do not discard local changes, do not restart unrelated services, and do not claim resolution without verifier evidence.',
    '',
    'Live problem payload:',
    JSON.stringify(payload, null, 2),
  ].join('\n');
}

function compactGoodLifeAgendaForWorker(item) {
  if (!item) return {};
  const route = effectiveGoodLifeAgendaWorkerRoute(item);
  return {
    id: item.id,
    status: item.status,
    content: item.content,
    sourceSignal: item.sourceSignal,
    topicTags: item.topicTags,
    createdAt: item.createdAt,
    updatedAt: item.updatedAt,
    ageMin: item.ageMin,
    temporalContext: item.temporalContext,
    workerRoute: route,
    originalWorkerRoute: item.workerRoute || null,
    review: item.review || null,
  };
}

function effectiveGoodLifeAgendaWorkerRoute(item) {
  return item?.workerRoute || item?.review?.suggestedWorker || null;
}

function buildGoodLifeAgendaWorkerPrompt(item) {
  const scopeLabel = goodLifeLabelForScope(goodLifeOverlayState.scope);
  const payload = compactGoodLifeAgendaForWorker(item);
  return [
    `Good Life routed work for ${scopeLabel}.`,
    '',
    'Inspect this Good Life agenda item with current evidence. Use the recommended or inferred worker lane as the starting point, verify the relevant files/endpoints when safe, and return a receipt with pass/fail/blocked evidence and the smallest concrete next action.',
    '',
    'Guardrails: do not use global PM2 stop/delete, do not discard local changes, do not restart unrelated services, and do not claim resolution without verifier evidence.',
    '',
    'Good Life agenda payload:',
    JSON.stringify(payload, null, 2),
  ].join('\n');
}

async function chooseGoodLifeWorker() {
  if (!workersState.workers.length) {
    const data = await workerApi('');
    workersState.workers = data.workers || [];
  }
  return workersState.workers.find((worker) => worker.name === 'systems')
    || workersState.workers.find((worker) => worker.name === 'freshness')
    || workersState.workers.find((worker) => worker.name === 'memory')
    || workersState.workers[0]
    || null;
}

function renderGoodLifeWorkDetail(data) {
  const operator = data?.operator || {};
  const card = operator.actionCard || {};
  const activeCommitments = operator.detail?.insights?.activeCommitments || [];
  const obligations = operator.detail?.work?.obligations || {};
  const agenda = obligations.activeAgenda || [];
  const goals = obligations.activeGoals || [];
  const reviewAgenda = agenda.filter((item) => item.review?.recommended);
  const reviewGoals = goals.filter((goal) => goal.id && goal.review?.recommended);
  const recentWorkerRuns = goodLifeWorkerRunsForScope(goodLifeOverlayState.scope);
  const agendaMeta = (item) => [
    item.status,
    item.ageMin != null ? `${item.ageMin}m` : null,
    effectiveGoodLifeAgendaWorkerRoute(item)?.worker ? `worker: ${effectiveGoodLifeAgendaWorkerRoute(item).worker}` : null,
  ].filter(Boolean).join(' - ');
  const agendaActions = (item) => `<div class="h23-goodlife-mini-actions">
    ${effectiveGoodLifeAgendaWorkerRoute(item)?.worker ? `<button type="button" onclick="runGoodLifeAgendaWorkerCheck('${escapeAttr(item.id || '')}')">Run ${escapeHtml(effectiveGoodLifeAgendaWorkerRoute(item).worker)}</button>` : ''}
    <button type="button" onclick="updateGoodLifeAgendaStatus('${escapeAttr(item.id || '')}', 'acknowledged')">Acknowledge</button>
    <button type="button" onclick="updateGoodLifeAgendaStatus('${escapeAttr(item.id || '')}', 'stale')">Dismiss</button>
  </div>`;
  const goalActions = (goal) => goal?.id ? `<div class="h23-goodlife-mini-actions">
    <button type="button" onclick="archiveGoodLifeGoal('${escapeAttr(goal.id || '')}')">Archive Goal</button>
  </div>` : '';
  const goalArtifact = (goal) => goal?.artifact?.relativePath
    ? `<p><strong>Artifact:</strong> ${escapeHtml(goal.artifact.exists ? 'ready' : 'pending')} - ${escapeHtml(goal.artifact.relativePath)}${goal.artifact.exists && goal.artifact.path ? ` <code>${escapeHtml(goal.artifact.path)}</code>` : ''}</p>`
    : '';
  const primaryAgenda = agenda[0] || null;
  const primaryManifest = primaryAgenda?.manifest || topGoal?.manifest || null;
  return `
    <h3>Current Work</h3>
    ${primaryAgenda ? `<div class="h23-goodlife-primary-action">
      <div>
        <label>Next Action</label>
        <strong>${escapeHtml(primaryAgenda.id || 'agenda')}</strong>
        <p>${escapeHtml(primaryAgenda.content || '')}</p>
        <small>${escapeHtml(agendaMeta(primaryAgenda))}</small>
      </div>
      ${agendaActions(primaryAgenda)}
    </div>` : ''}
    ${renderGoodLifeWorkManifest(primaryManifest)}
    <div class="h23-goodlife-detail-grid">
      <div><label>Intent</label><p>${escapeHtml(card.intent || operator.policy?.mode || 'unknown')}</p></div>
      <div><label>Stop Condition</label><p>${escapeHtml(card.stopCondition || 'not recorded')}</p></div>
      <div><label>Expected Outcome</label><p>${escapeHtml(card.expectedOutcome || 'not recorded')}</p></div>
      <div><label>Risk</label><p>${escapeHtml([card.riskTier != null ? `risk ${card.riskTier}` : null, card.reversible ? 'reversible' : null, card.evidenceRequired ? 'evidence required' : null].filter(Boolean).join(', ') || 'not recorded')}</p></div>
    </div>
    <section><h4>Active Agenda</h4>${reviewAgenda.length ? `<div class="h23-goodlife-section-actions"><button type="button" onclick="dismissGoodLifeAgendaReviewRows()">Dismiss ${reviewAgenda.length} review row${reviewAgenda.length === 1 ? '' : 's'}</button></div>` : ''}${agenda.length ? agenda.map((item) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(item.id || 'agenda')}</strong><span>${escapeHtml(item.content || '')}</span><small>${escapeHtml(agendaMeta(item))}</small>${item.review?.recommended ? `<p><strong>Review:</strong> ${escapeHtml(item.review.reason || 'operator review recommended')}${item.review.next ? ` - ${escapeHtml(item.review.next)}` : ''}</p>` : ''}${agendaActions(item)}</div>`).join('') : '<div class="h23-goodlife-empty">No active agenda rows</div>'}</section>
    <section><h4>Recent Worker Receipts</h4>${recentWorkerRuns.length ? recentWorkerRuns.map((run) => `<div class="h23-goodlife-evidence-row h23-goodlife-worker-run-row"><strong>${escapeHtml(run.worker || 'worker')}</strong><span>${escapeHtml(run.summary || run.runId || '')}</span><small>${escapeHtml([formatWorkerRunSource(run), formatWorkerTime(run.finishedAt || run.startedAt)].filter(Boolean).join(' - '))}</small><div class="h23-goodlife-mini-actions"><span class="h23-worker-status ${workerStatusClass(run.status)}">${escapeHtml(run.status || 'running')}</span><span class="h23-worker-status ${workerStatusClass(run.verifierStatus)}">${escapeHtml(run.verifierStatus || 'unknown')}</span><button type="button" onclick="openGoodLifeWorkerReceipt('${escapeAttr(run.runId || '')}')">Open Receipt</button></div></div>`).join('') : '<div class="h23-goodlife-empty">No Good Life worker receipts yet</div>'}${renderGoodLifeWorkerReceiptDetail()}</section>
    <section><h4>Active Goals</h4>${reviewGoals.length ? `<div class="h23-goodlife-section-actions"><button type="button" onclick="archiveGoodLifeGoalReviewRows()">Archive ${reviewGoals.length} review goal${reviewGoals.length === 1 ? '' : 's'}</button></div>` : ''}${goals.length ? goals.map((goal) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(goal.id || 'goal')}</strong><span>${escapeHtml(goal.description || '')}</span><small>${escapeHtml([goal.status, goal.source, goal.ageMin != null ? `${goal.ageMin}m` : null, goal.review?.recommended ? `review: ${goal.review.reason}` : null].filter(Boolean).join(' - '))}</small>${goalArtifact(goal)}${goal.review?.recommended ? `<p>${escapeHtml(goal.review.next || '')}</p>` : ''}${goalActions(goal)}</div>`).join('') : '<div class="h23-goodlife-empty">No active goals</div>'}</section>
    <section><h4>Active Commitments</h4>${activeCommitments.length ? activeCommitments.map((item) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(item.title || item.id)}</strong><span>${escapeHtml((item.reasons || []).join(' - ') || item.status || '')}</span><small>${escapeHtml(item.lane || '')}</small></div>`).join('') : '<div class="h23-goodlife-empty">No active commitments</div>'}</section>
  `;
}

function renderGoodLifeWorkManifest(manifest) {
  if (!manifest) return '';
  const artifact = manifest.artifact?.relativePath
    ? `${manifest.artifact.relativePath} - ${manifest.artifact.exists ? 'ready' : 'pending'}`
    : 'none required';
  return `
    <section><h4>Work Manifest</h4>
      <div class="h23-goodlife-detail-grid">
        <div><label>Allowed Transition</label><p>${escapeHtml(manifest.allowedTransition || 'not recorded')}</p></div>
        <div><label>Stop Line</label><p>${escapeHtml(manifest.stopLine || 'not recorded')}</p></div>
        <div><label>Source Surface</label><p>${escapeHtml(manifest.sourceSurface || 'not recorded')}</p></div>
        <div><label>Verifier</label><p>${escapeHtml(manifest.verifier || 'not recorded')}</p></div>
        <div><label>Receipt</label><p>${escapeHtml(manifest.receipt || 'not recorded')}</p></div>
        <div><label>Artifact</label><p>${escapeHtml(artifact)}</p><small>${escapeHtml(manifest.artifact?.path || '')}</small></div>
        <div><label>Forbidden Adjacent Work</label><p>${escapeHtml((manifest.forbiddenAdjacentMoves || []).join('; ') || 'not recorded')}</p></div>
        <div><label>Authority</label><p>${escapeHtml(manifest.authority || 'not recorded')}</p></div>
      </div>
    </section>
  `;
}

function renderGoodLifeResolutionDetail(problem) {
  if (!problem) return '<div class="h23-goodlife-empty h23-goodlife-pad">Select a resolution to inspect what closed.</div>';
  const evidence = problem.evidence || null;
  const attempts = (problem.remediationLog || []).slice().reverse();
  const recipes = (problem.fixRecipeHistory || (problem.fixRecipe ? [problem.fixRecipe] : [])).slice().reverse();
  return `
    <div class="h23-goodlife-detail-head">
      <span class="h23-goodlife-problem-state resolved">resolved</span>
      <strong>${escapeHtml(problem.id)}</strong>
    </div>
    <h3>${escapeHtml(problem.claim || '')}</h3>
    <div class="h23-goodlife-detail-grid">
      <div><label>Resolved</label><p>${problem.resolvedAt ? escapeHtml(timeSince(new Date(problem.resolvedAt))) : 'unknown'}</p></div>
      <div><label>Verifier</label><p>${escapeHtml(problem.lastResult?.detail || problem.fixRecipe?.verifierStatus || 'not recorded')}</p></div>
    </div>
    <section><h4>Evidence Receipt</h4>${evidence ? `
      <div class="h23-goodlife-evidence-receipt">
        <strong>${escapeHtml(evidence.receiptId || 'receipt')}</strong>
        <span>${escapeHtml([evidence.result, evidence.claimLevel].filter(Boolean).join(' - ') || 'recorded')}</span>
        ${evidence.receiptPath ? `<code>${escapeHtml(evidence.receiptPath)}</code>` : ''}
      </div>
    ` : '<div class="h23-goodlife-empty">No evidence receipt linked</div>'}</section>
    <section><h4>Remediation Plan</h4>${(problem.remediation || []).length ? renderGoodLifeJson(problem.remediation) : '<div class="h23-goodlife-empty">No remediation plan recorded</div>'}</section>
    <section><h4>Recent Attempts</h4>${attempts.length ? attempts.map((attempt) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(attempt.type || 'attempt')}</strong><span>${escapeHtml(attempt.outcome || '')}</span><small>${escapeHtml(attempt.detail || '')}</small></div>`).join('') : '<div class="h23-goodlife-empty">No attempts recorded</div>'}</section>
    <section><h4>Fix Recipes</h4>${recipes.length ? recipes.map((recipe) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(recipe.verifierStatus || recipe.dispatchOutcome || 'recipe')}</strong><span>${escapeHtml(recipe.summary || '')}</span><small>${recipe.at ? escapeHtml(timeSince(new Date(recipe.at))) : ''}</small></div>`).join('') : '<div class="h23-goodlife-empty">No fix recipe recorded; closure is based on verifier/evidence state.</div>'}</section>
  `;
}

function formatGoodLifeGb(bytes) {
  const value = Number(bytes);
  if (!Number.isFinite(value)) return null;
  return `${(value / 1024 / 1024 / 1024).toFixed(1)} GB`;
}

function compactGoodLifeHostDetail(value, max = 180) {
  const text = String(value || '').replace(/\s+/g, ' ').trim();
  if (text.length <= max) return text;
  return `${text.slice(0, max - 1)}...`;
}

function goodLifeHostPressureStatus(kind, host) {
  if (kind === 'cpu') {
    const loadRatio = Number(host?.cpu?.loadRatio);
    if (Number.isFinite(loadRatio) && loadRatio >= 1) return { className: 'watch', label: 'drives rest' };
    return { className: 'info', label: 'below rest threshold' };
  }
  if (kind === 'memory') {
    return { className: 'info', label: 'informational; swap drives pressure' };
  }
  if (kind === 'swap') {
    const usedPct = Number(host?.swap?.usedPct);
    if (Number.isFinite(usedPct) && usedPct >= 95) return { className: 'critical', label: 'drives repair' };
    if (Number.isFinite(usedPct) && usedPct >= 85) return { className: 'watch', label: 'drives rest' };
    return { className: 'info', label: 'below rest threshold' };
  }
  if (kind === 'disk') {
    const usagePct = Number(host?.disk?.usagePct);
    if (Number.isFinite(usagePct) && usagePct >= 95) return { className: 'critical', label: 'drives repair' };
    return { className: 'info', label: 'below repair threshold' };
  }
  if (kind === 'process') {
    const topCpuPct = Number(host?.process?.topCpuPct);
    if (Number.isFinite(topCpuPct) && topCpuPct >= 90) return { className: 'watch', label: 'spike; load/swap decide policy' };
    return { className: 'info', label: 'informational' };
  }
  return { className: 'info', label: 'observed' };
}

function renderGoodLifeHostPressure(host) {
  if (!host) return '';
  const rows = [
    host.cpu ? {
      label: 'CPU Load',
      value: host.cpu.loadRatio != null ? `${Math.round(Number(host.cpu.loadRatio) * 100)}% of cores` : null,
      detail: [
        host.cpu.load1 != null ? `load ${Number(host.cpu.load1).toFixed(2)}` : null,
        host.cpu.cpuCount != null ? `${host.cpu.cpuCount} cores` : null,
      ].filter(Boolean).join(' - '),
      status: goodLifeHostPressureStatus('cpu', host),
    } : null,
    host.memory ? {
      label: 'Memory',
      value: host.memory.freePct != null ? `${Number(host.memory.freePct).toFixed(1)}% raw free` : null,
      detail: [
        formatGoodLifeGb(host.memory.freeBytes),
        host.memory.totalBytes != null ? `of ${formatGoodLifeGb(host.memory.totalBytes)}` : null,
      ].filter(Boolean).join(' '),
      status: goodLifeHostPressureStatus('memory', host),
    } : null,
    host.swap ? {
      label: 'Swap',
      value: host.swap.usedPct != null ? `${Math.round(Number(host.swap.usedPct))}% used` : null,
      detail: [
        host.swap.usedMb != null ? `${Math.round(Number(host.swap.usedMb))} MB used` : null,
        host.swap.totalMb != null ? `of ${Math.round(Number(host.swap.totalMb))} MB` : null,
      ].filter(Boolean).join(' '),
      status: goodLifeHostPressureStatus('swap', host),
    } : null,
    host.disk ? {
      label: 'Disk',
      value: host.disk.usagePct != null ? `${Math.round(Number(host.disk.usagePct))}% used` : null,
      detail: host.disk.mount || '',
      status: goodLifeHostPressureStatus('disk', host),
    } : null,
    host.process ? {
      label: 'Top Process',
      value: host.process.topCpuPct != null ? `${Math.round(Number(host.process.topCpuPct))}% CPU` : null,
      detail: compactGoodLifeHostDetail(host.process.topProcess?.pm2Name || host.process.topProcess?.command || ''),
      status: goodLifeHostPressureStatus('process', host),
    } : null,
  ].filter((row) => row && (row.value || row.detail));

  if (!rows.length) return '';
  return `
    <section><h4>Host Pressure</h4>
      ${rows.map((row) => `<div class="h23-goodlife-evidence-row ${escapeHtml(row.status?.className || 'info')}">
        <strong>${escapeHtml(row.label)}</strong>
        <span>${escapeHtml(row.value || 'observed')}</span>
        <small>${escapeHtml(row.detail || '')}</small>
        <em>${escapeHtml(row.status?.label || 'observed')}</em>
      </div>`).join('')}
    </section>
  `;
}

function renderGoodLifePm2Changes(pm2) {
  if (!pm2) return '';
  const processRows = Array.isArray(pm2.processes) ? pm2.processes : [];
  const offlineRows = Array.isArray(pm2.offlineProcesses) ? pm2.offlineProcesses : [];
  if (processRows.length === 0 && offlineRows.length === 0 && pm2.currentTotal == null) return '';
  const invalid = Number(pm2.invalidRestartCounters || 0);
  const offline = Number(pm2.offline || 0);
  const currentTotal = Number.isFinite(Number(pm2.currentTotal)) ? Number(pm2.currentTotal) : null;
  const currentText = offline > 0
    ? `${offline} currently not online`
    : (currentTotal == null ? 'current PM2 status unavailable' : `${currentTotal} current Home23 processes online`);
  return `
    <section><h4>Runtime Changes</h4>
      <div class="h23-goodlife-evidence-row ${offline || invalid ? 'watch' : 'info'}">
        <strong>PM2 changes</strong>
        <span>${escapeHtml(`${Number(pm2.recentHome23Changes || 0)} Home23 process change${Number(pm2.recentHome23Changes || 0) === 1 ? '' : 's'}`)}</span>
        <small>${escapeHtml([currentText, invalid ? `${invalid} unknown restart counter${invalid === 1 ? '' : 's'}` : null].filter(Boolean).join(' - '))}</small>
        <em>${escapeHtml(offline ? 'repair signal' : (invalid ? 'verify counter' : 'observed'))}</em>
      </div>
      ${offlineRows.slice(0, 4).map((row) => `<div class="h23-goodlife-evidence-row critical">
        <strong>${escapeHtml(row.name || 'pm2 process')}</strong>
        <span>${escapeHtml(row.status || 'not online')}</span>
        <small>${escapeHtml(row.role || 'Home23 process')}</small>
        <em>current</em>
      </div>`).join('')}
      ${processRows.slice(0, 6).map((row) => `<div class="h23-goodlife-evidence-row">
        <strong>${escapeHtml(row.name || 'pm2 process')}</strong>
        <span>${escapeHtml(`${Number(row.changes || 0)} change${Number(row.changes || 0) === 1 ? '' : 's'}`)}</span>
        <small>${escapeHtml([
    row.lastChangeStatus ? `last change ${row.lastChangeStatus}` : null,
    row.role || null,
    row.lastRestartCount == null ? (row.rawRestartCount ? 'restart counter unknown' : null) : `restart ${row.lastRestartCount}`,
  ].filter(Boolean).join(' - '))}</small>
        <em>${escapeHtml(row.lastAt ? timeSince(new Date(row.lastAt)) : 'observed')}</em>
      </div>`).join('')}
    </section>
  `;
}

function renderGoodLifeScheduler(scheduler) {
  if (!scheduler) return '';
  const failing = Number(scheduler.failingJobs || 0);
  const enabled = Number(scheduler.enabledJobs || 0);
  const total = Number(scheduler.totalJobs || 0);
  const rowClass = failing > 0 ? 'watch' : '';
  return `
    <section><h4>Scheduler Sovereignty</h4>
      <div class="h23-goodlife-evidence-row ${rowClass}">
        <strong>${escapeHtml(failing > 0 ? `${failing} failing` : 'clear')}</strong>
        <span>${escapeHtml(`${enabled}/${total} enabled jobs; max error streak ${Number(scheduler.maxConsecutiveErrors || 0)}`)}</span>
        <small>${escapeHtml(scheduler.path || '')}</small>
        <em>${escapeHtml(failing > 0 ? 'repair signal' : 'observed')}</em>
      </div>
      ${(scheduler.worstJobs || []).slice(0, 5).map((job) => `<div class="h23-goodlife-evidence-row watch">
        <strong>${escapeHtml(job.name || job.id || 'scheduler job')}</strong>
        <span>${escapeHtml(`${Number(job.consecutiveErrors || 0)} consecutive error${Number(job.consecutiveErrors || 0) === 1 ? '' : 's'}`)}</span>
        <small>${escapeHtml([job.lastStatus || null, job.lastRunAt ? `last ${timeSince(new Date(job.lastRunAt))}` : null].filter(Boolean).join(' - '))}</small>
        <em>cron</em>
      </div>`).join('')}
    </section>
  `;
}

function renderGoodLifeInsightsDetail(data) {
  const detail = data?.operator?.detail || {};
  const metrics = detail.insights?.trendMetrics || {};
  const ledger = detail.insights?.ledgerTail || [];
  const restraintReceipts = detail.insights?.restraintReceipts || [];
  const provenance = data?.operator?.provenance || null;
  const correctionTombstones = provenance?.correctionTombstones || detail.insights?.correctionTombstones || [];
  const doctrineAdoption = provenance?.doctrineAdoption || detail.insights?.doctrineAdoption || null;
  const budget = detail.insights?.autonomyBudget || data?.operator?.autonomyBudget || null;
  const host = detail.insights?.host || data?.state?.evidence?.host || null;
  const pm2 = detail.insights?.pm2 || data?.state?.evidence?.pm2 || null;
  const scheduler = detail.insights?.scheduler || data?.state?.evidence?.scheduler || null;
  const provenanceHtml = provenance ? `
    <section><h4>Projection Provenance</h4>
      <div class="h23-goodlife-evidence-row">
        <strong>${escapeHtml(provenance.projection?.status || 'unknown')}</strong>
        <span>${escapeHtml(provenance.projection?.authority || '')}</span>
        <small>${escapeHtml(provenance.projection?.surface || '')}</small>
        <em>projection</em>
      </div>
      ${provenance.curriculumArc ? `<div class="h23-goodlife-evidence-row">
        <strong>From The Inside arc</strong>
        <span>${escapeHtml(`${Number(provenance.curriculumArc.issuesRead || 0)} issues mapped from #${String(provenance.curriculumArc.firstIssue || '').padStart(3, '0')} to #${String(provenance.curriculumArc.lastIssue || '').padStart(3, '0')}`)}</span>
        <small>${escapeHtml(provenance.curriculumArc.source || '')}</small>
        <em>doctrine</em>
      </div>` : ''}
      ${(provenance.evidence || []).slice(0, 5).map((row) => `<div class="h23-goodlife-evidence-row">
        <strong>${escapeHtml(row.surface || row.kind || 'evidence')}</strong>
        <span>${escapeHtml(row.authority || '')}</span>
        <small>${escapeHtml(row.counts ? Object.entries(row.counts).map(([key, value]) => `${key}:${value}`).join(' - ') : (row.entriesSampled != null ? `${row.entriesSampled} sampled` : ''))}</small>
        <em>${escapeHtml(row.kind || 'evidence')}</em>
      </div>`).join('')}
      ${(provenance.conflicts || []).slice(0, 4).map((row) => `<div class="h23-goodlife-evidence-row ${row.severity === 'critical' ? 'critical' : 'watch'}">
        <strong>${escapeHtml(row.code || 'conflict')}</strong>
        <span>${escapeHtml(row.message || '')}</span>
        <small>${escapeHtml((row.fields || []).join(', '))}</small>
        <em>${escapeHtml(row.severity || 'warning')}</em>
      </div>`).join('')}
      ${correctionTombstones.length ? `<h5>Correction Tombstones</h5>${correctionTombstones.slice(0, 5).map((row) => `<div class="h23-goodlife-evidence-row watch">
        <strong>${escapeHtml(row.subject || 'correction')}</strong>
        <span>${escapeHtml(row.correctedClaim || '')}</span>
        <small>${escapeHtml([row.oldClaim, row.correctingSurface].filter(Boolean).join(' - '))}</small>
        <em>${escapeHtml(row.actionPosture || 'do_not_inherit_old_projection')}</em>
      </div>`).join('')}` : ''}
      ${doctrineAdoption ? `<h5>Doctrine Adoption</h5>
        <div class="h23-goodlife-evidence-row">
          <strong>${escapeHtml(`${Number(doctrineAdoption.counts?.reusable || 0)} reusable`)}</strong>
          <span>${escapeHtml(`${Number(doctrineAdoption.counts?.blocked || 0)} blocked until source issue and implementation receipt are both named`)}</span>
          <small>${escapeHtml(doctrineAdoption.source || '')}</small>
          <em>curriculum</em>
        </div>
        ${(doctrineAdoption.reusable || []).slice(0, 4).map((row) => `<div class="h23-goodlife-evidence-row">
          <strong>${escapeHtml(row.title || row.id || 'doctrine')}</strong>
          <span>${escapeHtml(`Issue #${String(row.sourceIssue || '').padStart(3, '0')} - receipt ${row.implementationReceipt?.commit || row.implementationReceipt?.id || row.implementationReceipt?.artifact || 'named'}`)}</span>
          <small>${escapeHtml((row.doctrineFiles || []).slice(0, 2).join(' - '))}</small>
          <em>adopted</em>
        </div>`).join('')}
        ${(doctrineAdoption.blocked || []).slice(0, 3).map((row) => `<div class="h23-goodlife-evidence-row watch">
          <strong>${escapeHtml(row.title || row.id || 'blocked doctrine')}</strong>
          <span>${escapeHtml(row.reason || 'blocked')}</span>
          <small>${escapeHtml(row.sourceIssue ? `Issue #${String(row.sourceIssue).padStart(3, '0')}` : 'source issue missing')}</small>
          <em>blocked</em>
        </div>`).join('')}` : ''}
    </section>
  ` : '';
  const budgetHtml = budget ? `
    <section><h4>Autonomy Budget</h4>
      <div class="h23-goodlife-evidence-row">
        <strong>${escapeHtml(budget.status || 'available')}</strong>
        <span>${escapeHtml(budget.reason || '')}</span>
        <small>${escapeHtml(`${Number(budget.used || 0)}/${Number(budget.limit || 0)} used${budget.remaining != null ? ` - ${Number(budget.remaining || 0)} remaining` : ''}`)}</small>
      </div>
    </section>
  ` : '';
  return `
    <h3>Learned Signals</h3>
    <div class="h23-goodlife-detail-grid">
      ${Object.entries(metrics).slice(0, 8).map(([key, value]) => `<div><label>${escapeHtml(key)}</label><p>${escapeHtml(value)}</p></div>`).join('') || '<div><label>Trend Metrics</label><p>not recorded</p></div>'}
    </div>
    ${provenanceHtml}
    ${budgetHtml}
    ${renderGoodLifeHostPressure(host)}
    ${renderGoodLifePm2Changes(pm2)}
    ${renderGoodLifeScheduler(scheduler)}
    <section><h4>Restraint Receipts</h4>${restraintReceipts.length ? restraintReceipts.slice(0, 6).map((entry) => `<div class="h23-goodlife-evidence-row watch"><strong>${escapeHtml(entry.status || 'restraint')}</strong><span>${escapeHtml(entry.reason || entry.policyReason || '')}</span><small>${escapeHtml([entry.receiptId || null, entry.policyMode ? `policy:${entry.policyMode}` : null].filter(Boolean).join(' - '))}</small><em>${escapeHtml(entry.sourceIssue ? `issue #${entry.sourceIssue}` : 'receipt')}</em></div>`).join('') : '<div class="h23-goodlife-empty">No restraint receipts</div>'}</section>
    <section><h4>Recent Good Life Ledger</h4>${ledger.length ? ledger.map((entry) => `<div class="h23-goodlife-evidence-row"><strong>${escapeHtml(entry.event || entry.type || 'entry')}</strong><span>${escapeHtml(entry.summary || entry.message || entry.mode || '')}</span><small>${entry.at || entry.timestamp ? escapeHtml(timeSince(new Date(entry.at || entry.timestamp))) : ''}</small></div>`).join('') : '<div class="h23-goodlife-empty">No ledger entries</div>'}</section>
  `;
}

function renderGoodLifeOverlay() {
  const data = goodLifeSurfaceState.get(goodLifeOverlayState.scope);
  const operator = data?.operator || {};
  const overlay = document.getElementById('goodlife-overlay');
  if (!overlay || !data) return;
  setText('goodlife-overlay-title', `Good Life - ${goodLifeLabelForScope(goodLifeOverlayState.scope)}`);
  const status = document.getElementById('goodlife-overlay-status');
  if (status) {
    status.textContent = operator.status === 'current' ? 'CURRENT' : String(operator.status || 'unknown').toUpperCase();
    status.className = `h23-goodlife-overlay-status ${goodLifeCssClass(operator.status)}`;
  }
  setHtml('goodlife-overlay-top', renderGoodLifeTop(data));
  setHtml('goodlife-overlay-tabs', renderGoodLifeTabs(data));

  let listHtml = '';
  let detailHtml = '';
  if (goodLifeOverlayState.tab === 'issues') {
    const problems = goodLifeProblemsFor(data, ['chronic', 'open', 'unverifiable']);
    if (!goodLifeOverlayState.selectedProblemId || !problems.some((problem) => problem.id === goodLifeOverlayState.selectedProblemId)) {
      goodLifeOverlayState.selectedProblemId = problems[0]?.id || null;
    }
    listHtml = renderGoodLifeProblemList(problems);
    detailHtml = renderGoodLifeIssueDetail(problems.find((problem) => problem.id === goodLifeOverlayState.selectedProblemId), data);
  } else if (goodLifeOverlayState.tab === 'work') {
    listHtml = renderGoodLifeWorkList(data);
    detailHtml = renderGoodLifeWorkDetail(data);
  } else if (goodLifeOverlayState.tab === 'resolutions') {
    const problems = goodLifeProblemsFor(data, ['resolved']);
    if (!goodLifeOverlayState.selectedProblemId || !problems.some((problem) => problem.id === goodLifeOverlayState.selectedProblemId)) {
      goodLifeOverlayState.selectedProblemId = problems[0]?.id || null;
    }
    listHtml = renderGoodLifeProblemList(problems);
    detailHtml = renderGoodLifeResolutionDetail(problems.find((problem) => problem.id === goodLifeOverlayState.selectedProblemId));
  } else {
    listHtml = renderGoodLifeInsightsList(data);
    detailHtml = renderGoodLifeInsightsDetail(data);
  }

  setHtml('goodlife-overlay-list', listHtml);
  setHtml('goodlife-overlay-detail', detailHtml);
}

async function openGoodLifeOperator(scope = 'home', selectedProblemId = null, requestedTab = null) {
  const nextScope = scope || 'home';
  const scopeChanged = goodLifeOverlayState.scope !== nextScope;
  goodLifeOverlayState.scope = nextScope;
  const nextTab = requestedTab || (selectedProblemId ? 'issues' : null);
  goodLifeOverlayState.tab = nextTab || (scopeChanged ? 'issues' : goodLifeOverlayState.tab || 'issues');
  goodLifeOverlayState.selectedProblemId = selectedProblemId || (scopeChanged || nextTab ? null : goodLifeOverlayState.selectedProblemId);
  setText('goodlife-overlay-action-status', '');
  const overlay = document.getElementById('goodlife-overlay');
  if (overlay) overlay.style.display = 'flex';
  if (!goodLifeSurfaceState.has(goodLifeOverlayState.scope)) {
    setHtml('goodlife-overlay-list', '<div class="h23-goodlife-empty h23-goodlife-pad">Loading...</div>');
    await loadGoodLifeForScope(goodLifeOverlayState.scope);
  }
  if (!workersState.lastLoadedAt) {
    await loadWorkersSurface().catch(() => {});
  }
  renderGoodLifeOverlay();
}

function closeGoodLifeOperator() {
  const overlay = document.getElementById('goodlife-overlay');
  if (overlay) overlay.style.display = 'none';
}

function switchGoodLifeTab(tab) {
  goodLifeOverlayState.tab = tab;
  goodLifeOverlayState.selectedProblemId = null;
  renderGoodLifeOverlay();
}

function selectGoodLifeProblem(id) {
  goodLifeOverlayState.selectedProblemId = id;
  renderGoodLifeOverlay();
}

async function refreshGoodLifeOperator() {
  setText('goodlife-overlay-action-status', 'Refreshing...');
  await loadGoodLifeForScope(goodLifeOverlayState.scope);
  renderGoodLifeOverlay();
  setText('goodlife-overlay-action-status', 'Fresh data loaded.');
}

function openGoodLifeWorkers() {
  const workerTab = document.querySelector('.h23-tab[data-tab="workers"]');
  if (workerTab) {
    workerTab.click();
    setText('goodlife-overlay-action-status', 'Opened worker desk.');
  }
  loadWorkersSurface().catch(() => {});
}

async function reverifyGoodLifeOperator() {
  const base = goodLifeBaseForScope(goodLifeOverlayState.scope);
  setText('goodlife-overlay-action-status', 'Re-verify queued...');
  try {
    const res = await fetch(`${base}/api/live-problems/tick`, { method: 'POST' });
    const result = await res.json().catch(() => ({}));
    if (!res.ok) throw new Error(result.error || `HTTP ${res.status}`);
    await loadGoodLifeForScope(goodLifeOverlayState.scope);
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', result.note || 'Re-verify queued for the next engine tick.');
  } catch (err) {
    setText('goodlife-overlay-action-status', `Re-verify failed: ${err.message}`);
  }
}

async function testGoodLifeVerifier(problemId) {
  const data = goodLifeSurfaceState.get(goodLifeOverlayState.scope);
  const problem = (data?.liveProblems?.problems || []).find((entry) => entry.id === problemId);
  if (!problem?.verifier) {
    setText('goodlife-overlay-action-status', 'No verifier to test.');
    return;
  }
  const base = goodLifeBaseForScope(goodLifeOverlayState.scope);
  setText('goodlife-overlay-action-status', 'Testing verifier...');
  try {
    const res = await fetch(`${base}/api/live-problems/dry-run`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ verifier: problem.verifier }),
    });
    const result = await res.json();
    if (!res.ok) throw new Error(result.error || `HTTP ${res.status}`);
    setText('goodlife-overlay-action-status', result.supported === false
      ? `Verifier needs engine context: ${result.reason}`
      : `Verifier result: ${result.result?.ok ? 'pass' : 'fail'} - ${result.result?.detail || 'no detail'}`);
  } catch (err) {
    setText('goodlife-overlay-action-status', `Verifier test failed: ${err.message}`);
  }
}

async function recordGoodLifeUserIntervention(problemId) {
  if (!problemId) return;
  const base = goodLifeBaseForScope(goodLifeOverlayState.scope);
  const note = window.prompt('What did you handle for this issue?', 'Manual intervention completed.');
  if (note === null) return;
  setText('goodlife-overlay-action-status', 'Recording user intervention...');
  try {
    const res = await fetch(`${base}/api/live-problems/${encodeURIComponent(problemId)}/user-intervention`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        actor: 'good-life-operator',
        note: note || 'Manual intervention completed.',
      }),
    });
    const result = await res.json().catch(() => ({}));
    if (!res.ok || result.ok === false) throw new Error(result.error || `HTTP ${res.status}`);
    setText('goodlife-overlay-action-status', 'Intervention recorded; re-checking verifier...');
    await fetch(`${base}/api/live-problems/tick`, { method: 'POST' }).catch(() => null);
    await loadGoodLifeForScope(goodLifeOverlayState.scope);
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', 'Intervention receipt recorded. Verifier remains the source of truth.');
  } catch (err) {
    setText('goodlife-overlay-action-status', `Intervention record failed: ${err.message}`);
  }
}

async function updateGoodLifeAgendaStatus(agendaId, status) {
  if (!agendaId || !status) return;
  const base = goodLifeBaseForScope(goodLifeOverlayState.scope);
  setText('goodlife-overlay-action-status', `${status === 'stale' ? 'Dismissing' : 'Updating'} ${agendaId}...`);
  try {
    const res = await fetch(`${base}/api/agenda/${encodeURIComponent(agendaId)}/status`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        status,
        actor: 'good-life-operator',
        note: status === 'stale'
          ? 'dismissed from Good Life operator work surface'
          : 'acknowledged from Good Life operator work surface',
      }),
    });
    const result = await res.json().catch(() => ({}));
    if (!res.ok || result.ok === false) throw new Error(result.error || `HTTP ${res.status}`);
    await loadGoodLifeForScope(goodLifeOverlayState.scope);
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `${agendaId} marked ${status}.`);
  } catch (err) {
    setText('goodlife-overlay-action-status', `Agenda update failed: ${err.message}`);
  }
}

async function dismissGoodLifeAgendaReviewRows() {
  const data = goodLifeSurfaceState.get(goodLifeOverlayState.scope);
  const agenda = data?.operator?.detail?.work?.obligations?.activeAgenda || [];
  const reviewRows = agenda.filter((item) => item?.id && item.review?.recommended);
  if (!reviewRows.length) {
    setText('goodlife-overlay-action-status', 'No Good Life agenda review rows to dismiss.');
    return;
  }

  const confirmed = window.confirm(`Dismiss ${reviewRows.length} Good Life agenda review row${reviewRows.length === 1 ? '' : 's'} as stale? This keeps a status receipt and removes them from active work.`);
  if (!confirmed) return;

  const base = goodLifeBaseForScope(goodLifeOverlayState.scope);
  setText('goodlife-overlay-action-status', `Dismissing ${reviewRows.length} agenda review row${reviewRows.length === 1 ? '' : 's'}...`);
  let updated = 0;
  try {
    for (const row of reviewRows) {
      const res = await fetch(`${base}/api/agenda/${encodeURIComponent(row.id)}/status`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          status: 'stale',
          actor: 'good-life-operator',
          note: `dismissed stale Good Life review row: ${row.review?.reason || 'operator review recommended'}`,
        }),
      });
      const result = await res.json().catch(() => ({}));
      if (!res.ok || result.ok === false) throw new Error(result.error || `HTTP ${res.status}`);
      updated += 1;
    }
    await loadGoodLifeForScope(goodLifeOverlayState.scope);
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `${updated} agenda review row${updated === 1 ? '' : 's'} dismissed.`);
  } catch (err) {
    await loadGoodLifeForScope(goodLifeOverlayState.scope).catch(() => {});
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `Dismissed ${updated}/${reviewRows.length}; failed: ${err.message}`);
  }
}

async function archiveGoodLifeGoal(goalId) {
  if (!goalId) return;
  const confirmed = window.confirm(`Archive active goal ${goalId}? This removes it from the engine's active goal list without marking it completed.`);
  if (!confirmed) return;

  const base = goodLifeBaseForScope(goodLifeOverlayState.scope);
  setText('goodlife-overlay-action-status', `Archiving ${goalId}...`);
  try {
    const res = await fetch(`${base}/api/goals/${encodeURIComponent(goalId)}/archive`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        actor: 'good-life-operator',
        reason: 'archived from Good Life operator after review',
      }),
    });
    const result = await res.json().catch(() => ({}));
    if (!res.ok || result.ok === false) throw new Error(result.error || `HTTP ${res.status}`);
    await loadGoodLifeForScope(goodLifeOverlayState.scope);
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `${goalId} archived.`);
  } catch (err) {
    setText('goodlife-overlay-action-status', `Goal archive failed: ${err.message}`);
  }
}

async function archiveGoodLifeGoalReviewRows() {
  const data = goodLifeSurfaceState.get(goodLifeOverlayState.scope);
  const goals = data?.operator?.detail?.work?.obligations?.activeGoals || [];
  const reviewGoals = goals.filter((goal) => goal?.id && goal.review?.recommended);
  if (!reviewGoals.length) {
    setText('goodlife-overlay-action-status', 'No Good Life goals need operator review.');
    return;
  }

  const confirmed = window.confirm(`Archive ${reviewGoals.length} reviewed Good Life goal${reviewGoals.length === 1 ? '' : 's'}? This removes stale goals from the active list without marking them completed.`);
  if (!confirmed) return;

  const base = goodLifeBaseForScope(goodLifeOverlayState.scope);
  setText('goodlife-overlay-action-status', `Archiving ${reviewGoals.length} reviewed goal${reviewGoals.length === 1 ? '' : 's'}...`);
  let updated = 0;
  try {
    for (const goal of reviewGoals) {
      const res = await fetch(`${base}/api/goals/${encodeURIComponent(goal.id)}/archive`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          actor: 'good-life-operator',
          reason: `bulk archived from Good Life operator review: ${goal.review?.reason || 'operator review recommended'}`,
        }),
      });
      const result = await res.json().catch(() => ({}));
      if (!res.ok || result.ok === false) throw new Error(result.error || `HTTP ${res.status}`);
      updated += 1;
    }
    await loadGoodLifeForScope(goodLifeOverlayState.scope);
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `${updated} reviewed goal${updated === 1 ? '' : 's'} archived.`);
  } catch (err) {
    await loadGoodLifeForScope(goodLifeOverlayState.scope).catch(() => {});
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `Archived ${updated}/${reviewGoals.length}; failed: ${err.message}`);
  }
}

async function runGoodLifeWorkerCheck(problemId) {
  const data = goodLifeSurfaceState.get(goodLifeOverlayState.scope);
  const problem = (data?.liveProblems?.problems || []).find((entry) => entry.id === problemId);
  if (!problem) {
    setText('goodlife-overlay-action-status', 'Select an active issue first.');
    return;
  }

  setText('goodlife-overlay-action-status', 'Starting worker check...');
  try {
    const worker = await chooseGoodLifeWorker();
    if (!worker?.name) throw new Error('No worker specialists are available.');
    const result = await workerApi(`/${encodeURIComponent(worker.name)}/runs`, {
      method: 'POST',
      body: JSON.stringify({
        prompt: buildGoodLifeWorkerPrompt(problem),
        requestedBy: 'good-life-operator',
        requester: 'home23-dashboard',
        ownerAgent: goodLifeOwnerAgentForScope(goodLifeOverlayState.scope),
        source: { type: 'live-problem', id: problem.id },
        metadata: {
          surface: 'good-life',
          scope: goodLifeOverlayState.scope,
          problemId: problem.id,
        },
      }),
    });
    workersState.receipt = result.receipt || null;
    if (result.runId) {
      await loadWorkersSurface().catch(() => {});
      await openWorkerReceipt(result.runId).catch(() => {});
    }
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `Worker check complete: ${formatGoodLifeWorkerResult(workersState.receipt || result.receipt, result.runId)}.`);
  } catch (err) {
    setText('goodlife-overlay-action-status', `Worker check failed: ${err.message}`);
  }
}

async function runGoodLifeAgendaWorkerCheck(agendaId) {
  const data = goodLifeSurfaceState.get(goodLifeOverlayState.scope);
  const agenda = data?.operator?.detail?.work?.obligations?.activeAgenda || [];
  const item = agenda.find((entry) => entry.id === agendaId);
  if (!item) {
    setText('goodlife-overlay-action-status', 'Select an active Good Life work item first.');
    return;
  }
  const workerRoute = effectiveGoodLifeAgendaWorkerRoute(item);
  const workerName = workerRoute?.worker;
  if (!workerName) {
    setText('goodlife-overlay-action-status', 'This Good Life work item does not have a recommended worker route.');
    return;
  }

  const owner = goodLifeOwnerAgentForScope(goodLifeOverlayState.scope);
  setText('goodlife-overlay-action-status', `Starting ${workerName} worker for ${agendaId}...`);
  try {
    const result = await workerApi(`/${encodeURIComponent(workerName)}/runs`, {
      method: 'POST',
      body: JSON.stringify({
        prompt: buildGoodLifeAgendaWorkerPrompt(item),
        requestedBy: 'good-life-operator',
        requester: 'home23-dashboard',
        ownerAgent: owner,
        source: { type: 'good-life-agenda', id: agendaId },
        metadata: {
          surface: 'good-life',
          scope: goodLifeOverlayState.scope,
          agendaId,
          workerRoute,
        },
      }),
    });
    workersState.receipt = result.receipt || null;
    if (result.runId) {
      await loadWorkersSurface().catch(() => {});
      await openWorkerReceipt(result.runId).catch(() => {});
    }
    await loadGoodLifeForScope(goodLifeOverlayState.scope).catch(() => {});
    renderGoodLifeOverlay();
    setText('goodlife-overlay-action-status', `${workerName} worker complete: ${formatGoodLifeWorkerResult(workersState.receipt || result.receipt, result.runId)}.`);
  } catch (err) {
    setText('goodlife-overlay-action-status', `${workerName} worker failed: ${err.message}`);
  }
}

function updateSystemTile(state) {
  const journal = Array.isArray(state.journal) ? state.journal : [];
  const lastThought = journal.length > 0 ? journal[journal.length - 1] : null;
  const uptime = state.uptime || formatUptime(state);
  const thoughtCount = state.thoughtCount ?? journal.length;
  const nodeCount = state.memoryNodes ?? state.nodeCount ?? state.memory?.nodes?.length ?? null;
  const lastThoughtAt = state.lastThoughtAt || lastThought?.timestamp || null;

  if (uptime && uptime !== '—') setText('sys-uptime', uptime);
  setText('sys-thoughts', thoughtCount != null ? String(thoughtCount) : '—');
  setText('sys-nodes', nodeCount != null ? String(nodeCount) : '—');
  setText('sys-last', lastThoughtAt ? timeSince(new Date(lastThoughtAt)) : '—');

  // Excerpt of latest thought in system tile
  const latestThoughtText = state.lastThoughtText || lastThought?.thought;
  if (latestThoughtText) {
    const excerpt = latestThoughtText.length > 120
      ? `${latestThoughtText.slice(0, 120)}...`
      : latestThoughtText;
    setText('sys-excerpt', excerpt);
  } else {
    setText('sys-excerpt', '');
  }

  updatePulseFromState(state);
}

function extractNodeCount(state) {
  const nodes = state?.memory?.nodes ?? state?.memoryNodes ?? state?.nodeCount;
  if (Array.isArray(nodes)) return nodes.length;
  if (typeof nodes === 'number') return nodes;
  return null;
}

function extractActiveGoalCount(state) {
  const active = state?.goals?.active ?? state?.activeGoals;
  if (Array.isArray(active)) return active.length;
  if (typeof active === 'number') return active;
  if (active && typeof active === 'object') return Object.keys(active).length;
  return null;
}

function formatCompactNumber(value) {
  if (value == null || !Number.isFinite(Number(value))) return '—';
  return Number(value).toLocaleString('en-US');
}

function updateOverviewMetrics(state, summary = null) {
  const nodeCount = extractNodeCount(state) ?? extractNodeCount(summary);
  const activeGoalCount = extractActiveGoalCount(state) ?? extractActiveGoalCount(summary);
  const energy = state?.cognitiveState?.energy;
  if (typeof energy === 'number') {
    setText('pulse-energy', `⚡ ${Math.round(energy * 100)}%`);
  }
  setText('pulse-node-count', formatCompactNumber(nodeCount));
  setText('pulse-active-goals', activeGoalCount != null ? String(activeGoalCount) : '—');
}

function updatePulseFromState(state) {
  // Feed pulse bar from state API (initial + polling fallback)
  const cs = state.cognitiveState || {};
  const temporal = state.temporal || {};
  enginePulse.cycle = state.cycleCount || enginePulse.cycle;
  enginePulse.energy = cs.energy || enginePulse.energy;
  if (temporal.state === 'sleeping' || cs.mode === 'sleeping') {
    enginePulse.state = 'sleeping';
  } else if (enginePulse.state === 'unknown') {
    enginePulse.state = 'awake';
  }
  if (!enginePulse.phase || enginePulse.phase === '—') {
    enginePulse.phase = state.oscillatorMode || 'focus';
  }
  const journal = state.journal || [];
  if (journal.length > 0) {
    const last = journal[journal.length - 1];
    if (last.timestamp) enginePulse.lastThought = new Date(last.timestamp);
  }
  renderPulse();
}

function updateThoughtsTile(thoughts) {
  _cachedThoughts = thoughts;
  refreshHomeThoughtFeed();
}

function updateBrainLog(thoughts) {
  const container = document.getElementById('home-brainlog');
  if (!container) return;

  // Update brain log timestamp
  const stamp = document.getElementById('brainlog-stamp');
  if (stamp) stamp.textContent = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit' });

  if (thoughts.length === 0) {
    container.innerHTML = '<p class="h23-muted">Loading...</p>';
    return;
  }

  const reversed = [...thoughts].reverse();
  container.innerHTML = reversed.map(t => {
    const text = t.thought || t.content || t.text || '';
    const role = t.role || '';
    const time = t.timestamp
      ? new Date(t.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
      : (t.cycle ? `C${t.cycle}` : '');

    return `<div class="h23-log-entry">
      <span class="h23-log-time">${time}</span>
      <span class="h23-log-role">${role}</span>
      <span class="h23-log-text">${text.slice(0, 200)}</span>
    </div>`;
  }).join('');
}

// ── Dream Log ──

function updateDreamLog(dreams) {
  const container = document.getElementById('home-dreamlog');
  if (!container) return;
  _cachedDreams = dreams;

  const stamp = document.getElementById('dreamlog-stamp');
  if (stamp) stamp.textContent = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit' });

  // Filter to narrative dreams (with content), newest first
  const narratives = dreams
    .filter(d => d.content && d.content.length > 20)
    .sort((a, b) => new Date(b.timestamp || 0) - new Date(a.timestamp || 0));

  if (narratives.length === 0) {
    container.innerHTML = '<p class="h23-muted">No dreams yet — the agent dreams during sleep cycles.</p>';
    return;
  }

  container.innerHTML = narratives.slice(0, 10).map(d => {
    const time = d.timestamp
      ? new Date(d.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
      : (d.cycle ? `Cycle ${d.cycle}` : '');
    const cycle = d.cycle ? `cycle ${d.cycle}` : '';
    const meta = [time, cycle].filter(Boolean).join(' · ');
    const text = (d.content || d.thought || '').replace(/\*\*/g, '').replace(/\n/g, ' ').slice(0, 200);

    return `<div class="h23-dream-entry">
      <div class="h23-dream-meta">${meta}</div>
      <div class="h23-dream-text">${text}</div>
    </div>`;
  }).join('');

  refreshHomeThoughtFeed();
}

function startHomeThoughtRotation() {
  if (homeThoughtRotationTimer) return;

  homeThoughtRotationTimer = setInterval(() => {
    if (currentTab !== 'home' || _homeThoughtFeed.length <= 1) return;
    _homeThoughtIndex = (_homeThoughtIndex + 1) % _homeThoughtFeed.length;
    renderHomeThoughtEntry(_homeThoughtFeed[_homeThoughtIndex]);
  }, HOME_THOUGHT_ROTATE_MS);
}

function refreshHomeThoughtFeed() {
  const nextFeed = buildHomeThoughtFeed(_cachedThoughts, _cachedDreams);
  const textEl = document.getElementById('home-thought');
  const metaEl = document.getElementById('home-thought-meta');
  if (!textEl || !metaEl) return;

  if (nextFeed.length === 0) {
    textEl.dataset.kind = 'thought';
    metaEl.dataset.kind = 'thought';
    setText('home-thought', 'Loading...');
    setText('home-thought-meta', '');
    _homeThoughtFeed = [];
    _homeThoughtCurrentId = null;
    _homeThoughtIndex = 0;
    return;
  }

  const existingIndex = nextFeed.findIndex(entry => entry.id === _homeThoughtCurrentId);
  if (existingIndex >= 0) {
    _homeThoughtIndex = existingIndex;
  } else if (_homeThoughtIndex >= nextFeed.length) {
    _homeThoughtIndex = 0;
  }

  _homeThoughtFeed = nextFeed;
  renderHomeThoughtEntry(_homeThoughtFeed[_homeThoughtIndex]);
}

function buildHomeThoughtFeed(thoughts, dreams) {
  const thoughtEntries = buildRoleDiverseThoughtEntries(thoughts, 8);
  const dreamEntries = buildDreamEntries(dreams, 4);
  const feed = [];

  while (thoughtEntries.length || dreamEntries.length) {
    for (let i = 0; i < 2 && thoughtEntries.length; i += 1) {
      feed.push(thoughtEntries.shift());
    }

    if (dreamEntries.length) {
      feed.push(dreamEntries.shift());
    }

    if (!dreamEntries.length && thoughtEntries.length) {
      feed.push(thoughtEntries.shift());
    }
  }

  return dedupeFeedEntries(feed).slice(0, 10);
}

function buildRoleDiverseThoughtEntries(thoughts, maxEntries = 8) {
  const validThoughts = [...(thoughts || [])]
    .filter(entry => {
      const text = (entry.thought || entry.content || entry.text || '').trim();
      return text && entry.role !== 'sleep';
    })
    .sort((a, b) => getTimestampMs(b.timestamp) - getTimestampMs(a.timestamp));

  const buckets = new Map();
  for (const thought of validThoughts) {
    const role = thought.role || 'thought';
    if (!buckets.has(role)) buckets.set(role, []);
    buckets.get(role).push(thought);
  }

  const roleOrder = [...buckets.entries()]
    .sort((a, b) => getTimestampMs(b[1][0]?.timestamp) - getTimestampMs(a[1][0]?.timestamp))
    .map(([role]) => role);

  const entries = [];
  while (entries.length < maxEntries) {
    let added = false;
    for (const role of roleOrder) {
      const bucket = buckets.get(role);
      if (bucket && bucket.length > 0) {
        entries.push(normalizeThoughtEntry(bucket.shift()));
        added = true;
        if (entries.length >= maxEntries) break;
      }
    }
    if (!added) break;
  }

  return entries.filter(Boolean);
}

function buildDreamEntries(dreams, maxEntries = 4) {
  return [...(dreams || [])]
    .filter(entry => (entry.content || entry.thought || '').trim().length > 20)
    .sort((a, b) => getTimestampMs(b.timestamp) - getTimestampMs(a.timestamp))
    .slice(0, maxEntries)
    .map(normalizeDreamEntry)
    .filter(Boolean);
}

function normalizeThoughtEntry(entry) {
  const text = (entry.thought || entry.content || entry.text || '').trim();
  if (!text) return null;

  const timestampMs = getTimestampMs(entry.timestamp);
  const meta = ['Thought', (entry.role || 'inner life').toUpperCase()];
  if (entry.cycle) meta.push(`Cycle ${entry.cycle}`);
  if (timestampMs) meta.push(timeSince(new Date(timestampMs)));

  return {
    id: `thought:${entry.timestamp || entry.cycle || text.slice(0, 24)}`,
    kind: 'thought',
    text,
    meta: meta.join(' · '),
    timestampMs,
  };
}

function normalizeDreamEntry(entry) {
  const text = (entry.content || entry.thought || '').trim();
  if (!text) return null;

  const timestampMs = getTimestampMs(entry.timestamp);
  const meta = ['Dream'];
  if (entry.cycle) meta.push(`Cycle ${entry.cycle}`);
  if (timestampMs) meta.push(timeSince(new Date(timestampMs)));

  return {
    id: `dream:${entry.id || entry.timestamp || entry.cycle || text.slice(0, 24)}`,
    kind: 'dream',
    text,
    meta: meta.join(' · '),
    timestampMs,
  };
}

function dedupeFeedEntries(entries) {
  const seen = new Set();
  return entries.filter(entry => {
    const key = (entry.text || '')
      .toLowerCase()
      .replace(/\s+/g, ' ')
      .slice(0, 160);
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

function renderHomeThoughtEntry(entry) {
  const textEl = document.getElementById('home-thought');
  const metaEl = document.getElementById('home-thought-meta');
  if (!textEl || !metaEl || !entry) return;

  _homeThoughtCurrentId = entry.id;
  textEl.dataset.kind = entry.kind;
  metaEl.dataset.kind = entry.kind;
  setText('home-thought', entry.text);
  setText('home-thought-meta', entry.meta);
}

// ── Feeder Tile ──

let _cachedFeederData = null;

function updateFeederTile(data) {
  const container = document.getElementById('home-feeder');
  if (!container) return;

  const feeders = data.feeders || [];
  const feeder = feeders.find(f => f.member === primaryAgent.name) || feeders[0];

  if (!feeder) {
    container.innerHTML = '<p class="h23-muted">No feeder data</p>';
    return;
  }

  _cachedFeederData = feeder;
  const files = feeder.files || [];
  const compiled = feeder.compiledCount || 0;
  const total = feeder.totalFiles || 0;
  const processed = feeder.processedFiles || files.length;
  const pending = feeder.pendingCount || 0;
  const chunks = feeder.chunkCount || files.reduce((sum, f) => sum + (f.chunks || 0), 0);
  const pct = total > 0 ? Math.round((processed / total) * 100) : 0;

  // Most recent file
  const sorted = [...files].sort((a, b) => (b.lastIngested || '').localeCompare(a.lastIngested || ''));
  const recent = sorted[0];
  const recentName = recent ? recent.path.split('/').pop() : '';
  const recentAgo = recent?.lastIngested ? timeSince(new Date(recent.lastIngested)) : '';

  container.innerHTML = `
    <div class="h23-feeder-summary">
      <div class="h23-feeder-stat"><span class="value">${total}</span> in workspace</div>
      <div class="h23-feeder-stat"><span class="value${processed < total ? ' compiling' : ''}">${processed}</span> processed</div>
      <div class="h23-feeder-stat"><span class="value">${compiled}</span> compiled</div>
      <div class="h23-feeder-stat"><span class="value">${chunks}</span> nodes</div>
      <div class="h23-feeder-progress">
        <div class="h23-feeder-progress-bar"><div class="h23-feeder-progress-fill" style="width:${pct}%"></div></div>
        <div class="h23-feeder-progress-label">${processed} of ${total} · ${pending > 0 ? pending + ' remaining' : 'complete'}</div>
      </div>
    </div>
    ${recentName ? `<div class="h23-feeder-recent">Latest: <span class="filename">${recentName}</span> · ${recentAgo}</div>` : ''}
  `;
}

function openFeederOverlay() {
  const overlay = document.getElementById('feeder-overlay');
  const body = document.getElementById('feeder-overlay-body');
  if (!overlay || !body) return;

  if (!_cachedFeederData) {
    body.innerHTML = '<p class="h23-muted">No feeder data available</p>';
    overlay.style.display = 'flex';
    return;
  }

  const f = _cachedFeederData;
  const files = f.files || [];
  const compiled = f.compiledCount || 0;
  const quarantined = f.quarantinedCount || 0;
  const total = f.totalFiles || 0;
  const processed = f.processedFiles || files.length;
  const chunks = f.chunkCount || files.reduce((sum, x) => sum + (x.chunks || 0), 0);
  const pending = f.pendingCount || 0;
  const pct = total > 0 ? Math.round((processed / total) * 100) : 0;

  const sorted = [...files].sort((a, b) => (b.lastIngested || '').localeCompare(a.lastIngested || ''));

  body.innerHTML = `
    <div class="h23-feeder-overlay-stats">
      <div class="h23-feeder-stat"><span class="value">${total}</span> in workspace</div>
      <div class="h23-feeder-stat"><span class="value${processed < total ? ' compiling' : ''}">${processed}</span> processed</div>
      <div class="h23-feeder-stat"><span class="value">${compiled}</span> compiled</div>
      <div class="h23-feeder-stat"><span class="value">${chunks}</span> brain nodes</div>
      <div class="h23-feeder-stat"><span class="value">${pending}</span> remaining</div>
      ${quarantined ? `<div class="h23-feeder-stat"><span class="value" style="color:#fb923c">${quarantined}</span> quarantined</div>` : ''}
    </div>
    <div class="h23-feeder-progress" style="margin-bottom:16px">
      <div class="h23-feeder-progress-bar"><div class="h23-feeder-progress-fill" style="width:${pct}%"></div></div>
      <div class="h23-feeder-progress-label">${processed} of ${total} files processed · ${compiled} through LLM compiler</div>
    </div>
    <div class="h23-feeder-overlay-section">
      <h3>Recent Files (${Math.min(sorted.length, 50)} shown)</h3>
      <div class="h23-feeder-file-list">
        ${sorted.map(x => {
          const name = x.path.split('/').pop();
          const dir = x.path.split('/').slice(-2, -1)[0] || '';
          const ago = x.lastIngested ? timeSince(new Date(x.lastIngested)) : '—';
          const isQuarantined = x.status === 'suspect_truncation' || x.status === 'un_normalizable';
          const badge = isQuarantined
            ? '<span class="badge quarantined">quarantined</span>'
            : x.compiled
              ? '<span class="badge compiled">compiled</span>'
              : '<span class="badge raw">raw</span>';
          return `<div class="h23-feeder-file">
            <span class="path" title="${x.path}">${dir ? dir + '/' : ''}${name}</span>
            <span class="meta">
              ${badge}
              <span class="chunks">${x.chunks || 0} chunks</span>
              <span class="ago">${ago}</span>
            </span>
          </div>`;
        }).join('')}
      </div>
    </div>
  `;

  overlay.style.display = 'flex';
}

function closeFeederOverlay() {
  const overlay = document.getElementById('feeder-overlay');
  if (overlay) overlay.style.display = 'none';
}

// ── Secondary Agent Panels ──

function createAgentPanel(agentName) {
  const agent = agents.find(a => a.name === agentName);
  const displayName = agent?.displayName || agentName;

  const panel = document.createElement('div');
  panel.className = 'h23-panel';
	  panel.id = `panel-agent-${agentName}`;
	  panel.innerHTML = `
	    ${renderGoodLifeTile(`agent-${agentName}`, `Good Life - ${displayName}`)}
	    <div class="h23-grid-top">
	      <div class="h23-tile h23-tile-thoughts">
	        <div class="h23-tile-header"><span class="icon">🧠</span> ${displayName}</div>
        <div class="h23-thought-text" id="thought-${agentName}">Loading...</div>
        <div class="h23-thought-meta" id="thought-meta-${agentName}"></div>
      </div>
      <div class="h23-tile h23-tile-vibe">
        <div class="h23-tile-header"><span class="icon">🎨</span> Vibe</div>
        <div class="h23-vibe-image" id="vibe-image-${agentName}"><span class="h23-vibe-placeholder">Generating...</span></div>
        <div class="h23-vibe-caption" id="vibe-caption-${agentName}"></div>
      </div>
      <div class="h23-tile h23-tile-system">
        <div class="h23-tile-header"><span class="icon">⚡</span> System</div>
        <div class="h23-system-grid">
          <div class="h23-system-item"><label>UPTIME</label><div class="value" id="sys2-uptime-${agentName}">—</div></div>
          <div class="h23-system-item"><label>THOUGHTS</label><div class="value" id="sys2-thoughts-${agentName}">—</div></div>
          <div class="h23-system-item"><label>NODES</label><div class="value" id="sys2-nodes-${agentName}">—</div></div>
          <div class="h23-system-item"><label>LAST THOUGHT</label><div class="value" id="sys2-last-${agentName}">—</div></div>
        </div>
	      </div>
	    </div>
	    <div class="h23-tile h23-tile-brainlog">
	      <div class="h23-tile-header"><span class="icon">🧠</span> BRAIN LOG</div>
	      <div class="h23-brain-log" id="brainlog-${agentName}"><p class="h23-muted">Loading...</p></div>
    </div>
  `;
  return panel;
}

async function loadAgentPanel(agentName) {
  const agent = agents.find(a => a.name === agentName);
  if (!agent) return;
  const base = apiBase(agent);

  const [summary, engineHealth, goodLifeData] = await Promise.all([
    apiFetch(`${base}/api/home/summary`, { timeoutMs: 4000 }).catch(() => null),
    fetchEngineHealth(agent).catch(() => null),
    apiFetch(`${base}/api/good-life`, { timeoutMs: GOOD_LIFE_API_TIMEOUT_MS }).catch(() => null)
  ]);
  updateGoodLifeTile(goodLifeData, `agent-${agentName}`);

  if (summary) {
    setText(`sys2-thoughts-${agentName}`, summary.thoughtCount != null ? String(summary.thoughtCount) : '—');
    setText(`sys2-nodes-${agentName}`, summary.memoryNodes != null ? String(summary.memoryNodes) : '—');
    setText(`sys2-last-${agentName}`, summary.lastThoughtAt ? timeSince(new Date(summary.lastThoughtAt)) : '—');
  }

  if (engineHealth) {
    setText(`sys2-uptime-${agentName}`, formatDurationMs(engineHealth.uptime));
  }

  try {
    const data = await apiFetch(`${base}/api/thoughts?limit=20`);
    if (data) {
      const thoughts = data.thoughts || data.journal || data || [];
      if (thoughts.length > 0) {
        const latest = thoughts[thoughts.length - 1];
        setText(`thought-${agentName}`, latest.thought || latest.content || '');
        setText(`thought-meta-${agentName}`, `${(latest.role || 'thought').toUpperCase()} · CYCLE ${latest.cycle || ''}`);
      }
      // Brain log
      const container = document.getElementById(`brainlog-${agentName}`);
      if (container && thoughts.length > 0) {
        const reversed = [...thoughts].reverse();
        container.innerHTML = reversed.map(t => {
          const text = t.thought || t.content || '';
          const role = t.role || '';
          const time = t.timestamp
            ? new Date(t.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
            : '';
          return `<div class="h23-log-entry">
            <span class="h23-log-time">${time}</span>
            <span class="h23-log-role">${role}</span>
            <span class="h23-log-text">${text.slice(0, 200)}</span>
          </div>`;
        }).join('');
      }
    }
  } catch { /* offline */ }

  await loadVibeTile(agent, {
    imageId: `vibe-image-${agentName}`,
    captionId: `vibe-caption-${agentName}`,
  });
}

// ── Auto-Refresh ──

function startAutoRefresh() {
  setInterval(async () => {
    if (currentTab === 'home') {
      await refreshHomeTiles();
    } else if (currentTab.startsWith('agent-')) {
      await loadAgentPanel(currentTab.replace('agent-', ''));
    }
    // cosmo23 tab: iframe handles its own refresh
  }, REFRESH_MS);
}

// ── Utilities ──

function escapeHtml(value) {
  return String(value ?? '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function apiBase(agent) {
  const port = agent.dashboardPort;
  return port == (parseInt(window.location.port) || 5002)
    ? '' : `http://${window.location.hostname}:${port}`;
}

function setupVibeActions() {
  const galleryLink = document.getElementById('home-vibe-gallery-link');
  if (galleryLink) {
    galleryLink.href = '/home23/vibe-gallery';
  }

  const vibeTrigger = document.getElementById('vibe-trigger');
  if (vibeTrigger && vibeTrigger.dataset.bound !== 'true') {
    vibeTrigger.addEventListener('click', async (event) => {
      if (event.detail !== 3) return;
      await triggerVibeGeneration();
    });
    vibeTrigger.dataset.bound = 'true';
  }
}

async function triggerVibeGeneration() {
  setText('home-vibe-caption', 'Generating a fresh chaos vibe...');

  try {
    await fetch('/home23/api/vibe/generate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
    });
    await loadVibeTile(primaryAgent, {
      imageId: 'home-vibe-image',
      captionId: 'home-vibe-caption',
      galleryHrefId: 'home-vibe-gallery-link',
    });
  } catch (err) {
    setText('home-vibe-caption', `Generation failed: ${err.message}`);
  }
}

async function loadVibeTile(agent, { imageId, captionId, galleryHrefId = null }) {
  const base = apiBase(agent);
  const imageEl = document.getElementById(imageId);
  const captionEl = document.getElementById(captionId);
  const galleryHref = galleryHrefId ? document.getElementById(galleryHrefId) : null;
  if (!imageEl || !captionEl) return;

  if (galleryHref) {
    galleryHref.href = `${base}/home23/vibe-gallery`;
  }

  try {
    const data = await apiFetch(`${base}/home23/api/vibe/current`);
    const galleryUrl = `${base}/home23/vibe-gallery`;

    if (data?.item?.url) {
      imageEl.innerHTML = `<img src="${data.item.url}" alt="Vibe image for ${agent.displayName || agent.name}" loading="lazy">`;
      imageEl.classList.add('clickable');
      imageEl.onclick = () => { window.location.href = galleryUrl; };
      captionEl.textContent = data.item.caption || '';
      return;
    }

    imageEl.classList.remove('clickable');
    imageEl.onclick = null;
    const placeholder = data?.generating
      ? 'Conjuring a new chaos vibe...'
      : 'No image yet';
    imageEl.innerHTML = `<span class="h23-vibe-placeholder">${placeholder}</span>`;
    captionEl.textContent = data?.generating
      ? 'A fresh vibe image is being generated in the background.'
      : 'The gallery is empty. The dashboard will seed it on the next generation window.';
  } catch {
    imageEl.classList.remove('clickable');
    imageEl.onclick = null;
    imageEl.innerHTML = '<span class="h23-vibe-placeholder">Vibe offline</span>';
    captionEl.textContent = 'Could not load the current vibe image.';
  }
}

// ── Log Overlay ──

// Cache the last fetched data for overlay rendering
let _cachedThoughts = [];
let _cachedDreams = [];
let _homeThoughtFeed = [];
let _homeThoughtIndex = 0;
let _homeThoughtCurrentId = null;

function openLogOverlay(type) {
  const overlay = document.getElementById('log-overlay');
  const title = document.getElementById('log-overlay-title');
  const body = document.getElementById('log-overlay-body');
  if (!overlay || !body) return;

  if (type === 'brain') {
    title.textContent = '🧠 BRAIN LOG';
    const thoughts = _cachedThoughts;
    if (thoughts.length === 0) {
      body.innerHTML = '<p class="h23-muted">No thoughts yet.</p>';
    } else {
      const reversed = [...thoughts].reverse();
      body.innerHTML = reversed.map(t => {
        const text = t.thought || t.content || t.text || '';
        const role = t.role || '';
        const time = t.timestamp
          ? new Date(t.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
          : '';
        const cycle = t.cycle ? `cycle ${t.cycle}` : '';
        return `<div class="h23-log-entry-full">
          <div class="h23-log-entry-full-meta">
            <span>${time}</span>
            <span class="h23-log-entry-full-role">${role}</span>
            <span>${cycle}</span>
          </div>
          <div class="h23-log-entry-full-text">${text}</div>
        </div>`;
      }).join('');
    }
  } else if (type === 'dream') {
    title.textContent = '💭 DREAM LOG';
    const dreams = _cachedDreams.filter(d => d.content && d.content.length > 20);
    if (dreams.length === 0) {
      body.innerHTML = '<p class="h23-muted">No dreams yet — the agent dreams during sleep cycles.</p>';
    } else {
      const sorted = [...dreams].sort((a, b) => new Date(b.timestamp || 0) - new Date(a.timestamp || 0));
      body.innerHTML = sorted.map(d => {
        const time = d.timestamp
          ? new Date(d.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
          : '';
        const cycle = d.cycle ? `cycle ${d.cycle}` : '';
        const meta = [time, cycle].filter(Boolean).join(' · ');
        const text = (d.content || d.thought || '').replace(/\n/g, '<br>');
        return `<div class="h23-dream-entry-full">
          <div class="h23-dream-entry-full-meta">${meta}</div>
          <div class="h23-dream-entry-full-text">${text}</div>
        </div>`;
      }).join('');
    }
  }

  overlay.classList.add('active');
}

function closeLogOverlay() {
  const overlay = document.getElementById('log-overlay');
  if (overlay) overlay.classList.remove('active');
}

async function apiFetch(url, options = {}) {
  const { timeoutMs = 15000 } = options;
  const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
  if (!res.ok) return null;
  return res.json();
}

function setText(id, text) {
  const el = document.getElementById(id);
  if (el) el.textContent = text;
}

function setHtml(id, html) {
  const el = document.getElementById(id);
  if (el) el.innerHTML = html;
}

function getTimestampMs(value) {
  const ms = Date.parse(value || '');
  return Number.isFinite(ms) ? ms : 0;
}

function timeSince(date) {
  const s = Math.floor((Date.now() - date.getTime()) / 1000);
  if (s < 60) return `${s}s ago`;
  const m = Math.floor(s / 60);
  if (m < 60) return `${m}m ago`;
  const h = Math.floor(m / 60);
  if (h < 24) return `${h}h ${m % 60}m`;
  return `${Math.floor(h / 24)}d ago`;
}

function formatUptime(state) {
  // Try to derive from cycle count and interval
  if (state.cycleCount && state.cycleInterval) {
    const seconds = state.cycleCount * (state.cycleInterval / 1000);
    const h = Math.floor(seconds / 3600);
    const m = Math.floor((seconds % 3600) / 60);
    return `${h}h ${m}m`;
  }
  return '—';
}

function formatDurationMs(ms) {
  if (!Number.isFinite(ms) || ms < 0) return '—';

  const totalSeconds = Math.floor(ms / 1000);
  const days = Math.floor(totalSeconds / 86400);
  const hours = Math.floor((totalSeconds % 86400) / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = totalSeconds % 60;

  if (days > 0) return `${days}d ${hours}h`;
  if (hours > 0) return `${hours}h ${minutes}m`;
  if (minutes > 0) return `${minutes}m ${seconds}s`;
  return `${seconds}s`;
}

// ── Intelligence Tab ──

async function loadIntelligence() {
  const host = window.location.hostname;
  const dashPort = location.port;

  try {
    const res = await fetch(`http://${host}:${dashPort}/api/synthesis/state`);
    const state = await res.json();

    // Timestamp
    const tsEl = document.getElementById('intel-timestamp');
    if (tsEl) {
      tsEl.textContent = state.generatedAt
        ? `Last synthesis: ${new Date(state.generatedAt).toLocaleString()}`
        : 'No synthesis yet';
    }

    // Vitals
    const stats = state.brainStats || {};
    const setVal = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val ?? '—'; };
    setVal('iv-nodes', stats.nodes?.toLocaleString());
    setVal('iv-edges', stats.edges?.toLocaleString());
    setVal('iv-cycles', stats.cycles);
    setVal('iv-compiled', stats.documentsCompiled);

    // Self-Understanding
    const selfEl = document.getElementById('intel-self-content');
    if (selfEl && state.selfUnderstanding) {
      const su = state.selfUnderstanding;
      let html = `<p>${su.summary || 'No self-understanding yet.'}</p>`;
      if (su.relationship) {
        html += `<p style="margin-top:0.5rem;color:#93c5fd;font-size:0.85rem;">${su.relationship}</p>`;
      }
      if (su.currentObsessions && su.currentObsessions.length > 0) {
        html += `<div class="h23-intel-obsessions">${su.currentObsessions.map(o => `<span class="h23-intel-obsession">${o}</span>`).join('')}</div>`;
      }
      selfEl.innerHTML = html;
    } else if (selfEl) {
      selfEl.innerHTML = '<p class="h23-muted">Awaiting first synthesis run...</p>';
    }

    // Consolidated Insights
    const insightsEl = document.getElementById('intel-insights-list');
    if (insightsEl && state.consolidatedInsights && state.consolidatedInsights.length > 0) {
      insightsEl.innerHTML = state.consolidatedInsights.map(i => `
        <div class="h23-intel-insight">
          <div class="h23-intel-insight-title">${i.title || 'Untitled'}</div>
          <div class="h23-intel-insight-excerpt">${i.excerpt || ''}</div>
          <div class="h23-intel-insight-meta">
            ${i.source ? `Source: ${i.source}` : ''}
            ${i.themes ? i.themes.map(t => `<span class="h23-intel-insight-theme">${t}</span>`).join('') : ''}
          </div>
        </div>
      `).join('');
    } else if (insightsEl) {
      insightsEl.innerHTML = '<div class="h23-intel-card"><p class="h23-muted">No insights yet. Run synthesis to generate.</p></div>';
    }

    // Knowledge Index
    const indexEl = document.getElementById('intel-index-content');
    if (indexEl) {
      indexEl.textContent = state.knowledgeIndex || 'No compiled documents yet.';
    }

    // Recent Activity
    const activityEl = document.getElementById('intel-activity-list');
    if (activityEl && state.recentActivity && state.recentActivity.length > 0) {
      activityEl.innerHTML = state.recentActivity.map(a => `<li>${a}</li>`).join('');
    } else if (activityEl) {
      activityEl.innerHTML = '<li class="h23-muted">No recent activity.</li>';
    }
  } catch (err) {
    console.warn('[intel] Failed to load synthesis state:', err.message);
  }
}

function setupIntelSynthButton() {
  const btn = document.getElementById('intel-synth-btn');
  if (!btn) return;

  btn.addEventListener('click', async () => {
    btn.classList.add('running');
    btn.textContent = 'Running...';

    const host = window.location.hostname;
    const dashPort = location.port;

    try {
      await fetch(`http://${host}:${dashPort}/api/synthesis/run`, { method: 'POST' });
      // Poll for completion
      let attempts = 0;
      const poll = setInterval(async () => {
        attempts++;
        try {
          const res = await fetch(`http://${host}:${dashPort}/api/synthesis/state`);
          const state = await res.json();
          if (state.generatedAt && new Date(state.generatedAt).getTime() > Date.now() - 60_000) {
            clearInterval(poll);
            btn.classList.remove('running');
            btn.textContent = 'Run Synthesis';
            await loadIntelligence();
          }
        } catch { /* keep polling */ }
        if (attempts > 60) {
          clearInterval(poll);
          btn.classList.remove('running');
          btn.textContent = 'Run Synthesis';
        }
      }, 2000);
    } catch {
      btn.classList.remove('running');
      btn.textContent = 'Run Synthesis';
    }
  });
}

// ── Update Notification ──

async function checkUpdateNotification({ showEvenIfCurrent = false } = {}) {
  try {
    const res = await fetch('/home23/api/settings/update-status');
    if (!res.ok) return;
    const data = await res.json();
    const bar = document.getElementById('update-notification');
    const text = document.getElementById('update-notification-text');
    const versionLabel = document.getElementById('sidebar-version-label');
    const updateLine = document.getElementById('sidebar-update-line');
    const sidebarButton = document.getElementById('sidebar-update-action');

    if (versionLabel && data.currentVersion) {
      versionLabel.textContent = `Home23 v${data.currentVersion}`;
    }

    if (data.updateAvailable) {
      if (bar && text) {
        text.textContent = `Home23 v${data.latestVersion} available \u2014 run home23 update in your terminal`;
        bar.style.display = 'flex';
      }
      if (updateLine) updateLine.textContent = `Update ready: v${data.latestVersion}.`;
      if (sidebarButton) sidebarButton.textContent = 'Review update';
    } else {
      if (bar && text && showEvenIfCurrent) {
        text.textContent = `Home23 is current at v${data.currentVersion || data.latestVersion || 'current'}.`;
        bar.style.display = 'flex';
      }
      if (updateLine) updateLine.textContent = `Current version: v${data.currentVersion || data.latestVersion || 'current'}.`;
      if (sidebarButton) sidebarButton.textContent = 'Check for updates';
    }

    const dismissBtn = document.getElementById('update-dismiss');
    if (dismissBtn) {
      dismissBtn.onclick = () => { bar.style.display = 'none'; };
    }
  } catch { /* silent */ }
}

// ── Start ──

document.addEventListener('DOMContentLoaded', init);
