  var collapseBtn = document.getElementById('sidebar-collapse');
  if (collapseBtn) {
    collapseBtn.addEventListener('click', function() {
      var sb = document.getElementById('sidebar');
      if (sb) { sb.classList.toggle('collapsed'); collapseBtn.title = sb.classList.contains('collapsed') ? 'Expand sidebar' : 'Collapse sidebar'; }
    });
  }

  window.addEventListener('hashchange', onHash);

  var content = document.getElementById('content');
  if (content) content.addEventListener('click', function(e) {
    var evidenceToggleBtn = e.target.closest('[data-rec-evidence-toggle]');
    if (evidenceToggleBtn) {
      var evidenceId = evidenceToggleBtn.getAttribute('data-rec-evidence-toggle');
      var evidenceEl = evidenceId ? document.getElementById(evidenceId) : null;
      if (!evidenceEl) return;
      var isOpen = evidenceEl.style.display !== 'none';
      evidenceEl.style.display = isOpen ? 'none' : 'block';
      evidenceToggleBtn.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
      evidenceToggleBtn.textContent = (isOpen ? '\u25b6' : '\u25bc') + evidenceToggleBtn.textContent.slice(1);
      return;
    }
    var enableCacheBtn = e.target.closest('[data-action="enable-semantic-cache"]');
    if (enableCacheBtn) {
      if (enableCacheBtn.disabled) return;
      var modelName = enableCacheBtn.getAttribute('data-model') || 'model';
      var oldText = enableCacheBtn.textContent;
      enableCacheBtn.disabled = true;
      enableCacheBtn.textContent = 'Applying...';
      api('/api/config', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ cache: { enabled: true } })
      }).then(function() {
        if (!_cachedConfig) _cachedConfig = {};
        if (!_cachedConfig.cache) _cachedConfig.cache = {};
        _cachedConfig.cache.enabled = true;
        toast('Semantic caching enabled for runtime (' + modelName + ').');
        enableCacheBtn.textContent = 'Enabled';
      }).catch(function(err) {
        enableCacheBtn.disabled = false;
        enableCacheBtn.textContent = oldText;
        toast(err.message || 'Failed to enable semantic caching');
      });
      return;
    }
    var navBtn = e.target.closest('[data-page-nav]');
    if (navBtn) {
      var nextPage = navBtn.getAttribute('data-page-nav');
      if (nextPage === 'efficiency') App._effTab = 'performance';
      App.navigate(nextPage);
      return;
    }
    var graphNode = e.target.closest('[data-node-model]');
    if (graphNode) {
      App._modelGraphFocusModel = graphNode.getAttribute('data-node-model');
      App._modelGraphFocusEdge = null;
      App.navigate('efficiency');
      return;
    }
    var graphEdge = e.target.closest('[data-edge-key]');
    if (graphEdge) {
      App._modelGraphFocusEdge = graphEdge.getAttribute('data-edge-key');
      App._modelGraphFocusModel = null;
      App.navigate('efficiency');
      return;
    }
    if (e.target.closest('#model-graph-clear-focus')) {
      App._modelGraphFocusModel = null;
      App._modelGraphFocusEdge = null;
      App.navigate('efficiency');
      return;
    }
    if (e.target.closest('#routing-profile-reset')) {
      App._routingProfileDraft = normalizeRoutingProfile(App._routingProfileDefaults
        ? { correctness: App._routingProfileDefaults.correctness, cost: App._routingProfileDefaults.cost, speed: App._routingProfileDefaults.speed }
        : { correctness: 0.0, cost: 0.0, speed: 0.0 });
      App.navigate('efficiency');
      return;
    }
    if (e.target.closest('#routing-profile-apply')) {
      var applyBtn = e.target.closest('#routing-profile-apply');
      if (applyBtn) { applyBtn.disabled = true; applyBtn.textContent = 'Applying...'; }
      var patch = projectRoutingPatchFromProfile(App._routingProfileDraft || App._routingProfileDefaults || { correctness: 0, cost: 0, speed: 0 });
      api('/api/config', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(patch)
      }).then(function(resp) {
        App._routingProfileDefaults = normalizeRoutingProfile(App._routingProfileDraft || App._routingProfileDefaults || { correctness: 0, cost: 0, speed: 0 });
        App._routingProfileDraft = normalizeRoutingProfile(App._routingProfileDefaults);
        App._routingProfilePersistedAt = new Date().toISOString();
        App._routingProfilePersisted = !!(resp && resp.persisted);
        toast(App._routingProfilePersisted ? 'Routing profile applied and persisted.' : 'Routing profile applied.');
        App.navigate('efficiency');
      }).catch(function(err) {
        if (applyBtn) { applyBtn.disabled = false; applyBtn.textContent = 'Apply profile'; }
        toast(err.message || 'Failed to apply routing profile');
      });
      return;
    }
    if (e.target.closest('#budget-reset')) {
      App._contextBudgetDraft = normalizeContextBudget(App._contextBudgetDefaults || {});
      App.navigate('efficiency');
      return;
    }
    if (e.target.closest('#budget-apply')) {
      var bApplyBtn = e.target.closest('#budget-apply');
      if (bApplyBtn) { bApplyBtn.disabled = true; bApplyBtn.textContent = 'Applying...'; }
      var budgetDraft = normalizeContextBudget(App._contextBudgetDraft || App._contextBudgetDefaults || {});
      var bPatch = { context_budget: budgetDraft };
      api('/api/config', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(bPatch)
      }).then(function(resp) {
        App._contextBudgetDefaults = normalizeContextBudget(budgetDraft);
        App._contextBudgetDraft = normalizeContextBudget(budgetDraft);
        App._contextBudgetPersistedAt = new Date().toISOString();
        App._contextBudgetPersisted = !!(resp && resp.persisted);
        toast(App._contextBudgetPersisted ? 'Context budget applied and persisted.' : 'Context budget applied.');
        App.navigate('efficiency');
      }).catch(function(err) {
        if (bApplyBtn) { bApplyBtn.disabled = false; bApplyBtn.textContent = 'Apply budget'; }
        toast(err.message || 'Failed to apply context budget');
      });
      return;
    }
    if (e.target.closest('.channel-test-btn')) {
      var testBtn = e.target.closest('.channel-test-btn');
      var channelName = testBtn.getAttribute('data-test-channel');
      if (!channelName) return;
      testBtn.disabled = true; testBtn.textContent = 'Testing...';
      api('/api/channels/' + encodeURIComponent(channelName) + '/test', { method: 'POST' })
        .then(function(resp) {
          var ok = resp && resp.ok;
          var details = resp && resp.diagnostics && resp.diagnostics.details;
          toast(ok ? (channelName + ': ' + (details || 'OK')) : (channelName + ': ' + (details || 'Failed')));
          testBtn.disabled = false; testBtn.textContent = 'Test';
        })
        .catch(function(err) {
          toast(channelName + ' test failed: ' + (err.message || 'unknown error'));
          testBtn.disabled = false; testBtn.textContent = 'Test';
        });
      return;
    }
    if (e.target.closest('.integration-test-btn')) {
      var iBtn = e.target.closest('.integration-test-btn');
      var platform = iBtn.getAttribute('data-test-platform');
      if (!platform) return;
      iBtn.disabled = true; iBtn.textContent = 'Testing...';
      api('/api/channels/' + encodeURIComponent(platform) + '/test', { method: 'POST' })
        .then(function(resp) {
          var ok = resp && resp.ok;
          var details = resp && resp.diagnostics && resp.diagnostics.details;
          toast(ok ? (platform + ': ' + (details || 'OK')) : (platform + ': ' + (details || 'Failed')));
          iBtn.disabled = false; iBtn.textContent = 'Test';
        })
        .catch(function(err) {
          toast(platform + ' test failed: ' + (err.message || 'unknown error'));
          iBtn.disabled = false; iBtn.textContent = 'Test';
        });
      return;
    }
    if (e.target.closest('#dismiss-onboarding')) {
      window.localStorage.setItem('ic_dash_onboarding_dismissed', '1');
      var onboarding = document.getElementById('ov-onboarding');
      if (onboarding) onboarding.style.display = 'none';
      return;
    }
    var dismissHintBtn = e.target.closest('[data-dismiss-hint]');
    if (dismissHintBtn) {
      var hintId = dismissHintBtn.getAttribute('data-dismiss-hint');
      dismissHint(hintId);
      var hintNode = dismissHintBtn.closest('.hint-banner');
      if (hintNode) hintNode.remove();
      return;
    }
    if (e.target.closest('#btn-back-sessions')) {
      App._activeSession = null; App.navigate('sessions'); return;
    }
    if (e.target.closest('#sess-prev')) { App._sessionsPage = Math.max(0, (App._sessionsPage || 0) - 1); App.navigate('sessions'); return; }
    if (e.target.closest('#sess-next')) { App._sessionsPage = (App._sessionsPage || 0) + 1; App.navigate('sessions'); return; }
    if (e.target.closest('#mem-prev')) { App._memoryPage = Math.max(0, (App._memoryPage || 0) - 1); App.navigate('memory'); return; }
    if (e.target.closest('#mem-next')) { App._memoryPage = (App._memoryPage || 0) + 1; App.navigate('memory'); return; }
    if (e.target.closest('.session-delete-btn')) {
      var delId = e.target.closest('.session-delete-btn').getAttribute('data-delete-session');
      if (delId && confirm('Delete session ' + delId.substring(0, 8) + '…?')) {
        api('/api/sessions/' + encodeURIComponent(delId), { method: 'DELETE' }).then(function() { toast('Session deleted'); App.navigate('sessions'); }).catch(function(err) { toast(err.message || 'Failed'); });
      }
      return;
    }
    if (e.target.closest('.session-archive-btn')) {
      var archiveId = e.target.closest('.session-archive-btn').getAttribute('data-archive-session');
      if (archiveId && confirm('Archive session ' + archiveId.substring(0, 8) + '…?')) {
        api('/api/sessions/' + encodeURIComponent(archiveId) + '/archive', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ reason: 'Archived from sessions dashboard' })
        }).then(function() {
          toast('Session archived');
          App.navigate('sessions');
        }).catch(function(err) { toast(err.message || 'Failed'); });
      }
      return;
    }
    if (e.target.closest('#btn-new-session')) {
      App._resolveActiveAgentId().then(function(agentId) {
        return api('/api/sessions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ agent_id: agentId }) }).then(function(r) {
          var sid = r.session_id || r.id;
          App._activeSession = { id: sid, agent_id: agentId, agent_name: AGENT_DISPLAY_NAME || null };
          dismissHint('sessions-helper');
          try { window.localStorage.setItem('ic_sessions_helper_dismissed', '1'); } catch (_) {}
          toast('Session created');
          App.navigate('sessions');
        });
      }).catch(function(err) { toast(err.message || 'Failed'); });
      return;
    }
    if (e.target.closest('#btn-send-msg')) {
      if (App._sendingMessage) return;
      var input = document.getElementById('session-msg-input');
      var msg = input ? input.value.trim() : '';
      if (!msg || !App._activeSession) return;
      App._sendingMessage = true;
      input.value = '';
      input.disabled = true;
      var sendBtn = document.getElementById('btn-send-msg');
      if (sendBtn) { sendBtn.disabled = true; sendBtn.textContent = 'Thinking\u2026'; }
      var thread = document.querySelector('.message-thread');
      if (thread) {
        var userBubble = document.createElement('div');
        userBubble.className = 'message user';
        setHtml(userBubble, '<div class="message-role">user</div><div>' + renderSafeMarkdown(msg) + '</div>');
        thread.appendChild(userBubble);
        var thinkEl = document.createElement('div');
        thinkEl.className = 'thinking-indicator';
        thinkEl.id = 'thinking-bubble';
        setHtml(thinkEl, '<span class="thinking-brain">\uD83E\uDDE0</span><span class="thinking-dots"><span></span><span></span><span></span></span>');
        thread.appendChild(thinkEl);
        thread.scrollTop = thread.scrollHeight;
      }
      function unlockChat() {
        App._sendingMessage = false;
        var tb = document.getElementById('thinking-bubble');
        if (tb) tb.remove();
        if (input) { input.disabled = false; input.focus(); }
        if (sendBtn) { sendBtn.disabled = false; sendBtn.textContent = 'Send'; }
      }
      (async function streamMessage() {
        var asstBubble = null;
        var contentEl = null;
        var streamModel = '';
        var accumulated = '';
        var streamIntercepted = false;
        try {
          var resp = await fetch('/api/agent/message/stream', {
            method: 'POST',
            headers: authHeaders({ 'Content-Type': 'application/json' }),
            body: JSON.stringify({ content: msg, session_id: App._activeSession.id })
          });
          if (!resp.ok) {
            var errBody = await resp.text();
            try { var p = JSON.parse(errBody); throw new Error(p.error || errBody); } catch(ex) { if (ex.message) throw ex; throw new Error(errBody); }
          }
          var reader = resp.body.getReader();
          var decoder = new TextDecoder();
          var sseBuffer = '';
          while (true) {
            var result = await reader.read();
            if (result.done) break;
            sseBuffer += decoder.decode(result.value, { stream: true });
            var lines = sseBuffer.split('\n');
            sseBuffer = lines.pop() || '';
            for (var li = 0; li < lines.length; li++) {
              var line = lines[li].trim();
              if (!line.startsWith('data:')) continue;
              var payload = line.substring(5).trim();
              if (!payload || payload === '[DONE]') continue;
              try { var d = JSON.parse(payload); } catch(ex) { continue; }
              if (d.type === 'stream_start') {
                if (d.session_id) App._activeSession.id = d.session_id;
                App._liveStreamTurn = {
                  turn_id: d.turn_id || '',
                  session_id: d.session_id || App._activeSession.id || '',
                  model: d.model || ''
                };
                if (App.page === 'context') App.navigate('context');
                streamModel = d.model || '';
                var tb2 = document.getElementById('thinking-bubble');
                if (tb2) tb2.remove();
                if (thread) {
                  asstBubble = document.createElement('div');
                  asstBubble.className = 'message assistant';
                  var assistantLabel = sessionAssistantLabel(App._activeSession, 'assistant');
                  var roleHtml = '<div class="message-role">' + esc(assistantLabel);
                  if (streamModel) roleHtml += ' <span class="badge muted" style="font-size:0.5625rem;margin-right:0.375rem">' + esc(streamModel) + '</span>';
                  roleHtml += ' <span class="badge" style="font-size:0.5rem;background:var(--accent);color:#fff">streaming</span>';
                  roleHtml += '</div>';
                  contentEl = document.createElement('div');
                  contentEl.className = 'streaming-content';
                  setHtml(asstBubble, roleHtml);
                  asstBubble.appendChild(contentEl);
                  thread.appendChild(asstBubble);
                }
              } else if (d.type === 'chunk' && d.delta) {
                accumulated += d.delta;
                if (contentEl) { setHtml(contentEl, renderSafeMarkdown(accumulated)); thread.scrollTop = thread.scrollHeight; }
                if (sendBtn) sendBtn.textContent = 'Streaming\u2026';
              } else if (d.type === 'stream_end') {
                if (!d.turn_id || (App._liveStreamTurn && App._liveStreamTurn.turn_id === d.turn_id)) {
                  App._liveStreamTurn = null;
                  if (App.page === 'context') App.navigate('context');
                }
                // If stream was intercepted (retry/blocked), preserve the notice
                // instead of re-rendering empty accumulated content
                var wasIntercepted = d.aborted || d.reason === 'aborted' || d.reason === 'content_blocked' || streamIntercepted;
                if (asstBubble && !wasIntercepted) {
                  var endModel = d.model || streamModel;
                  var finalMeta = '';
                  if (endModel) finalMeta += '<span class="badge muted" style="font-size:0.5625rem;margin-right:0.375rem">' + esc(endModel) + '</span>';
                  if (d.tokens_in || d.tokens_out) finalMeta += '<span class="badge muted" style="font-size:0.5rem;margin-right:0.375rem">' + (d.tokens_in||0) + '/' + (d.tokens_out||0) + ' tokens</span>';
                  var assistantLabelFinal = sessionAssistantLabel(App._activeSession, 'assistant');
                  setHtml(asstBubble, '<div class="message-role">' + esc(assistantLabelFinal) + (finalMeta ? ' ' + finalMeta : '') + '</div><div>' + renderSafeMarkdown(accumulated) + '</div>');
                }
              } else if (d.type === 'stream_retry') {
                // Buffer-phase guard caught a problem before streaming started
                accumulated = '';
                streamIntercepted = true;
                if (contentEl) setHtml(contentEl, '<em style="color:var(--warning)">Response intercepted — retrying\u2026</em>');
                if (sendBtn) sendBtn.textContent = 'Retrying\u2026';
              } else if (d.type === 'stream_replace') {
                // Post-stream guard caught a late leak — replace streamed content
                accumulated = d.replacement || '';
                streamIntercepted = false; // replacement is the final content
                if (contentEl) setHtml(contentEl, renderSafeMarkdown(accumulated));
              } else if (d.type === 'stream_blocked') {
                // Content blocked by safety filter
                accumulated = '';
                streamIntercepted = true;
                if (contentEl) setHtml(contentEl, '<em style="color:var(--error)">Response blocked by safety filter.</em>');
              } else if (d.type === 'error') {
                streamIntercepted = true;
                if (contentEl) setHtml(contentEl, '<em style="color:var(--error)">' + esc(d.error || 'Provider error') + '</em>');
                toast(d.error || 'Stream error');
              }
            }
          }
        } catch(err) {
          var errMsg2 = err.message || 'Agent failed to respond';
          toast(errMsg2);
        }
        unlockChat();
      })();
      return;
    }
    var rosterCard = e.target.closest('.roster-card[data-roster-id]');
    if (rosterCard) {
      var agentId = rosterCard.getAttribute('data-roster-id');
      Promise.all([
        api('/api/roster'),
        App._loadAvailableModels({ nonBlocking: true, skipFetch: true }),
        api('/api/config').catch(function() { return { models: {} }; }),
        fetchWithFallback('/api/skills', { skills: [] }, 'skills')
      ]).then(function(arr) {
        var data = arr[0] || { roster: [] };
        var availableModels = arr[1] || [];
        var cfgModels = (arr[2] && arr[2].models) || {};
        var skillsData = (arr[3] && arr[3].data) ? arr[3].data : { skills: [] };
        var skillIdByName = {};
        (skillsData.skills || []).forEach(function(skill) {
          if (!skill || !skill.name || !skill.id) return;
          skillIdByName[String(skill.name).toLowerCase()] = String(skill.id);
        });
        var agent = (data.roster || []).find(function(a) { return (a.name || a.id) === agentId; });
        if (!agent) return;
        var modal = document.getElementById('roster-modal');
        if (!modal) return;
        var isCmd = agent.role === 'orchestrator';
        var color = agent.color || 'var(--accent)';
        var displayName = agent.display_name || agent.name;
        var stateColor = agent.state === 'Running' ? '#22c55e' : (agent.state === 'Error' ? '#ef4444' : (agent.state === 'Disabled' ? '#9baad6' : '#eab308'));
        var roleLabel = isCmd ? 'ORCHESTRATOR' : (agent.role === 'model-proxy' ? 'MODEL PROXY' : 'SUBAGENT');
        var orchestratorOrder = [];
        var orchestratorOrderState = [];
        var rosterDragModelIndex = null;
        function pushOrchestratorModel(v) {
          var s = String(v || '').trim();
          if (!s) return;
          if (orchestratorOrder.indexOf(s) === -1) orchestratorOrder.push(s);
        }
        if (isCmd) {
          pushOrchestratorModel(cfgModels.primary || agent.model);
          (cfgModels.fallbacks || []).forEach(pushOrchestratorModel);
          if (!orchestratorOrder.length) pushOrchestratorModel(agent.model);
          orchestratorOrderState = orchestratorOrder.slice();
        }
        function renderOrchestratorOrderRows(order) {
          if (!order.length) {
            return '<div class="card" style="padding:0.6rem;color:var(--muted)">No models added yet.</div>';
          }
          return order.map(function(name, idx) {
            var roleBadge = idx === 0
              ? '<span class="badge success" style="font-size:0.6rem">Primary</span>'
              : '<span class="badge muted" style="font-size:0.6rem">Fallback ' + idx + '</span>';
            var makePrimaryBtn = idx === 0 ? '' : '<button class="model-order-btn" data-roster-model-order-primary="' + idx + '">Make primary</button>';
            var removeBtn = '<button class="model-order-btn" data-roster-model-order-remove="' + idx + '">Remove</button>';
            return '<div class="model-order-item" draggable="true" data-roster-model-order-item="' + idx + '">'
              + '<div class="model-order-handle" title="Drag to reorder">\u22ee\u22ee</div>'
              + '<div class="model-order-name" title="' + esc(name) + '">' + esc(name) + '</div>'
              + roleBadge
              + '<div class="model-order-actions">' + makePrimaryBtn + '</div>'
              + removeBtn
              + '</div>';
          }).join('');
        }
        function rerenderOrchestratorOrderList() {
          var listEl = document.getElementById('roster-model-order-list');
          if (listEl) setHtml(listEl, renderOrchestratorOrderRows(orchestratorOrderState));
        }

        var voice = agent.voice || {};
        var voiceHtml = '';
        if (voice.formality || voice.proactiveness) {
          var attrs = [
            { label: 'Formality', val: voice.formality },
            { label: 'Proactive', val: voice.proactiveness },
            { label: 'Verbosity', val: voice.verbosity },
            { label: 'Humor', val: voice.humor },
            { label: 'Domain', val: voice.domain }
          ];
          voiceHtml = '<div style="margin-top:1rem"><div style="font-size:0.75rem;font-weight:600;color:var(--muted);letter-spacing:0.05em;margin-bottom:0.5rem">VOICE PROFILE</div>';
          attrs.forEach(function(a) {
            if (!a.val) return;
            var barVal = { balanced: 50, formal: 85, casual: 20, concise: 30, verbose: 80, suggest: 50, wait: 15, initiative: 90, dry: 25, robotic: 40, witty: 75 };
            var pct = barVal[a.val] || 50;
            voiceHtml += '<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:4px;font-size:0.75rem">'
              + '<span style="width:75px;color:var(--muted)">' + a.label + '</span>'
              + '<div style="flex:1;height:6px;background:var(--surface);border-radius:3px;overflow:hidden"><div style="width:' + pct + '%;height:100%;background:' + color + ';border-radius:3px"></div></div>'
              + '<span style="width:60px;text-align:right;color:var(--text)">' + esc(a.val) + '</span></div>';
          });
          voiceHtml += '</div>';
        }

        var missionsHtml = '';
        if (agent.missions && agent.missions.length > 0) {
          missionsHtml = '<div style="margin-top:1rem"><div style="font-size:0.75rem;font-weight:600;color:var(--muted);letter-spacing:0.05em;margin-bottom:0.5rem">MISSIONS</div>';
          agent.missions.forEach(function(m) {
            var prioColor = m.priority === 'high' ? '#ef4444' : (m.priority === 'medium' ? '#eab308' : '#22c55e');
            missionsHtml += '<div style="display:flex;align-items:baseline;gap:0.5rem;margin-bottom:0.4rem;font-size:0.8125rem">'
              + '<span style="width:6px;height:6px;border-radius:50%;background:' + prioColor + ';flex-shrink:0;margin-top:4px"></span>'
              + '<div><strong style="color:var(--text)">' + esc(m.name) + '</strong>'
              + (m.timeframe ? ' <span style="color:var(--muted);font-size:0.7rem">(' + esc(m.timeframe) + ')</span>' : '')
              + '<div style="color:var(--muted);font-size:0.75rem">' + esc(m.description || '') + '</div></div></div>';
          });
          missionsHtml += '</div>';
        }

        var skillsHtml = '';
        var skills = agent.skills || [];
        if (skills.length > 0) {
          var breakdown = agent.skill_breakdown || {};
          skillsHtml = '<div style="margin-top:1rem"><div style="font-size:0.75rem;font-weight:600;color:var(--muted);letter-spacing:0.05em;margin-bottom:0.5rem">SKILLS (' + skills.length + ')</div>';
          var kindColors = { tool: 'var(--accent)', cognitive: '#22c55e', format: '#06b6d4', multimodal: '#f59e0b', agent: '#8b5cf6' };
          var hasBreakdown = Object.keys(breakdown).length > 0;
          if (hasBreakdown) {
            Object.keys(breakdown).forEach(function(kind) {
              var items = breakdown[kind];
              var kc = kindColors[kind] || 'var(--muted)';
              skillsHtml += '<div style="margin-bottom:0.5rem"><span style="font-size:0.7rem;font-weight:600;color:' + kc + ';letter-spacing:0.03em">' + esc(kind.toUpperCase()) + '</span>'
                + '<div style="display:flex;flex-wrap:wrap;gap:4px;margin-top:3px">';
              items.forEach(function(s) {
                var skillName = String(s || '');
                var skillId = skillIdByName[skillName.toLowerCase()] || '';
                skillsHtml += '<span data-skill-open="' + esc(skillId) + '" data-skill-name="' + esc(skillName) + '" style="cursor:pointer;font-size:0.7rem;padding:1px 6px;border-radius:3px;background:rgba(255,255,255,0.05);color:var(--text);border:1px solid rgba(255,255,255,0.08)">' + esc(skillName) + '</span>';
              });
              skillsHtml += '</div></div>';
            });
          } else {
            skillsHtml += '<div style="display:flex;flex-wrap:wrap;gap:4px">';
            skills.forEach(function(s) {
              var name = typeof s === 'string' ? s : (s.name || String(s));
              var desc = (typeof s === 'object' && s.description) ? s.description : '';
              var skillId = skillIdByName[String(name).toLowerCase()] || '';
              skillsHtml += '<span data-skill-open="' + esc(skillId) + '" data-skill-name="' + esc(name) + '" title="' + esc(desc) + '" style="cursor:pointer;font-size:0.7rem;padding:1px 6px;border-radius:3px;background:rgba(255,255,255,0.05);color:var(--text);border:1px solid rgba(255,255,255,0.08)">' + esc(name) + '</span>';
            });
            skillsHtml += '</div>';
          }
          skillsHtml += '</div>';
        }

        var rulesHtml = '';
        if (agent.firmware_rules && agent.firmware_rules.length > 0) {
          rulesHtml = '<div style="margin-top:1rem"><div style="font-size:0.75rem;font-weight:600;color:var(--muted);letter-spacing:0.05em;margin-bottom:0.5rem">FIRMWARE RULES</div>';
          agent.firmware_rules.forEach(function(r) {
            var rColor = r.type === 'must' ? '#22c55e' : '#ef4444';
            var rLabel = r.type === 'must' ? 'MUST' : 'MUST NOT';
            rulesHtml += '<div style="display:flex;gap:0.5rem;margin-bottom:0.3rem;font-size:0.75rem;align-items:baseline">'
              + '<span style="font-weight:700;color:' + rColor + ';font-size:0.65rem;flex-shrink:0">' + rLabel + '</span>'
              + '<span style="color:var(--text)">' + esc(r.rule) + '</span></div>';
          });
          rulesHtml += '</div>';
        }

        var statsHtml = '';
        if (agent.stats) {
          var s = agent.stats;
          statsHtml = '<div style="margin-top:1rem;display:grid;grid-template-columns:repeat(2,1fr);gap:0.5rem">';
          var statItems = [
            { label: 'Subagents', val: s.subordinate_count },
            { label: 'Running', val: s.running_subordinates },
            { label: 'Total Skills', val: s.total_skills },
            { label: 'Enabled Skills', val: s.enabled_skills }
          ];
          statItems.forEach(function(si) {
            if (si.val == null) return;
            statsHtml += '<div style="text-align:center;padding:0.5rem;background:var(--surface);border-radius:var(--radius)">'
              + '<div style="font-size:1.25rem;font-weight:700;color:' + color + '">' + si.val + '</div>'
              + '<div style="font-size:0.65rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em">' + si.label + '</div></div>';
          });
          statsHtml += '</div>';
        }
        if (agent.session_count != null && !agent.stats) {
          statsHtml = '<div style="margin-top:1rem;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem">'
            + '<div style="text-align:center;padding:0.5rem;background:var(--surface);border-radius:var(--radius);flex:1">'
            + '<div style="font-size:1.25rem;font-weight:700;color:' + color + '">' + agent.session_count + '</div>'
            + '<div style="font-size:0.65rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em">Sessions</div></div>'
            + '<div style="text-align:center;padding:0.5rem;background:var(--surface);border-radius:var(--radius);flex:1">'
            + '<div style="font-size:0.82rem;font-weight:700;color:' + color + '">' + esc(formatTimestampLabel(agent.last_used_at)) + '</div>'
            + '<div style="font-size:0.65rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em">Last Used</div></div></div>';
        }

        var subordinates = agent.subordinates || [];
        var proxyLookup = {};
        (data.model_proxies || []).forEach(function(a) {
          if (!a) return;
          if (a.id) proxyLookup[String(a.id)] = true;
          if (a.name) proxyLookup[String(a.name)] = true;
        });
        subordinates = subordinates.filter(function(sid) { return !proxyLookup[String(sid)]; });
        var subsHtml = '';
        if (subordinates.length > 0) {
          subsHtml = '<div style="margin-top:1rem"><div style="font-size:0.75rem;font-weight:600;color:var(--muted);letter-spacing:0.05em;margin-bottom:0.5rem">SUBAGENTS (' + subordinates.length + ')</div>'
            + '<div style="display:flex;flex-wrap:wrap;gap:4px">';
          subordinates.forEach(function(sid) {
            subsHtml += '<span class="badge" style="cursor:pointer" data-roster-sub="' + esc(sid) + '">' + esc(sid) + '</span>';
          });
          subsHtml += '</div></div>';
        }

        var html = '<div style="max-width:520px;margin:auto;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden">'
          + '<div style="background:linear-gradient(135deg,' + color + '22,' + color + '08);padding:1.5rem;border-bottom:1px solid var(--border);position:relative">'
          + '<button id="roster-modal-close" style="position:absolute;top:12px;right:12px;background:none;border:none;color:var(--muted);cursor:pointer;font-size:1.2rem">\u2715</button>'
          + '<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.25rem">'
          + '<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:' + stateColor + '"></span>'
          + '<span style="font-size:0.65rem;font-weight:600;letter-spacing:0.05em;color:' + color + '">' + roleLabel + '</span>'
          + '</div>'
          + '<div style="font-size:1.5rem;font-weight:800;color:var(--text);margin-bottom:0.25rem">' + esc(displayName) + '</div>'
          + '<div style="font-family:var(--mono);font-size:0.75rem;color:var(--muted)">' + esc(agent.name || '') + '</div>'
          + '<div id="roster-model-display" style="display:flex;align-items:center;gap:0.5rem;margin-top:0.5rem">'
          + '<span style="font-family:var(--mono);font-size:0.75rem;color:' + color + '">' + esc(agent.model || '—') + '</span>'
          + '<button id="roster-change-model-btn" style="font-size:0.6rem;padding:1px 6px;border-radius:3px;background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.15);color:var(--muted);cursor:pointer" title="Change model">change</button>'
          + (!isCmd ? '<button id="roster-edit-subagent-btn" style="font-size:0.6rem;padding:1px 6px;border-radius:3px;background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.15);color:var(--muted);cursor:pointer" title="Edit agent">edit</button>' : '')
          + '</div>'
          + '<div id="roster-model-editor" style="display:none;margin-top:0.5rem">'
          + (function() {
            var allowControlModes = !isCmd && String(agent.role || '').toLowerCase() !== 'model-proxy';
            var opts = App._renderModelOptionsHtml({
              discoveredModels: availableModels,
              roster: data.roster || [],
              config: { models: cfgModels },
              selected: orchestratorOrderState.concat([agent.model || '']),
              includeControlModes: allowControlModes
            });
            if (isCmd) {
              return '<div style="font-size:0.6875rem;color:var(--muted);margin-bottom:0.375rem">Model order (top = primary, next = fallback sequence)</div>'
                + '<div class="model-order-list" id="roster-model-order-list">' + renderOrchestratorOrderRows(orchestratorOrderState) + '</div>'
                + '<div style="display:flex;gap:0.4rem;align-items:center;margin-top:6px">'
                + '<select id="roster-model-select" style="flex:1;font-size:0.75rem;font-family:var(--mono);padding:4px 6px;border-radius:4px;background:var(--surface);color:var(--text);border:1px solid var(--border)">'
                + '<option value="">-- quick add known model --</option>' + opts + '</select>'
                + '<button id="roster-model-order-add" class="btn secondary roster-model-add-btn" style="font-size:0.7rem;padding:3px 8px">Add</button>'
                + '</div>'
                + '<div style="display:flex;gap:0.4rem;align-items:center;margin-top:4px">'
                + '<input id="roster-model-custom" type="text" placeholder="or type provider/model and click Add" style="flex:1;font-size:0.75rem;font-family:var(--mono);padding:4px 6px;border-radius:4px;background:var(--surface);color:var(--text);border:1px solid var(--border)">'
                + '<button class="btn secondary roster-model-add-btn" style="font-size:0.7rem;padding:3px 8px">Add</button>'
                + '</div>'
                + '<div style="display:flex;gap:0.4rem;margin-top:6px">'
                + '<button id="roster-model-save" class="btn" style="font-size:0.7rem;padding:3px 10px">Apply</button>'
                + '<button id="roster-model-cancel" class="btn secondary" style="font-size:0.7rem;padding:3px 10px">Cancel</button>'
                + '</div>';
            }
            var selectedModel = String(agent.model || '');
            var singleOpts = App._buildModelOptionEntries({
              discoveredModels: availableModels,
              roster: data.roster || [],
              config: { models: cfgModels },
              selected: [selectedModel],
              includeControlModes: allowControlModes
            }).map(function(entry) {
              var sel = entry.value === selectedModel ? ' selected' : '';
              return '<option value="' + esc(entry.value) + '"' + sel + '>' + esc(entry.label) + '</option>';
            }).join('');
            return '<div style="display:flex;gap:0.4rem;align-items:center">'
              + '<select id="roster-model-select" style="flex:1;font-size:0.75rem;font-family:var(--mono);padding:4px 6px;border-radius:4px;background:var(--surface);color:var(--text);border:1px solid var(--border)">'
              + '<option value="">-- select model --</option>' + singleOpts + '</select>'
              + '</div>'
              + '<div style="display:flex;gap:0.4rem;align-items:center;margin-top:4px">'
              + '<input id="roster-model-custom" type="text" placeholder="or type provider/model" style="flex:1;font-size:0.75rem;font-family:var(--mono);padding:4px 6px;border-radius:4px;background:var(--surface);color:var(--text);border:1px solid var(--border)">'
              + '</div>'
              + '<div style="display:flex;gap:0.4rem;margin-top:6px">'
              + '<button id="roster-model-save" class="btn" style="font-size:0.7rem;padding:3px 10px">Apply</button>'
              + '<button id="roster-model-cancel" class="btn secondary" style="font-size:0.7rem;padding:3px 10px">Cancel</button>'
              + '</div>';
          })()
          + '</div>'
          + (agent.description ? '<div style="font-size:0.8125rem;color:var(--text);margin-top:0.75rem;line-height:1.4;max-height:4.5em;overflow:hidden;text-overflow:ellipsis">' + esc(agent.description) + '</div>' : '')
          + '</div>'
          + '<div style="padding:1.25rem">'
          + voiceHtml + missionsHtml + skillsHtml + rulesHtml + statsHtml + subsHtml
          + (agent.supervisor ? '<div style="margin-top:1rem;font-size:0.75rem;color:var(--muted)">Reports to: <strong style="color:var(--text)">' + esc(agent.supervisor) + '</strong></div>' : '')
          + '</div></div>';

        modal.style.display = 'flex';
        modal.style.alignItems = 'flex-start';
        modal.style.justifyContent = 'center';
        setHtml(modal, html);
        if (isCmd) rerenderOrchestratorOrderList();
        modal.onclick = function(ev) {
          if (ev.target === modal || ev.target.closest('#roster-modal-close')) {
            modal.style.display = 'none';
          }
          var subLink = ev.target.closest('[data-roster-sub]');
          if (subLink) {
            var subId = subLink.getAttribute('data-roster-sub');
            modal.style.display = 'none';
            var subCard = document.querySelector('.roster-card[data-roster-id="' + subId + '"]');
            if (subCard) subCard.click();
          }
          if (ev.target.closest('#roster-change-model-btn')) {
            var disp = document.getElementById('roster-model-display');
            var ed = document.getElementById('roster-model-editor');
            if (disp) disp.style.display = 'none';
            if (ed) ed.style.display = 'block';
          }
          if (ev.target.closest('#roster-edit-subagent-btn')) {
            App._agentsTab = 'list';
            App._pendingSubagentEdit = {
              name: agent.name || '',
              editing: agent.name || '',
              display: agent.display_name || '',
              model: agent.model || '',
              fallbacks: (agent.fallback_models || []).join(','),
              role: agent.role || 'subagent',
              skills: (agent.skills || []).join(','),
              desc: agent.description || ''
            };
            modal.style.display = 'none';
            App.navigate('agents');
            return;
          }
          if (ev.target.closest('#roster-model-cancel')) {
            var disp2 = document.getElementById('roster-model-display');
            var ed2 = document.getElementById('roster-model-editor');
            if (disp2) disp2.style.display = 'flex';
            if (ed2) ed2.style.display = 'none';
          }
          if (ev.target.closest('.roster-model-add-btn')) {
            var selAdd = document.getElementById('roster-model-select');
            var customAdd = document.getElementById('roster-model-custom');
            var toAdd = (customAdd && customAdd.value.trim()) || (selAdd && selAdd.value) || '';
            if (!toAdd) { toast('Select or enter a model'); return; }
            if (orchestratorOrderState.indexOf(toAdd) !== -1) { toast('Model already listed'); return; }
            orchestratorOrderState.push(toAdd);
            rerenderOrchestratorOrderList();
            if (customAdd) customAdd.value = '';
            if (selAdd) selAdd.value = '';
          }
          var removeOrderBtn = ev.target.closest('[data-roster-model-order-remove]');
          if (removeOrderBtn) {
            var removeIdx = Number(removeOrderBtn.getAttribute('data-roster-model-order-remove'));
            if (!isNaN(removeIdx) && removeIdx >= 0 && removeIdx < orchestratorOrderState.length) {
              orchestratorOrderState.splice(removeIdx, 1);
              rerenderOrchestratorOrderList();
            }
          }
          var primaryOrderBtn = ev.target.closest('[data-roster-model-order-primary]');
          if (primaryOrderBtn) {
            var promoteIdx = Number(primaryOrderBtn.getAttribute('data-roster-model-order-primary'));
            if (!isNaN(promoteIdx) && promoteIdx > 0 && promoteIdx < orchestratorOrderState.length) {
              var promoted = orchestratorOrderState.splice(promoteIdx, 1)[0];
              orchestratorOrderState.unshift(promoted);
              rerenderOrchestratorOrderList();
            }
          }
          if (ev.target.closest('#roster-model-save')) {
            var sel = document.getElementById('roster-model-select');
            var custom = document.getElementById('roster-model-custom');
            var agentKey = agent.name || agent.id;
            var payload;
            if (isCmd) {
              if (!orchestratorOrderState.length) { toast('Add at least one model in order list'); return; }
              payload = { model: orchestratorOrderState[0], fallbacks: orchestratorOrderState.slice(1) };
            } else {
              var newModel = (custom && custom.value.trim()) || (sel && sel.value) || '';
              if (!newModel) { toast('Select or enter a model'); return; }
              payload = { model: newModel };
            }
            api('/api/roster/' + encodeURIComponent(agentKey) + '/model', {
              method: 'PUT',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(payload)
            }).then(function(resp) {
              if (isCmd && Array.isArray(resp.model_order)) {
                toast('Model order updated (' + resp.model_order.length + ' total)');
              } else {
                toast('Model changed: ' + (resp.old_model || '?') + ' \u2192 ' + resp.new_model);
              }
              modal.style.display = 'none';
              App.navigate('agents');
            }).catch(function(err) {
              toast(err.message || 'Failed to change model');
            });
          }
        };
        modal.ondragstart = function(ev) {
          if (!isCmd) return;
          var item = ev.target.closest('[data-roster-model-order-item]');
          if (!item) return;
          rosterDragModelIndex = Number(item.getAttribute('data-roster-model-order-item'));
          item.classList.add('dragging');
          if (ev.dataTransfer) {
            ev.dataTransfer.effectAllowed = 'move';
            ev.dataTransfer.setData('text/plain', String(rosterDragModelIndex));
          }
        };
        modal.ondragover = function(ev) {
          if (!isCmd) return;
          var item = ev.target.closest('[data-roster-model-order-item]');
          if (!item) return;
          ev.preventDefault();
          if (ev.dataTransfer) ev.dataTransfer.dropEffect = 'move';
          modal.querySelectorAll('.model-order-item.drop-target').forEach(function(el) { el.classList.remove('drop-target'); });
          item.classList.add('drop-target');
        };
        modal.ondrop = function(ev) {
          if (!isCmd) return;
          var item = ev.target.closest('[data-roster-model-order-item]');
          if (!item) return;
          ev.preventDefault();
          var toIdx = Number(item.getAttribute('data-roster-model-order-item'));
          modal.querySelectorAll('.model-order-item.drop-target,.model-order-item.dragging').forEach(function(el) { el.classList.remove('drop-target'); el.classList.remove('dragging'); });
          var fromIdx = rosterDragModelIndex;
          rosterDragModelIndex = null;
          if (isNaN(fromIdx) || isNaN(toIdx) || fromIdx === toIdx || fromIdx < 0 || toIdx < 0 || fromIdx >= orchestratorOrderState.length || toIdx >= orchestratorOrderState.length) return;
          var moved = orchestratorOrderState.splice(fromIdx, 1)[0];
          orchestratorOrderState.splice(toIdx, 0, moved);
          rerenderOrchestratorOrderList();
        };
        modal.ondragend = function() {
          if (!isCmd) return;
          modal.querySelectorAll('.model-order-item.drop-target,.model-order-item.dragging').forEach(function(el) { el.classList.remove('drop-target'); el.classList.remove('dragging'); });
          rosterDragModelIndex = null;
        };
      });
      return;
    }
    if (e.target.closest('#btn-reload-skills')) {
      api('/api/skills/reload', { method: 'POST' }).then(function() { toast('Skills reloaded'); App.refreshSkills(); }).catch(function(err) { toast(err.message || 'Failed'); });
      return;
    }
    if (e.target.closest('#btn-create-skill')) {
      App._openSkillEditorModal(null, null);
      return;
    }
    var skillEditOpen = e.target.closest('[data-skill-edit-open]');
    if (skillEditOpen) {
      var skillEditOpenId = skillEditOpen.getAttribute('data-skill-edit-open') || '';
      if (!skillEditOpenId) { toast('Missing skill id'); return; }
      api('/api/skills/' + encodeURIComponent(skillEditOpenId)).then(function(skill) {
        App._openSkillEditorModal(skillEditOpenId, skill.source_content || '');
      }).catch(function(err) {
        toast(err.message || 'Failed to load skill');
      });
      return;
    }
    if (e.target.closest('#btn-catalog-refresh')) {
      App.refreshSkills();
      toast('Catalog refreshed');
      return;
    }
    var catalogInstallBtn = e.target.closest('#btn-catalog-install');
    var catalogActivateBtn = e.target.closest('#btn-catalog-activate');
    var catalogInstallActivateBtn = e.target.closest('#btn-catalog-install-activate');
    if (catalogInstallBtn || catalogActivateBtn || catalogInstallActivateBtn) {
      var selectedRows = Array.prototype.slice.call(document.querySelectorAll('.cat-skill-check:checked'))
        .map(function(el) {
          return {
            name: (el.value || '').trim(),
            installed: el.getAttribute('data-installed') === '1'
          };
        })
        .filter(function(v) { return !!v.name; });
      if (selectedRows.length === 0) {
        toast('Select at least one catalog skill');
        return;
      }
      if (catalogActivateBtn) {
        var selectedForActivate = selectedRows.map(function(row) { return row.name; });
        api('/api/skills/catalog/activate', {
          method: 'POST',
          body: { skills: selectedForActivate }
        }).then(function() {
          toast('Activated selected catalog skills');
          App.refreshSkills();
        }).catch(function(err) {
          toast(err.message || 'Catalog activation failed');
        });
        return;
      }
      var activateOnInstall = !!catalogInstallActivateBtn;
      var selectedForInstall = selectedRows.filter(function(row) { return !row.installed; }).map(function(row) { return row.name; });
      if (selectedForInstall.length === 0) {
        toast('All selected catalog skills are already installed');
        return;
      }
      var payload = { skills: selectedForInstall, activate: activateOnInstall };
      api('/api/skills/catalog/install', { method: 'POST', body: payload })
        .then(function(resp) {
          var installed = (resp && resp.installed) ? resp.installed.length : selectedForInstall.length;
          toast('Installed ' + installed + ' skill(s)' + (activateOnInstall ? ' and reloaded' : ''));
          if (!activateOnInstall) {
            return api('/api/skills/reload', { method: 'POST' }).then(function() {
              toast('Skills reloaded');
            });
          }
        })
        .then(function() {
          App.refreshSkills();
        })
        .catch(function(err) {
          toast(err.message || 'Catalog install failed');
        });
      return;
    }
    var pluginInstallBtn = e.target.closest('[data-plugin-install]');
    if (pluginInstallBtn) {
      var pluginName = pluginInstallBtn.getAttribute('data-plugin-install');
      if (!pluginName) { toast('Missing plugin name'); return; }
      pluginInstallBtn.disabled = true;
      pluginInstallBtn.textContent = 'Installing…';
      api('/api/skills/catalog/install', { method: 'POST', body: JSON.stringify({ skills: [pluginName], activate: true }) })
        .then(function(resp) {
          toast('Installed plugin: ' + pluginName + (resp.tools ? ' (' + resp.tools + ' tools)' : ''));
          App.refreshSkills();
        })
        .catch(function(err) {
          toast(err.message || 'Plugin install failed');
        })
        .finally(function() {
          pluginInstallBtn.disabled = false;
          pluginInstallBtn.textContent = 'Install';
        });
      return;
    }
    var skillDelete = e.target.closest('[data-skill-delete]');
    if (skillDelete) {
      var skillId = skillDelete.getAttribute('data-skill-delete');
      var skillName = skillDelete.getAttribute('data-skill-name') || '';
      if (!skillId || !skillName) { toast('Missing skill metadata'); return; }
      App._openSkillDeleteModal(skillId, skillName);
      return;
    }
    var skillOpen = e.target.closest('[data-skill-open]');
    if (skillOpen) {
      if (!e.target.closest('[data-skill-delete]') && !e.target.closest('[data-skill-toggle]') && !e.target.closest('[data-skill-edit-open]')) {
        var skillOpenId = skillOpen.getAttribute('data-skill-open') || '';
        var skillOpenName = skillOpen.getAttribute('data-skill-name') || '';
        App._openSkillDetailModal(skillOpenId, skillOpenName);
        return;
      }
    }
    var skillToggle = e.target.closest('[data-skill-toggle]');
    if (skillToggle) {
      var skillIdToggle = skillToggle.getAttribute('data-skill-toggle') || '';
      var skillNameToggle = skillToggle.getAttribute('data-skill-name') || skillIdToggle;
      var nextChecked = !!skillToggle.checked;
      var prevChecked = !nextChecked;
      if (!skillIdToggle) {
        skillToggle.checked = prevChecked;
        toast('Missing skill id');
        return;
      }
      if (skillToggle.getAttribute('data-skill-pending') === '1') return;
      skillToggle.setAttribute('data-skill-pending', '1');
      skillToggle.disabled = true;
      api('/api/skills/' + encodeURIComponent(skillIdToggle) + '/toggle', { method: 'PUT' })
        .then(function(resp) {
          skillToggle.checked = !!resp.enabled;
          toast((resp.enabled ? 'Skill enabled: ' : 'Skill disabled: ') + skillNameToggle);
          App.navigate('skills');
        })
        .catch(function(err) {
          skillToggle.checked = prevChecked;
          toast(err.message || 'Failed to toggle skill');
        })
        .finally(function() {
          skillToggle.removeAttribute('data-skill-pending');
          skillToggle.disabled = false;
        });
      return;
    }
    if (e.target.closest('#copy-address')) {
      var pre = document.getElementById('wallet-addr');
      if (pre && pre.textContent) navigator.clipboard.writeText(pre.textContent).then(function() { toast('Address copied'); });
      return;
    }
    var revenueTaskBtn = e.target.closest('[data-revenue-task-action][data-opportunity-id][data-revenue-task-kind]');
    if (revenueTaskBtn) {
      var taskKind = revenueTaskBtn.getAttribute('data-revenue-task-kind');
      var taskAction = revenueTaskBtn.getAttribute('data-revenue-task-action');
      var opportunityId = revenueTaskBtn.getAttribute('data-opportunity-id');
      if (!taskKind || !taskAction || !opportunityId) { toast('Missing revenue task metadata'); return; }
      var basePath = taskKind === 'tax' ? '/api/services/tax-payouts/' : '/api/services/swaps/';
      var request = { method: 'POST' };
      if (taskAction === 'confirm') {
        var txHash = window.prompt('Enter the transaction hash to confirm:', '');
        if (!txHash || !txHash.trim()) return;
        request.headers = { 'Content-Type': 'application/json' };
        request.body = JSON.stringify({ tx_hash: txHash.trim() });
      } else if (taskAction === 'fail') {
        var reason = window.prompt('Enter the failure reason:', '');
        if (!reason || !reason.trim()) return;
        request.headers = { 'Content-Type': 'application/json' };
        request.body = JSON.stringify({ reason: reason.trim() });
      } else if (taskAction === 'submit') {
        var calldata = window.prompt('Enter calldata (0x...) for submission:', '');
        if (!calldata || !calldata.trim()) return;
        var contractAddress = window.prompt('Optional contract address override (leave blank to use configured task source):', '') || '';
        request.headers = { 'Content-Type': 'application/json' };
        request.body = JSON.stringify({
          calldata: calldata.trim(),
          contract_address: contractAddress.trim() || null
        });
      }
      api(basePath + encodeURIComponent(opportunityId) + '/' + taskAction, request)
        .then(function(resp) {
          var msg = taskKind === 'tax' ? 'Tax payout ' : 'Swap ';
          msg += taskAction + ' completed';
          if (resp && resp.receipt_status) msg += ' — receipt ' + resp.receipt_status;
          if (resp && resp.tx_hash) msg += ' — ' + String(resp.tx_hash).slice(0, 18) + '…';
          toast(msg);
          App.navigate('wallet');
        })
        .catch(function(err) {
          toast(err.message || ('Revenue task ' + taskAction + ' failed'));
        });
      return;
    }
    if (e.target.closest('#memory-search-btn')) {
      var q = document.getElementById('memory-search-q'); var query = (q && q.value) || '';
      if (!query.trim()) { toast('Enter a search query'); return; }
      api('/api/memory/search?q=' + encodeURIComponent(query)).then(function(r) {
        var res = document.getElementById('memory-search-results'); if (!res) return;
        var results = r.results || [];
        setHtml(res, results.map(function(e) { return '<div class="card" style="margin-bottom:0.5rem"><div class="card-mono">' + esc(e.content || JSON.stringify(e)) + '</div><span class="badge muted">' + esc(e.type || '') + '</span></div>'; }).join('') || '<p style="color:var(--muted)">No results</p>');
      }).catch(function(err) { var res = document.getElementById('memory-search-results'); if (res) setHtml(res, '<p style="color:var(--error)">' + esc(err.message || 'Search failed') + '</p>'); });
      return;
    }
    var tabBtn = e.target.closest('.tabs button[data-tab]');
    if (tabBtn) { App._memoryTab = tabBtn.getAttribute('data-tab'); App._memoryCategory = ''; App._memorySessionId = ''; App.navigate('memory'); return; }
    var memCatBtn = e.target.closest('[data-mem-cat]');
    if (memCatBtn) { App._memoryCategory = memCatBtn.getAttribute('data-mem-cat'); App._memoryPage = 0; App.navigate('memory'); return; }
    var memSessBtn = e.target.closest('[data-mem-session]');
    if (memSessBtn) { App._memorySessionId = memSessBtn.getAttribute('data-mem-session'); App.navigate('memory'); return; }
    var effTabBtn = e.target.closest('[data-eff-tab]');
    if (effTabBtn) { App._effTab = effTabBtn.getAttribute('data-eff-tab') || 'performance'; App.navigate('efficiency'); return; }
    var effPeriodBtn = e.target.closest('[data-eff-period]');
    if (effPeriodBtn) { App._effPeriod = effPeriodBtn.getAttribute('data-eff-period'); App.navigate('efficiency'); return; }

    var agentsTab = e.target.closest('[data-agents-tab]');
    if (agentsTab) { App._agentsTab = agentsTab.getAttribute('data-agents-tab'); App.navigate('agents'); return; }

    var skillsTab = e.target.closest('[data-skills-tab]');
    if (skillsTab) { App._skillsTab = skillsTab.getAttribute('data-skills-tab'); App.navigate('skills'); return; }

    var metricsTabBtn = e.target.closest('[data-metrics-tab]');
    if (metricsTabBtn) { App._metricsTab = metricsTabBtn.getAttribute('data-metrics-tab') || 'overview'; App.navigate('metrics'); return; }

    // Pipeline Traces: Flow link click — render decision flow
    var flowLink = e.target.closest('.trace-flow-link');
    if (flowLink) {
      e.preventDefault();
      var flowTurnId = flowLink.getAttribute('data-flow-turn-id');
      if (flowTurnId && App.renderTraceFlow) {
        App._flowTurnId = flowTurnId;
        App.renderTraceFlow(flowTurnId).then(function(html) {
          var content = document.getElementById('content');
          if (content) setHtml(content, html);
        });
      }
      return;
    }
    // Flow back button
    var flowBackBtn = e.target.closest('#flow-back-btn');
    if (flowBackBtn) { App._flowTurnId = null; App.navigate('metrics'); return; }
    // SVG flow node click — show floating detail popover
    var svgNode = e.target.closest('.flow-svg-node');
    if (svgNode) {
      var idx = parseInt(svgNode.getAttribute('data-flow-idx'), 10);
      var dataEl = document.getElementById('flow-node-data');
      var popEl = document.getElementById('flow-popover');
      var popContent = document.getElementById('flow-popover-content');
      if (dataEl && popEl && popContent && !isNaN(idx)) {
        try {
          var allNodes = JSON.parse(dataEl.textContent);
          var n = allNodes[idx];
          if (!n) return;
          var detail = n.detail || {};
          var html = '<div style="font-weight:700;margin-bottom:0.5rem">' + esc(n.label || n.id) + '</div>'
            + '<div style="font-size:0.75rem;color:var(--muted);margin-bottom:0.5rem">'
            + esc((n.duration_ms || 0) + 'ms') + ' · ' + esc(n.status || 'executed') + '</div>';
          var keys = Object.keys(detail);
          if (keys.length) {
            keys.forEach(function(k) {
              var v = detail[k];
              var vStr = typeof v === 'object' ? JSON.stringify(v, null, 2) : String(v);
              html += '<div style="margin-bottom:0.35rem"><span style="color:var(--accent);font-weight:600">' + esc(k) + ':</span> '
                + '<span style="font-family:var(--font-mono);font-size:0.6875rem;word-break:break-all">' + esc(vStr.length > 300 ? vStr.substring(0, 297) + '...' : vStr) + '</span></div>';
            });
          } else {
            html += '<div style="color:var(--muted);font-style:italic">No annotations for this stage</div>';
          }
          html += '<div style="margin-top:0.5rem;text-align:right"><button class="btn secondary" id="flow-popover-close" style="font-size:0.6875rem;padding:0.2rem 0.5rem">Close</button></div>';
          setHtml(popContent, html);
          // Position near the clicked node
          var rect = svgNode.getBoundingClientRect();
          var container = document.getElementById('flow-svg-container');
          var cRect = container ? container.getBoundingClientRect() : rect;
          popEl.style.display = 'block';
          popEl.style.left = (rect.right - cRect.left + 12) + 'px';
          popEl.style.top = (rect.top - cRect.top - 10) + 'px';
        } catch(ex) {}
      }
      return;
    }
    // Close flow popover
    var popClose = e.target.closest('#flow-popover-close');
    if (popClose) { var pop = document.getElementById('flow-popover'); if (pop) pop.style.display = 'none'; return; }
    // Click outside popover closes it
    if (!e.target.closest('#flow-popover') && !e.target.closest('.flow-svg-node')) {
      var pop2 = document.getElementById('flow-popover'); if (pop2) pop2.style.display = 'none';
    }

    // Pipeline Traces: turn row click — toggle waterfall
    var traceRow = e.target.closest('.traces-turn-row');
    if (traceRow) {
      var tId = traceRow.getAttribute('data-turn-id');
      var wfRow = document.getElementById('wf-' + tId);
      if (!wfRow) return;
      var isOpen = wfRow.style.display !== 'none';
      if (isOpen) { wfRow.style.display = 'none'; return; }
      wfRow.style.display = '';
      var wfContent = wfRow.querySelector('.wf-content');
      if (wfContent && wfContent.getAttribute('data-loaded')) return;
      api('/api/observability/traces/' + encodeURIComponent(tId) + '/waterfall').catch(function() { return { stages: [] }; }).then(function(wd) {
        var stages = wd.waterfall || wd.stages || [];
        if (!stages.length) { if (wfContent) setHtml(wfContent, '<span style="color:var(--muted)">No stages recorded.</span>'); return; }
        var maxMs = Math.max.apply(null, stages.map(function(s) { return s.duration_ms || 1; }));
        var svgH = stages.length * 28 + 10;
        var html = '<svg width="100%" height="' + svgH + '" style="display:block">';
        stages.forEach(function(s, i) {
          var w = Math.max(((s.duration_ms || 0) / maxMs) * 75, 2);
          var color = s.outcome === 'Ok' ? '#22c55e' : s.outcome === 'Skipped' ? '#6b7280' : '#ef4444';
          var y = i * 28 + 5;
          html += '<rect x="20%" y="' + y + '" width="' + w + '%" height="22" rx="3" fill="' + color + '" opacity="0.8"/>';
          html += '<text x="2%" y="' + (y + 15) + '" font-size="11" fill="var(--text)" font-family="var(--mono)">' + esc(s.name || '') + '</text>';
          html += '<text x="' + (22 + w) + '%" y="' + (y + 15) + '" font-size="10" fill="var(--muted)" font-family="var(--mono)">' + (s.duration_ms || 0) + 'ms</text>';
        });
        html += '</svg>';
        // ── Planner Decision Card ─────────────────────────────────────────────
        // Collect task_state.* and delegation.* annotations across all stages.
        var tsAnnotations = {};
        stages.forEach(function(s) {
          var ann = s.annotations || {};
          Object.keys(ann).forEach(function(k) {
            if (k.indexOf('task_state.') === 0 || k.indexOf('delegation.') === 0) {
              tsAnnotations[k] = ann[k];
            }
          });
        });
        var tsClassification = tsAnnotations['task_state.inputs.classification'] || tsAnnotations['delegation.task_mode'];
        var tsAction = tsAnnotations['task_state.selected.action'] || tsAnnotations['delegation.planner_next_action'];
        var tsRationale = tsAnnotations['task_state.selected.rationale'] || tsAnnotations['delegation.planner_rationale'];
        var tsConfidence = tsAnnotations['task_state.candidates.count'];
        if (tsClassification != null || tsAction != null || tsRationale != null) {
          var plannerCard = '<div style="margin-top:0.75rem;padding:0.65rem 0.85rem;background:var(--surface-2);border:1px solid var(--border-ghost);border-left:3px solid var(--accent);border-radius:var(--radius)">'
            + '<div style="font-family:var(--font-headline);font-size:0.5625rem;font-weight:700;letter-spacing:0.15em;text-transform:uppercase;color:var(--accent);margin-bottom:0.5rem">Planner Decision</div>'
            + '<div style="display:flex;flex-wrap:wrap;gap:0.75rem 1.5rem">'
            + (tsClassification != null ? '<div><div style="font-size:0.625rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.1em;margin-bottom:0.15rem">Classification</div><div style="font-size:0.8125rem;color:var(--text);font-weight:600">' + esc(String(tsClassification)) + '</div></div>' : '')
            + (tsAction != null ? '<div><div style="font-size:0.625rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.1em;margin-bottom:0.15rem">Selected Action</div><div style="font-size:0.8125rem;color:var(--tertiary);font-weight:600;font-family:var(--font-mono)">' + esc(String(tsAction)) + '</div></div>' : '')
            + (tsConfidence != null ? '<div><div style="font-size:0.625rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.1em;margin-bottom:0.15rem">Candidates</div><div style="font-size:0.8125rem;color:var(--secondary);font-weight:600">' + esc(String(tsConfidence)) + '</div></div>' : '')
            + '</div>'
            + (tsRationale != null ? '<div style="margin-top:0.5rem;font-size:0.75rem;color:var(--muted);line-height:1.5;border-top:1px solid var(--border-ghost);padding-top:0.4rem"><span style="color:var(--outline);font-size:0.625rem;text-transform:uppercase;letter-spacing:0.1em;font-weight:600">Rationale — </span>' + esc(String(tsRationale)) + '</div>' : '')
            + '</div>';
          html += plannerCard;
        }
        if (wfContent) { setHtml(wfContent, html); wfContent.setAttribute('data-loaded', '1'); }
      });
      return;
    }

    // Flight Recorder: load button
    if (e.target.closest('#flight-load-btn')) {
      var flInput = document.getElementById('flight-turn-input');
      var flTurn = (flInput && flInput.value || '').trim();
      if (!flTurn) { toast('Enter a turn ID'); return; }
      App._flightTurnId = flTurn;
      App.navigate('metrics');
      return;
    }

    var settingsMode = e.target.closest('[data-settings-mode]');
    if (settingsMode) {
      var newMode = settingsMode.getAttribute('data-settings-mode');
      if (newMode === 'raw' && App._settingsMode !== 'raw') App._settingsRawText = null;
      if (App._settingsMode === 'raw' && newMode !== 'raw') App._settingsRawText = null;
      if (newMode === 'models') App._initModelOrderFromDraft();
      App._settingsMode = newMode; App.navigate('settings'); return;
    }
    if (e.target.closest('#hints-reset-dismissed')) {
      clearHintDismissals();
      setHintsEnabled(true);
      try { window.localStorage.setItem(HINTS_PROMPTED_KEY, '1'); } catch (_) {}
      toast('Dismissed hints reset');
      App.navigate('settings');
      return;
    }
    if (e.target.closest('#trusted-sender-add')) {
      var senderInput = document.getElementById('trusted-sender-input');
      var sender = (senderInput && senderInput.value || '').trim();
      if (!sender) { toast('Enter a sender id first'); return; }
      var draftTs = App._getSettingsDraft();
      if (!draftTs.channels) draftTs.channels = {};
      var curTs = Array.isArray(draftTs.channels.trusted_sender_ids) ? draftTs.channels.trusted_sender_ids.slice() : [];
      if (curTs.indexOf(sender) !== -1) { toast('Sender already trusted'); return; }
      curTs.push(sender);
      draftTs.channels.trusted_sender_ids = curTs;
      if (senderInput) senderInput.value = '';
      App._settingsDirty = JSON.stringify(draftTs) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    var removeTrustedSenderBtn = e.target.closest('.trusted-sender-remove[data-trusted-sender]');
    if (removeTrustedSenderBtn) {
      var senderVal = removeTrustedSenderBtn.getAttribute('data-trusted-sender');
      var draftTr = App._getSettingsDraft();
      if (!draftTr.channels) draftTr.channels = {};
      var curTr = Array.isArray(draftTr.channels.trusted_sender_ids) ? draftTr.channels.trusted_sender_ids.slice() : [];
      draftTr.channels.trusted_sender_ids = curTr.filter(function(v) { return String(v) !== String(senderVal); });
      App._settingsDirty = JSON.stringify(draftTr) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    // ── Generic channel allow-list add/remove handlers ──
    var CH_META = {
      telegram: { listField: 'allowed_chat_ids', validate: function(v) { return /^-?[0-9]+$/.test(v) ? null : 'Chat id must be an integer'; }, coerce: Number },
      whatsapp: { listField: 'allowed_numbers', validate: null, coerce: String },
      signal:   { listField: 'allowed_numbers', validate: null, coerce: String },
      discord:  { listField: 'allowed_guild_ids', validate: null, coerce: String },
      email:    { listField: 'allowed_senders', validate: null, coerce: String }
    };
    var chAddBtn = e.target.closest('.ch-list-add[data-ch-name]');
    if (chAddBtn) {
      var chName = chAddBtn.getAttribute('data-ch-name');
      var chField = chAddBtn.getAttribute('data-ch-field');
      var chMeta = CH_META[chName] || { listField: chField, validate: null, coerce: String };
      var chInput = document.querySelector('.ch-list-input[data-ch-name="' + chName + '"][data-ch-field="' + chField + '"]');
      var chRaw = (chInput && chInput.value || '').trim();
      if (!chRaw) { toast('Enter a value first'); return; }
      if (chMeta.validate) { var err = chMeta.validate(chRaw); if (err) { toast(err); return; } }
      var chCoerced = chMeta.coerce ? chMeta.coerce(chRaw) : chRaw;
      var draftCh = App._getSettingsDraft();
      if (!draftCh.channels) draftCh.channels = {};
      if (!draftCh.channels[chName] || typeof draftCh.channels[chName] !== 'object') draftCh.channels[chName] = {};
      var chList = Array.isArray(draftCh.channels[chName][chField]) ? draftCh.channels[chName][chField].slice() : [];
      var chExists = chList.some(function(v) { return String(v) === String(chCoerced); });
      if (chExists) { toast('Value already in list'); return; }
      chList.push(chCoerced);
      draftCh.channels[chName][chField] = chList;
      if (chInput) chInput.value = '';
      App._settingsDirty = JSON.stringify(draftCh) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    var chRemoveBtn = e.target.closest('.ch-list-remove[data-ch-name]');
    if (chRemoveBtn) {
      var chName = chRemoveBtn.getAttribute('data-ch-name');
      var chField = chRemoveBtn.getAttribute('data-ch-field');
      var chRemoveVal = chRemoveBtn.getAttribute('data-ch-value');
      var draftChR = App._getSettingsDraft();
      if (!draftChR.channels) draftChR.channels = {};
      if (!draftChR.channels[chName] || typeof draftChR.channels[chName] !== 'object') draftChR.channels[chName] = {};
      var chCur = Array.isArray(draftChR.channels[chName][chField]) ? draftChR.channels[chName][chField].slice() : [];
      draftChR.channels[chName][chField] = chCur.filter(function(v) { return String(v) !== String(chRemoveVal); });
      App._settingsDirty = JSON.stringify(draftChR) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    if (e.target.closest('#model-order-add-btn')) {
      var addInput = document.getElementById('model-order-add-input');
      var modelName = (addInput && addInput.value || '').trim();
      if (!modelName) { toast('Enter a model name'); return; }
      if (!App._settingsModelOrder) App._initModelOrderFromDraft();
      if (App._settingsModelOrder.indexOf(modelName) !== -1) { toast('Model already in order list'); return; }
      App._settingsModelOrder.push(modelName);
      App._syncModelOrderToDraft();
      App.navigate('settings');
      return;
    }
    if (e.target.closest('#revenue-swap-chain-add-btn')) {
      var chainInput = document.getElementById('revenue-swap-chain-add');
      var chainName = (chainInput && chainInput.value || '').trim().toUpperCase();
      if (!chainName) { toast('Enter a chain name'); return; }
      var draftSwapAdd = App._getSettingsDraft();
      if (!draftSwapAdd.treasury) draftSwapAdd.treasury = {};
      if (!draftSwapAdd.treasury.revenue_swap) draftSwapAdd.treasury.revenue_swap = {};
      if (!Array.isArray(draftSwapAdd.treasury.revenue_swap.chains)) draftSwapAdd.treasury.revenue_swap.chains = [];
      var exists = draftSwapAdd.treasury.revenue_swap.chains.some(function(entry) {
        return String((entry && entry.chain) || '').trim().toUpperCase() === chainName;
      });
      if (exists) { toast('Chain already configured'); return; }
      draftSwapAdd.treasury.revenue_swap.chains.push({
        chain: chainName,
        target_contract_address: '',
        swap_contract_address: ''
      });
      if (chainInput) chainInput.value = '';
      App._settingsDirty = JSON.stringify(draftSwapAdd) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    var revenueSwapRemoveBtn = e.target.closest('[data-revenue-swap-remove-chain]');
    if (revenueSwapRemoveBtn) {
      var removeIdx = Number(revenueSwapRemoveBtn.getAttribute('data-revenue-swap-remove-chain'));
      var draftSwapRemove = App._getSettingsDraft();
      var swapChains = draftSwapRemove && draftSwapRemove.treasury && draftSwapRemove.treasury.revenue_swap && Array.isArray(draftSwapRemove.treasury.revenue_swap.chains)
        ? draftSwapRemove.treasury.revenue_swap.chains
        : null;
      if (!swapChains || removeIdx < 0 || removeIdx >= swapChains.length) return;
      swapChains.splice(removeIdx, 1);
      App._settingsDirty = JSON.stringify(draftSwapRemove) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    var modelPrimaryBtn = e.target.closest('[data-model-order-make-primary]');
    if (modelPrimaryBtn) {
      var pIdx = Number(modelPrimaryBtn.getAttribute('data-model-order-make-primary'));
      if (!App._settingsModelOrder || pIdx <= 0 || pIdx >= App._settingsModelOrder.length) return;
      var primaryName = App._settingsModelOrder.splice(pIdx, 1)[0];
      App._settingsModelOrder.unshift(primaryName);
      App._syncModelOrderToDraft();
      App.navigate('settings');
      return;
    }
    var modelRemoveBtn = e.target.closest('[data-model-order-remove]');
    if (modelRemoveBtn) {
      var rIdx = Number(modelRemoveBtn.getAttribute('data-model-order-remove'));
      if (!App._settingsModelOrder || rIdx < 0 || rIdx >= App._settingsModelOrder.length) return;
      App._settingsModelOrder.splice(rIdx, 1);
      App._syncModelOrderToDraft();
      App.navigate('settings');
      return;
    }
    if (e.target.closest('#settings-save') || e.target.closest('#settings-apply')) {
      var isApply = !!e.target.closest('#settings-apply');
      if (App._settingsMode === 'raw') {
        api('/api/config/raw', { method: 'PUT', headers: { 'Content-Type': 'text/plain' }, body: App._settingsRawText || '' }).then(function() {
          App._settingsDraft = null;
          App._settingsDirty = false;
          App._settingsRawText = null;
          toast(isApply ? 'Configuration applied from TOML' : 'Configuration saved from TOML');
          App.navigate('settings');
        }).catch(function(err) { toast(err.message || 'Save failed'); });
        return;
      }
      var draft = App._getSettingsDraft();
      var patch = App._buildMutableConfigPatch(draft);
      api('/api/config', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(patch) }).then(function() {
        var merged = JSON.parse(JSON.stringify(_cachedConfig || {}));
        Object.keys(patch || {}).forEach(function(k) { merged[k] = patch[k]; });
        _cachedConfig = merged;
        App._settingsDirty = false; App._settingsRawText = null;
        toast(isApply ? 'Configuration applied' : 'Configuration saved');
        App.navigate('settings');
      }).catch(function(err) { toast(err.message || 'Save failed'); });
      return;
    }
    if (e.target.closest('#settings-cancel')) {
      App._settingsDraft = null; App._settingsRawText = null; App._settingsDirty = false;
      toast('Changes discarded'); App.navigate('settings'); return;
    }
    var chainBtn = e.target.closest('[data-chain-preset]');
    if (chainBtn) {
      var name = chainBtn.getAttribute('data-chain-preset');
      var preset = App._CHAIN_PRESETS[name];
      if (preset) {
        var draft = App._getSettingsDraft();
        App._setNestedValue(draft, 'wallet.chain_id', preset.chain_id);
        App._setNestedValue(draft, 'wallet.rpc_url', preset.rpc_url);
        App._settingsDirty = JSON.stringify(draft) !== JSON.stringify(_cachedConfig);
        App._settingsRawText = null;
        App.navigate('settings');
      }
      return;
    }
    if (e.target.closest('.channel-enable-btn')) {
      var channelName = e.target.closest('.channel-enable-btn').getAttribute('data-enable-channel');
      if (!channelName) return;
      var draft = App._getSettingsDraft();
      if (!draft.channels) draft.channels = {};
      var defaults = { telegram: { enabled: true, token_env: 'TELEGRAM_BOT_TOKEN' }, discord: { enabled: true, token_env: 'DISCORD_BOT_TOKEN' }, whatsapp: { enabled: true, token_env: 'WHATSAPP_TOKEN' }, signal: { enabled: true, phone_number: '', daemon_url: 'http://localhost:8080' }, email: { enabled: true, imap_host: '', smtp_host: '' }, web: { enabled: true }, voice: { enabled: true } };
      draft.channels[channelName] = defaults[channelName] || { enabled: true };
      App._settingsDirty = true; App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    if (e.target.closest('#add-provider')) {
      var providerInput = document.getElementById('add-provider-input');
      var name = (providerInput && providerInput.value || '');
      if (!name || !name.trim()) return;
      name = name.trim().toLowerCase();
      var draft = App._getSettingsDraft();
      if (!draft.providers) draft.providers = {};
      if (draft.providers[name]) { toast('Provider "' + name + '" already exists'); return; }
      draft.providers[name] = { url: '', tier: 'T2', format: 'openai', chat_path: '/v1/chat/completions' };
      if (providerInput) providerInput.value = '';
      App._settingsDirty = true; App._settingsRawText = null;
      App.navigate('settings');
      return;
    }
    if (e.target.id === 'keystore-unlock-btn') {
      e.target.disabled = true; e.target.textContent = 'Unlocking\u2026';
      api('/api/keystore/unlock', { method: 'POST' }).then(function(res) {
        toast(res.action === 'already_unlocked' ? 'Keystore already unlocked' : 'Keystore unlocked');
        App._settingsCache = null; App.navigate('settings');
      }).catch(function(err) {
        toast('Unlock failed: ' + (err.message || 'unknown error'));
        e.target.disabled = false; e.target.textContent = 'Unlock keystore';
      });
      return;
    }
    var keyBtn = e.target.closest('[data-action="save-key"]') || e.target.closest('[data-action="remove-key"]');
    if (keyBtn) {
      var row = keyBtn.closest('.key-manage-row');
      var provName = row && row.getAttribute('data-provider');
      var msgEl = row && row.querySelector('.key-manage-msg');
      if (!provName) return;
      var action = keyBtn.getAttribute('data-action');
      if (action === 'save-key') {
        var inp = row.querySelector('.key-input');
        var val = inp && inp.value.trim();
        if (!val) { toast('Paste an API key first'); return; }
        keyBtn.disabled = true; keyBtn.textContent = 'Saving\u2026';
        api('/api/providers/' + encodeURIComponent(provName) + '/key', {
          method: 'PUT', body: JSON.stringify({ api_key: val })
        }).then(function() {
          toast('Key saved for ' + provName);
          App._settingsCache = null; App.navigate('settings');
        }).catch(function(err) {
          var msg = (err.message || 'Save failed');
          if (msg.indexOf('locked') !== -1) msg = 'Keystore is locked \u2014 unlock it first';
          if (msgEl) { msgEl.textContent = msg; msgEl.style.color = 'var(--error)'; }
          keyBtn.disabled = false; keyBtn.textContent = 'Save to keystore';
        });
      } else if (action === 'remove-key') {
        if (!confirm('Remove ' + provName + ' API key from keystore?')) return;
        keyBtn.disabled = true; keyBtn.textContent = 'Removing\u2026';
        api('/api/providers/' + encodeURIComponent(provName) + '/key', {
          method: 'DELETE'
        }).then(function() {
          toast('Key removed for ' + provName);
          App._settingsCache = null; App.navigate('settings');
        }).catch(function(err) {
          if (msgEl) { msgEl.textContent = err.message || 'Remove failed'; msgEl.style.color = 'var(--error)'; }
          keyBtn.disabled = false; keyBtn.textContent = 'Remove';
        });
      }
      return;
    }

    var calNav = e.target.closest('[data-cal-nav]');
    if (calNav) {
      var dir = calNav.getAttribute('data-cal-nav');
      if (dir === 'prev') { App._calMonth--; if (App._calMonth < 0) { App._calMonth = 11; App._calYear--; } }
      else if (dir === 'next') { App._calMonth++; if (App._calMonth > 11) { App._calMonth = 0; App._calYear++; } }
      else { App._calMonth = new Date().getMonth(); App._calYear = new Date().getFullYear(); }
      App._calSelected = null; App.navigate('scheduler'); return;
    }
    if (e.target.closest('[data-cal-add]')) { App._calModalMode = 'add'; App._calEditJob = null; App.navigate('scheduler'); return; }
    var editBtn = e.target.closest('[data-cal-edit]');
    if (editBtn) {
      var idx = Number(editBtn.getAttribute('data-cal-edit')); var j = _cachedCronJobs[idx];
      if (j) { App._calModalMode = 'edit'; App._calEditJob = { name: j.name, description: (j.description || App._cronIntentFromJob(j) || ''), schedule_kind: j.schedule_kind, schedule_expr: j.schedule_expr, _idx: idx, _origName: j.name }; App.navigate('scheduler'); }
      return;
    }
    var delBtn = e.target.closest('[data-cal-delete]');
    var runBtn = e.target.closest('[data-cal-run]');
    if (runBtn) {
      var runIdx = Number(runBtn.getAttribute('data-cal-run')); var rj = _cachedCronJobs[runIdx];
      if (rj && rj.id) {
        api('/api/cron/jobs/' + encodeURIComponent(rj.id) + '/run', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
          .then(function(resp) {
            var msg = (resp.status === 'success' ? 'Ran ' : 'Run failed: ') + rj.name + (resp.error ? ' — ' + resp.error : '');
            if (resp.output_text) msg += ' — ' + String(resp.output_text).replace(/\s+/g, ' ').trim().slice(0, 160);
            toast(msg);
            App.navigate('scheduler');
          })
          .catch(function(err) { toast(err.message || 'Run failed'); });
      }
      return;
    }
    if (delBtn) {
      var idx = Number(delBtn.getAttribute('data-cal-delete')); var j = _cachedCronJobs[idx];
      if (j) {
        if (!confirm('Delete scheduled job "' + j.name + '" and its run history?')) return;
        api('/api/cron/jobs/' + encodeURIComponent(j.id), { method: 'DELETE' }).then(function() { toast('Deleted "' + j.name + '"'); App.navigate('scheduler'); }).catch(function(err) { toast(err.message || 'Delete failed'); App.navigate('scheduler'); });
      }
      return;
    }
    if (e.target.closest('[data-cal-modal-close]') || (e.target.closest('[data-cal-overlay]') && e.target.hasAttribute('data-cal-overlay'))) { App._calModalMode = null; App._calEditJob = null; App.navigate('scheduler'); return; }
    var dayBtn = e.target.closest('[data-cal-day]');
    if (dayBtn) { dayBtn.classList.toggle('active'); var sched = App._readSchedFromUI(); var sumEl = document.getElementById('cal-sched-summary'); if (sumEl) sumEl.textContent = App._schedSummary(sched); return; }
    if (e.target.closest('#cal-modal-save')) {
      var nameEl = document.getElementById('cal-job-name'); var name = (nameEl && nameEl.value || '').trim();
      if (!name) { toast('Name is required'); return; }
      var sched = App._readSchedFromUI(); var cron = App._schedToCron(sched);
      var descEl = document.getElementById('cal-job-description'); var intent = (descEl && descEl.value || '').trim();
      var payloadObj = intent ? { action: 'agent_task', task: intent } : { action: 'log', message: ('scheduled job: ' + name) };
      var jobData = { name: name, description: intent || null, schedule_kind: cron.kind, schedule_expr: cron.expr, payload_json: JSON.stringify(payloadObj), agent_id: App._activeAgentId || 'roboticus' };
      if (App._calModalMode === 'add') {
        api('/api/cron/jobs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(jobData) }).then(function() { toast('Added "' + name + '"'); App._calModalMode = null; App._calEditJob = null; App.navigate('scheduler'); }).catch(function(err) { toast(err.message || 'Add failed'); });
      } else {
        var editIdx = App._calEditJob && App._calEditJob._idx;
        var editJob = editIdx != null ? _cachedCronJobs[editIdx] : null;
        if (editJob && editJob.id) {
          api('/api/cron/jobs/' + encodeURIComponent(editJob.id), { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(jobData) }).then(function() { toast('Updated "' + name + '"'); App._calModalMode = null; App._calEditJob = null; App.navigate('scheduler'); }).catch(function(err) { toast(err.message || 'Update failed'); });
        } else { toast('Cannot update: job ID missing'); }
      }
      return;
    }
    var evtChip = e.target.closest('[data-evt-job]');
    if (evtChip) {
      if (evtChip.hasAttribute('data-past')) {
        var parentDay = evtChip.closest('.cal-day[data-date]');
        if (parentDay) { var date = parentDay.getAttribute('data-date'); App._calSelected = date; App.navigate('scheduler'); }
        return;
      }
      var jobName = evtChip.getAttribute('data-evt-job');
      var idx = _cachedCronJobs.findIndex(function(j) { return j.name === jobName; });
      if (idx >= 0) { var j = _cachedCronJobs[idx]; App._calModalMode = 'edit'; App._calEditJob = { name: j.name, description: (j.description || App._cronIntentFromJob(j) || ''), schedule_kind: j.schedule_kind, schedule_expr: j.schedule_expr, _idx: idx, _origName: j.name }; App.navigate('scheduler'); }
      return;
    }
    var calDay = e.target.closest('.cal-day[data-date]');
    if (calDay) { var date = calDay.getAttribute('data-date'); App._calSelected = App._calSelected === date ? null : date; App.navigate('scheduler'); return; }

    var copyBtn = e.target.closest('.copy-id-btn[data-copy-id]');
    if (copyBtn) {
      e.stopPropagation();
      var copyId = copyBtn.getAttribute('data-copy-id');
      var svgEl = copyBtn.querySelector('svg');
      navigator.clipboard.writeText(copyId).then(function() {
        toast('Session ID copied');
        if (svgEl) { svgEl.innerHTML = '<polyline points="4 8 7 11 12 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>'; setTimeout(function() { svgEl.innerHTML = '<rect x="5" y="5" width="8" height="8" rx="1" stroke="currentColor" stroke-width="1.3"/><path d="M3 11V3a1 1 0 011-1h8" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>'; }, 1500); }
      }).catch(function() { toast('Copy failed \u2014 try HTTPS'); });
      return;
    }
    var row = e.target.closest('tbody tr[data-id]');
    if (row) {
      var id = row.getAttribute('data-id');
      var tds = row.querySelectorAll('td');
      var nickTd = tds[0]; var agentTd = tds[1];
      var nick = nickTd ? nickTd.textContent.trim() : '';
      dismissHint('sessions-helper');
      try { window.localStorage.setItem('ic_sessions_helper_dismissed', '1'); } catch (_) {}
      App._activeSession = {
        id: id,
        agent_id: agentTd ? agentTd.textContent : 'default',
        agent_name: AGENT_DISPLAY_NAME || null,
        nickname: nick !== id ? nick : null
      };
      App.navigate('sessions');
      return;
    }
    // Grade stars: click to submit grade
    var starEl = e.target.closest('.grade-stars .star');
    if (starEl) {
      var gradeRow = starEl.closest('.grade-stars');
      var turnId = gradeRow.getAttribute('data-turn-id');
      var grade = parseInt(starEl.getAttribute('data-grade'));
      var allStars = gradeRow.querySelectorAll('.star');
      allStars.forEach(function(s) {
        var g = parseInt(s.getAttribute('data-grade'));
        if (g <= grade) { s.classList.add('filled'); } else { s.classList.remove('filled'); }
      });
      api('/api/turns/' + encodeURIComponent(turnId) + '/feedback', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ grade: grade })
      }).catch(function() {
        api('/api/turns/' + encodeURIComponent(turnId) + '/feedback', {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ grade: grade })
        }).catch(function() {});
      });
      return;
    }
    // Grade comment toggle
    var commentToggle = e.target.closest('.grade-comment-toggle');
    if (commentToggle) {
      var gradeRow = commentToggle.closest('.grade-stars');
      var turnId = gradeRow.getAttribute('data-turn-id');
      var existing = gradeRow.parentElement.querySelector('.grade-comment-row');
      if (existing) { existing.remove(); return; }
      var row = document.createElement('div');
      row.className = 'grade-comment-row';
      var inp = document.createElement('input');
      inp.type = 'text';
      inp.placeholder = 'Add a comment\u2026';
      inp.className = 'grade-comment-input';
      inp.setAttribute('data-turn-id', turnId);
      var btn = document.createElement('button');
      btn.className = 'grade-save-btn';
      btn.setAttribute('data-turn-id', turnId);
      btn.textContent = 'Save';
      row.appendChild(inp);
      row.appendChild(btn);
      gradeRow.parentElement.insertBefore(row, gradeRow.nextSibling);
      inp.focus();
      return;
    }
    // Grade comment save
    var commentSave = e.target.closest('.grade-save-btn');
    if (commentSave) {
      var turnId = commentSave.getAttribute('data-turn-id');
      var input = commentSave.parentElement.querySelector('.grade-comment-input');
      var comment = input ? input.value.trim() : '';
      var gradeRow = commentSave.parentElement.previousElementSibling;
      var filledStars = gradeRow ? gradeRow.querySelectorAll('.star.filled').length : 0;
      var grade = filledStars > 0 ? filledStars : 3;
      api('/api/turns/' + encodeURIComponent(turnId) + '/feedback', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ grade: grade, comment: comment || null })
      }).then(function() {
        commentSave.parentElement.remove();
        toast('Comment saved');
      }).catch(function() { toast('Failed to save comment'); });
      return;
    }
    // Context Explorer: session selection
    var ctxSessionRow = e.target.closest('tr[data-ctx-session]');
    if (ctxSessionRow) {
      try { App._ctxSession = JSON.parse(ctxSessionRow.getAttribute('data-ctx-session')); } catch(ex) {}
      App._ctxActiveTurn = null;
      App.navigate('context');
      return;
    }
    // Context Explorer: turn selection
    var ctxTurnItem = e.target.closest('.ctx-timeline-item[data-turn-id]');
    if (ctxTurnItem) {
      var turnId = ctxTurnItem.getAttribute('data-turn-id');
      var found = App._ctxTurns.find(function(t) { return t.id === turnId; });
      if (found) { App._ctxActiveTurn = found; App.navigate('context'); }
      return;
    }
    var ctxLiveBtn = e.target.closest('#ctx-open-live-turn');
    if (ctxLiveBtn) {
      var liveTurnId = ctxLiveBtn.getAttribute('data-turn-id');
      var liveSessionId = ctxLiveBtn.getAttribute('data-session-id');
      if (liveSessionId) {
        App._ctxSession = { id: liveSessionId, nickname: liveSessionId };
      }
      if (liveTurnId) {
        App._ctxActiveTurn = { id: liveTurnId };
      }
      App.navigate('context');
      return;
    }
    var ctxCopyLiveBtn = e.target.closest('#ctx-copy-live-turn');
    if (ctxCopyLiveBtn) {
      var copyTurnId = ctxCopyLiveBtn.getAttribute('data-turn-id') || '';
      if (!copyTurnId) {
        toast('No live turn ID available');
        return;
      }
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(copyTurnId).then(function() {
          toast('Turn ID copied');
        }).catch(function() {
          toast('Copy failed');
        });
      } else {
        try {
          var tmp = document.createElement('textarea');
          tmp.value = copyTurnId;
          tmp.setAttribute('readonly', '');
          tmp.style.position = 'absolute';
          tmp.style.left = '-9999px';
          document.body.appendChild(tmp);
          tmp.select();
          document.execCommand('copy');
          document.body.removeChild(tmp);
          toast('Turn ID copied');
        } catch (ex) {
          toast('Copy failed');
        }
      }
      return;
    }
    // Context Explorer: back button
    if (e.target.closest('#ctx-back-btn')) {
      if (App._ctxActiveTurn) { App._ctxActiveTurn = null; }
      else { App._ctxSession = null; }
      App.navigate('context');
      return;
    }
    // Per-message context expand button
    var ctxBtn = e.target.closest('.ctx-expand-btn');
    if (ctxBtn) {
      var msgIdx = ctxBtn.getAttribute('data-msg-idx');
      var detailEl = document.getElementById('ctx-detail-' + msgIdx);
      if (!detailEl) return;
      if (detailEl.style.display !== 'none') { detailEl.style.display = 'none'; return; }
      var sessionId = ctxBtn.getAttribute('data-session-id');
      setHtml(detailEl, '<span style="color:var(--muted);font-size:0.75rem">Loading context\u2026</span>');
      detailEl.style.display = 'block';
      api('/api/sessions/' + encodeURIComponent(sessionId) + '/turns').then(function(r) {
        var turns = r.turns || [];
        var asstIdx = 0;
        var allMsgs = App._sessionMessages || [];
        for (var mi = 0; mi < allMsgs.length && mi <= parseInt(msgIdx); mi++) {
          if (allMsgs[mi].role === 'assistant') asstIdx++;
        }
        var turn = turns[asstIdx - 1];
        if (!turn) { setHtml(detailEl, '<span style="color:var(--muted);font-size:0.75rem">No turn data found.</span>'); return; }
        return Promise.all([
          api('/api/turns/' + encodeURIComponent(turn.id) + '/context').catch(function() { return null; }),
          api('/api/turns/' + encodeURIComponent(turn.id) + '/tips').catch(function() { return { tips: [] }; })
        ]).then(function(results) {
          var ctx = results[0], tipsData = results[1];
          var html = '';
          if (ctx && ctx.snapshot !== false) {
            var budget = ctx.token_budget || 1;
            var sysPct = Math.round(((ctx.system_prompt_tokens || 0) / budget) * 100);
            var memPct = Math.round(((ctx.memory_tokens || 0) / budget) * 100);
            var histPct = Math.round(((ctx.history_tokens || 0) / budget) * 100);
            var freePct = Math.max(0, 100 - sysPct - memPct - histPct);
            html += '<div style="font-size:0.6875rem;color:var(--muted);margin-bottom:0.25rem">' + esc(ctx.complexity_level) + ' \u2022 budget: ' + (ctx.token_budget || 0).toLocaleString() + ' tokens \u2022 depth: ' + (ctx.history_depth || 0) + '</div>';
            html += '<div class="ctx-bar"><span class="sys" style="width:' + sysPct + '%"></span><span class="mem" style="width:' + memPct + '%"></span><span class="hist" style="width:' + histPct + '%"></span><span class="free" style="width:' + freePct + '%"></span></div>';
            html += '<div class="ctx-legend"><span class="l-sys">System ' + (ctx.system_prompt_tokens || 0) + '</span><span class="l-mem">Memory ' + (ctx.memory_tokens || 0) + '</span><span class="l-hist">History ' + (ctx.history_tokens || 0) + '</span></div>';
            if (ctx.model) html += '<div style="margin-top:0.25rem;font-size:0.6875rem;color:var(--muted)">Model: ' + esc(ctx.model) + '</div>';
          } else {
            html += '<span style="color:var(--muted)">No context snapshot.</span>';
          }
          if (turn.cost != null) html += '<div style="margin-top:0.25rem;font-size:0.6875rem;color:var(--success)">$' + turn.cost.toFixed(4) + ' \u2022 ' + ((turn.tokens_in || 0) + (turn.tokens_out || 0)).toLocaleString() + ' tokens</div>';
          var tips = (tipsData && tipsData.tips) || [];
          if (tips.length > 0) {
            html += '<div class="ctx-tips">';
            tips.forEach(function(tip) {
              html += '<span class="ctx-tip ' + esc(tip.severity) + '" title="' + esc(tip.suggestion) + '"><span class="tip-dot"></span>' + esc(tip.message) + '</span>';
            });
            html += '</div>';
          }
          html += '<button class="btn secondary ctx-analyze-btn" data-analyze-turn="' + esc(turn.id) + '">Analyze with AI</button>';
          if (turn.thinking) html += '<details class="ctx-section"><summary>Reasoning</summary><div>' + renderSafeMarkdown(turn.thinking) + '</div></details>';
          setHtml(detailEl, html);
        });
      }).catch(function() { setHtml(detailEl, '<span style="color:var(--error);font-size:0.75rem">Failed to load context.</span>'); });
      return;
    }
    var analyzeBtn = e.target.closest('[data-analyze-turn]');
    if (analyzeBtn) {
      var turnId = analyzeBtn.getAttribute('data-analyze-turn');
      analyzeBtn.disabled = true; analyzeBtn.textContent = 'Analyzing\u2026';
      api('/api/turns/' + encodeURIComponent(turnId) + '/analyze', { method: 'POST' }).then(function(r) {
        analyzeBtn.textContent = 'Analyze with AI'; analyzeBtn.disabled = false;
        var parent = analyzeBtn.parentElement;
        var existing = parent.querySelector('.ctx-analyze-result');
        if (existing) existing.remove();
        var div = document.createElement('div');
        div.className = 'ctx-analyze-result';
        div.style.cssText = 'margin-top:0.375rem;padding:0.5rem;background:rgba(0,0,0,0.15);border-radius:3px;font-size:0.6875rem;border:1px solid var(--border)';
        var inner = '<div style="color:var(--muted);margin-bottom:0.25rem">Turn analysis</div>';
        inner += '<div style="margin-bottom:0.35rem;color:var(--text)">' + esc(r.summary || 'No summary') + '</div>';
        var tips = r.tips || [];
        if (tips.length > 0) {
          inner += '<ul style="margin:0;padding-left:1rem">';
          tips.forEach(function(t) {
            inner += '<li style="margin:0.18rem 0"><strong>' + esc(t.rule_name || 'tip') + ':</strong> ' + esc(t.suggestion || t.message || '') + '</li>';
          });
          inner += '</ul>';
        } else {
          inner += '<div style="color:var(--muted)">No additional recommendations.</div>';
        }
        setHtml(div, inner);
        parent.insertBefore(div, analyzeBtn.nextSibling);
      }).catch(function(err) {
        analyzeBtn.textContent = 'Analyze with AI'; analyzeBtn.disabled = false;
        toast(err.message || 'Analysis failed');
      });
      return;
    }
  });
  if (content) content.addEventListener('change', function(e) {
    if (e.target && e.target.id === 'mem-session-select') { App._memorySessionId = e.target.value; App._memoryPage = 0; App.navigate('memory'); return; }
    if (e.target && e.target.id === 'traces-session-select') { App._tracesSessionId = e.target.value; App.navigate('metrics'); return; }
    if (e.target && e.target.id === 'budget-channel-min') {
      var draft = normalizeContextBudget(App._contextBudgetDraft || App._contextBudgetDefaults || {});
      draft.channel_minimum = String(e.target.value || 'L1').toUpperCase();
      App._contextBudgetDraft = normalizeContextBudget(draft);
      return;
    }
    if (!e.target || e.target.id !== 'model-graph-task-select') return;
    App._modelGraphFocusTurn = e.target.value || null;
    App._modelGraphFocusModel = null;
    App._modelGraphFocusEdge = null;
    App.navigate('efficiency');
  });
  if (content) content.addEventListener('input', function(e) {
    var slider = e.target.closest('[data-routing-slider]');
    if (!slider) return;
    var key = slider.getAttribute('data-routing-slider');
    if (!key) return;
    if (!App._routingProfileDraft) {
      App._routingProfileDraft = App._routingProfileDefaults
        ? { correctness: App._routingProfileDefaults.correctness, cost: App._routingProfileDefaults.cost, speed: App._routingProfileDefaults.speed }
        : { correctness: 0.0, cost: 0.0, speed: 0.0 };
    }
    App._routingProfileDraft[key] = clamp01(slider.value);
    App._routingProfileDraft = normalizeRoutingProfile(App._routingProfileDraft, key);
    ['correctness', 'cost', 'speed'].forEach(function(k) {
      var valEl = document.getElementById('routing-slider-' + k + '-val');
      if (valEl) valEl.textContent = App._routingProfileDraft[k].toFixed(2);
      var sliderEl = document.getElementById('routing-slider-' + k);
      if (sliderEl) sliderEl.value = App._routingProfileDraft[k].toFixed(2);
    });
    var totalEl = document.getElementById('routing-slider-total');
    if (totalEl) {
      var total = routingProfileTotal(App._routingProfileDraft);
      totalEl.textContent = total.toFixed(2);
      totalEl.style.color = total > 1.0 + 1e-9 ? 'var(--warning, #f59e0b)' : '';
      totalEl.title = total > 1.0 + 1e-9 ? 'Values exceed 1.0 and will be normalized on apply' : '';
    }
    var spiderHost = document.getElementById('routing-profile-spider-host');
    if (spiderHost) spiderHost.innerHTML = renderRoutingSpiderSvg(App._routingProfileDraft);
  });
  // Context budget slider handler
  if (content) content.addEventListener('input', function(e) {
    var bSlider = e.target.closest('[data-budget-slider]');
    if (!bSlider) return;
    var bKey = bSlider.getAttribute('data-budget-slider');
    if (!bKey) return;
    var draft = normalizeContextBudget(App._contextBudgetDraft || App._contextBudgetDefaults || {});
    draft[bKey] = parseInt(bSlider.value, 10) || draft[bKey];
    App._contextBudgetDraft = normalizeContextBudget(draft);
    var valEl = document.getElementById('budget-' + bKey + '-val');
    if (valEl) valEl.textContent = App._contextBudgetDraft[bKey];
  });
  if (content) content.addEventListener('input', function(e) {
    if (!e.target || e.target.id !== 'catalog-filter-input') return;
    var needle = String(e.target.value || '').trim().toLowerCase();
    var rows = Array.prototype.slice.call(document.querySelectorAll('[data-catalog-row="1"]'));
    var visible = 0;
    rows.forEach(function(row) {
      var hay = String(row.getAttribute('data-catalog-search') || '').toLowerCase();
      var match = !needle || hay.indexOf(needle) !== -1;
      row.style.display = match ? '' : 'none';
      if (match) visible += 1;
    });
    var empty = document.getElementById('catalog-filter-empty');
    if (empty) empty.style.display = visible === 0 ? '' : 'none';
  });
  if (content) content.addEventListener('input', function(e) {
    if (e.target.id === 'mem-filter-input') { App._memoryFilter = e.target.value; App._memoryPage = 0; App.navigate('memory'); return; }
  });

  if (content) content.addEventListener('input', function(e) {
    if (e.target.id === 'cal-sched-interval' || e.target.id === 'cal-sched-hour' || e.target.id === 'cal-sched-minute') { var sched = App._readSchedFromUI(); var sumEl = document.getElementById('cal-sched-summary'); if (sumEl) sumEl.textContent = App._schedSummary(sched); return; }
    var rawEditor = e.target.closest('#settings-raw-editor');
    if (rawEditor) {
      App._settingsRawText = rawEditor.value; App._settingsDirty = true;
      syncRawHighlight();
      document.querySelectorAll('#settings-save,#settings-apply,#settings-cancel').forEach(function(b) { b.disabled = false; b.style.opacity = ''; b.style.pointerEvents = ''; });
      return;
    }
    var formInput = e.target.closest('[data-settings-path]');
    if (formInput) {
      var path = formInput.getAttribute('data-settings-path');
      var draft = App._getSettingsDraft();
      var rawVal = formInput.value;
      var newVal;
      if (formInput.type === 'checkbox') { newVal = formInput.checked; }
      else if (formInput.type === 'number') { newVal = Number(rawVal); }
      else if (formInput.hasAttribute('data-settings-array')) { newVal = rawVal.split('\n').map(function(s){return s.trim()}).filter(function(s){return s.length > 0}); }
      else { newVal = rawVal === '' ? null : rawVal; }
      App._setNestedValue(draft, path, newVal);
      App._settingsDirty = JSON.stringify(draft) !== JSON.stringify(_cachedConfig);
      App._settingsRawText = null;
      if (formInput.type === 'checkbox') { var lbl = formInput.closest('.settings-toggle-wrap'); if (lbl) { var span = lbl.querySelector('.settings-toggle-label'); if (span) span.textContent = newVal ? 'On' : 'Off'; } }
      document.querySelectorAll('#settings-save,#settings-apply,#settings-cancel').forEach(function(b) { if (App._settingsDirty) { b.disabled = false; b.style.opacity = ''; b.style.pointerEvents = ''; } else { b.disabled = true; b.style.opacity = '0.4'; b.style.pointerEvents = 'none'; } });
      return;
    }
    var revenueSwapChainInput = e.target.closest('[data-revenue-swap-chain-field]');
    if (revenueSwapChainInput) {
      var revenueField = revenueSwapChainInput.getAttribute('data-revenue-swap-chain-field');
      var revenueIdx = Number(revenueSwapChainInput.getAttribute('data-revenue-swap-chain-index'));
      var draftSwapField = App._getSettingsDraft();
      if (!draftSwapField.treasury) draftSwapField.treasury = {};
      if (!draftSwapField.treasury.revenue_swap) draftSwapField.treasury.revenue_swap = {};
      if (!Array.isArray(draftSwapField.treasury.revenue_swap.chains)) draftSwapField.treasury.revenue_swap.chains = [];
      if (!draftSwapField.treasury.revenue_swap.chains[revenueIdx]) draftSwapField.treasury.revenue_swap.chains[revenueIdx] = { chain: '', target_contract_address: '', swap_contract_address: '' };
      var revenueVal = revenueSwapChainInput.value;
      if (revenueField === 'chain') revenueVal = String(revenueVal || '').toUpperCase();
      draftSwapField.treasury.revenue_swap.chains[revenueIdx][revenueField] = revenueVal;
      App._settingsDirty = JSON.stringify(draftSwapField) !== JSON.stringify(_cachedConfig);
      App._settingsRawText = null;
      document.querySelectorAll('#settings-save,#settings-apply,#settings-cancel').forEach(function(b) { if (App._settingsDirty) { b.disabled = false; b.style.opacity = ''; b.style.pointerEvents = ''; } else { b.disabled = true; b.style.opacity = '0.4'; b.style.pointerEvents = 'none'; } });
      return;
    }
  });
  if (content) content.addEventListener('dragstart', function(e) {
    var item = e.target.closest('[data-model-order-item]');
    if (!item || App._settingsMode !== 'models') return;
    App._settingsDragModelIndex = Number(item.getAttribute('data-model-order-item'));
    item.classList.add('dragging');
    if (e.dataTransfer) {
      e.dataTransfer.effectAllowed = 'move';
      e.dataTransfer.setData('text/plain', String(App._settingsDragModelIndex));
    }
  });
  if (content) content.addEventListener('dragover', function(e) {
    var item = e.target.closest('[data-model-order-item]');
    if (!item || App._settingsMode !== 'models') return;
    e.preventDefault();
    if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
    document.querySelectorAll('.model-order-item.drop-target').forEach(function(el) { el.classList.remove('drop-target'); });
    item.classList.add('drop-target');
  });
  if (content) content.addEventListener('dragleave', function(e) {
    var item = e.target.closest('[data-model-order-item]');
    if (!item) return;
    item.classList.remove('drop-target');
  });
  if (content) content.addEventListener('drop', function(e) {
    var item = e.target.closest('[data-model-order-item]');
    if (!item || App._settingsMode !== 'models') return;
    e.preventDefault();
    var fromIdx = App._settingsDragModelIndex;
    var toIdx = Number(item.getAttribute('data-model-order-item'));
    document.querySelectorAll('.model-order-item.drop-target,.model-order-item.dragging').forEach(function(el) { el.classList.remove('drop-target'); el.classList.remove('dragging'); });
    App._settingsDragModelIndex = null;
    if (!App._settingsModelOrder || fromIdx == null || fromIdx === toIdx || fromIdx < 0 || toIdx < 0) return;
    var moved = App._settingsModelOrder.splice(fromIdx, 1)[0];
    App._settingsModelOrder.splice(toIdx, 0, moved);
    App._syncModelOrderToDraft();
    App.navigate('settings');
  });
  if (content) content.addEventListener('dragend', function() {
    document.querySelectorAll('.model-order-item.drop-target,.model-order-item.dragging').forEach(function(el) { el.classList.remove('drop-target'); el.classList.remove('dragging'); });
    App._settingsDragModelIndex = null;
  });
  if (content) content.addEventListener('keydown', function(e) {
    if (e.target.id === 'session-msg-input' && e.key === 'Enter') { if (e.shiftKey) return; e.preventDefault(); if (App._sendingMessage) return; var btn = document.getElementById('btn-send-msg'); if (btn && !btn.disabled) btn.click(); return; }
    if (e.target.id === 'model-order-add-input' && e.key === 'Enter') { e.preventDefault(); var addBtn = document.getElementById('model-order-add-btn'); if (addBtn) addBtn.click(); return; }
    if (e.target.id === 'revenue-swap-chain-add' && e.key === 'Enter') { e.preventDefault(); var addRevenueSwapChainBtn = document.getElementById('revenue-swap-chain-add-btn'); if (addRevenueSwapChainBtn) addRevenueSwapChainBtn.click(); return; }
    if (e.target.id === 'add-provider-input' && e.key === 'Enter') { e.preventDefault(); var addProviderBtn = document.getElementById('add-provider'); if (addProviderBtn) addProviderBtn.click(); return; }
    if (e.target.id === 'trusted-sender-input' && e.key === 'Enter') { e.preventDefault(); var addSenderBtn = document.getElementById('trusted-sender-add'); if (addSenderBtn) addSenderBtn.click(); return; }
    if (e.target.classList && e.target.classList.contains('ch-list-input') && e.key === 'Enter') { e.preventDefault(); var chN = e.target.getAttribute('data-ch-name'); var chF = e.target.getAttribute('data-ch-field'); var chAddBtn = document.querySelector('.ch-list-add[data-ch-name="' + chN + '"][data-ch-field="' + chF + '"]'); if (chAddBtn) chAddBtn.click(); return; }
    if (e.target.id === 'roster-model-custom' && e.key === 'Enter') { e.preventDefault(); var rosterAddBtn = document.getElementById('roster-model-order-add'); var rosterSaveBtn = document.getElementById('roster-model-save'); if (rosterAddBtn) rosterAddBtn.click(); else if (rosterSaveBtn) rosterSaveBtn.click(); return; }
    if (e.target.id === 'settings-raw-editor' && e.key === 'Tab') { e.preventDefault(); var ta = e.target; var start = ta.selectionStart, end = ta.selectionEnd; ta.value = ta.value.substring(0, start) + '  ' + ta.value.substring(end); ta.selectionStart = ta.selectionEnd = start + 2; ta.dispatchEvent(new Event('input', { bubbles: true })); }
  });
  if (content) content.addEventListener('change', function(e) {
    if (e.target.id === 'model-order-auto') {
      var draft = App._getSettingsDraft();
      if (!draft.models) draft.models = {};
      draft.models.auto_order = e.target.checked;
      App._settingsDirty = JSON.stringify(draft) !== JSON.stringify(_cachedConfig);
      App.navigate('settings');
      return;
    }
    if (e.target.classList && e.target.classList.contains('startup-ann-check')) {
      var draftSa = App._getSettingsDraft();
      if (!draftSa.channels) draftSa.channels = {};
      var checks = Array.prototype.slice.call(document.querySelectorAll('.startup-ann-check:checked')).map(function(el) { return String(el.getAttribute('data-startup-channel') || '').trim(); }).filter(Boolean);
      draftSa.channels.startup_announcements = checks.length ? checks : null;
      App._settingsDirty = JSON.stringify(draftSa) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      document.querySelectorAll('#settings-save,#settings-apply,#settings-cancel').forEach(function(b) { if (App._settingsDirty) { b.disabled = false; b.style.opacity = ''; b.style.pointerEvents = ''; } else { b.disabled = true; b.style.opacity = '0.4'; b.style.pointerEvents = 'none'; } });
      return;
    }
    if (e.target.id === 'cal-sched-freq') {
      var job = App._calEditJob || { name: '', schedule_kind: 'cron', schedule_expr: '' };
      var nameEl = document.getElementById('cal-job-name'); if (nameEl) job.name = nameEl.value;
      var freq = e.target.value;
      var defaults = { interval: { kind: 'cron', expr: '*/5 * * * *' }, hourly: { kind: 'cron', expr: '0 * * * *' }, daily: { kind: 'cron', expr: '0 2 * * *' }, weekly: { kind: 'cron', expr: '0 9 * * 1,2,3,4,5' } };
      var d = defaults[freq] || defaults.daily; job.schedule_kind = d.kind; job.schedule_expr = d.expr; App._calEditJob = job; App.navigate('scheduler'); return;
    }
    if (e.target.id === 'cal-sched-interval' || e.target.id === 'cal-sched-interval-unit' || e.target.id === 'cal-sched-hour' || e.target.id === 'cal-sched-minute') { var sched = App._readSchedFromUI(); var sumEl = document.getElementById('cal-sched-summary'); if (sumEl) sumEl.textContent = App._schedSummary(sched); return; }
    var formInput = e.target.closest('[data-settings-path]');
    if (formInput) {
      var path = formInput.getAttribute('data-settings-path'); var draft = App._getSettingsDraft();
      var newVal;
      if (formInput.type === 'checkbox') { newVal = formInput.checked; }
      else if (formInput.tagName === 'SELECT') { newVal = formInput.value; }
      else if (formInput.type === 'number') { newVal = Number(formInput.value); }
      else if (formInput.hasAttribute('data-settings-array')) { newVal = formInput.value.split('\n').map(function(s){return s.trim()}).filter(function(s){return s.length > 0}); }
      else { newVal = formInput.value === '' ? null : formInput.value; }
      App._setNestedValue(draft, path, newVal);
      App._settingsDirty = JSON.stringify(draft) !== JSON.stringify(_cachedConfig); App._settingsRawText = null;
      if (formInput.type === 'checkbox') { var lbl = formInput.closest('.settings-toggle-wrap'); if (lbl) { var span = lbl.querySelector('.settings-toggle-label'); if (span) span.textContent = newVal ? 'On' : 'Off'; } }
      document.querySelectorAll('#settings-save,#settings-apply,#settings-cancel').forEach(function(b) { if (App._settingsDirty) { b.disabled = false; b.style.opacity = ''; b.style.pointerEvents = ''; } else { b.disabled = true; b.style.opacity = '0.4'; b.style.pointerEvents = 'none'; } });
    }
    var revenueSwapChainInput = e.target.closest('[data-revenue-swap-chain-field]');
    if (revenueSwapChainInput && revenueSwapChainInput.getAttribute('data-revenue-swap-chain-field') === 'chain') {
      revenueSwapChainInput.value = String(revenueSwapChainInput.value || '').toUpperCase();
    }
  });
