App._settingsMode = 'form';
App._settingsDraft = null;
App._settingsDraftKey = null;
App._settingsModelOrder = null;
App._settingsDragModelIndex = null;
App._settingsRawText = null;
App._settingsDirty = false;

// ── Missing function stubs (lost in 2.27 decomposition) ─────────────
// These provide safe defaults so Settings/Agents pages render.
// Full implementations tracked for 0.11.4.

App._resolveActiveAgentId = function() {
    // Returns a promise resolving to the active agent ID
    return api('/api/health').then(function(h) { return h.agent || 'duncan'; }).catch(function() { return 'duncan'; });
};

App._renderModelOptionsHtml = function(opts) {
    // Render <option> tags for a model selector from discovered + configured models
    var entries = App._buildModelOptionEntries(opts);
    return entries.map(function(e) {
        var sel = (opts.selected || []).indexOf(e.value) >= 0 ? ' selected' : '';
        return '<option value="' + esc(e.value) + '"' + sel + '>' + esc(e.label) + '</option>';
    }).join('');
};

App._buildModelOptionEntries = function(opts) {
    // Build a deduped list of {value, label} model entries from all sources
    var seen = {};
    var entries = [];
    function add(model, source) {
        if (!model || seen[model]) return;
        seen[model] = true;
        entries.push({ value: model, label: model + (source ? ' (' + source + ')' : '') });
    }
    // Config models
    var cfg = (opts.config || {}).models || {};
    if (cfg.primary) add(cfg.primary, 'primary');
    (cfg.fallbacks || []).forEach(function(m) { add(m, 'fallback'); });
    // Discovered models
    (opts.discoveredModels || []).forEach(function(m) {
        var name = typeof m === 'string' ? m : (m.id || m.name || '');
        if (name) add(name, 'discovered');
    });
    return entries;
};

App._buildMutableConfigPatch = function(draft) {
    // Build a config patch from the settings draft for saving
    if (!draft) return {};
    var patch = JSON.parse(JSON.stringify(draft));
    // Remove read-only / computed fields
    delete patch.providers;
    return patch;
};

App._initModelOrderFromDraft = function() {
    // Initialize the model order drag list from the current settings draft
    var draft = App._getSettingsDraft();
    var models = draft.models || {};
    var order = [];
    if (models.primary) order.push(models.primary);
    (models.fallbacks || []).forEach(function(m) { if (order.indexOf(m) < 0) order.push(m); });
    App._settingsModelOrder = order;
};

App._syncModelOrderToDraft = function() {
    // Write the current model order back to the settings draft
    var order = App._settingsModelOrder || [];
    var draft = App._getSettingsDraft();
    if (!draft.models) draft.models = {};
    draft.models.primary = order[0] || '';
    draft.models.fallbacks = order.slice(1);
    App._settingsDirty = true;
};

App._renderProviderDiscoveryAlerts = function() {
    // Provider reachability alerts — rendered from cached config provider data.
    // Returns HTML string with alert banners for unreachable providers.
    if (!_cachedConfig || !_cachedConfig.providers) return '';
    var alerts = [];
    var providers = _cachedConfig.providers || {};
    Object.keys(providers).forEach(function(name) {
        var p = providers[name];
        if (p._key_status === 'missing' && !p.is_local) {
            alerts.push('<div class="provider-alert-link" data-provider="' + esc(name) + '" style="padding:0.5rem 0.75rem;margin-bottom:0.5rem;border-left:3px solid var(--warning);background:rgba(245,158,11,0.08);font-size:0.75rem;border-radius:var(--radius);cursor:pointer" title="Click to scroll to provider">'
                + '<strong>' + esc(name) + '</strong>: API key not configured'
                + (p.api_key_env ? ' (set <code>' + esc(p.api_key_env) + '</code> or add to keystore)' : '')
                + '</div>');
        }
    });
    return alerts.join('');
};

App._getSettingsDraft = function() {
    if (!this._settingsDraft && _cachedConfig) {
        this._settingsDraft = JSON.parse(JSON.stringify(_cachedConfig));
    }
    return this._settingsDraft || {};
};

App.renderSettings = function() {
      var self = this;
      var mode = self._settingsMode;
      return Promise.all([
        api('/api/config'),
        api('/api/config/capabilities').catch(function() { return { immutable_sections: ['server', 'a2a', 'wallet'] }; }),
        mode === 'raw' ? api('/api/config/raw').catch(function() { return ''; }) : Promise.resolve(null),
        api('/api/keystore/status').catch(function() { return { unlocked: true }; })
      ]).then(function(arr) {
        var cfg = arr[0] || {};
        var caps = arr[1] || {};
        var rawToml = arr[2];
        var keystoreStatus = arr[3] || {};
        cfg._keystore_locked = !keystoreStatus.unlocked;
        self._configCapabilities = caps;
        var immutableSet = {};
        (caps.immutable_sections || []).forEach(function(s) { immutableSet[s] = true; });
        _cachedConfig = cfg;
        if (!self._settingsDraft) self._settingsDraft = JSON.parse(JSON.stringify(cfg));
        var draft = self._getSettingsDraft();
        var dirty = self._settingsDirty;
        var dirtyDot = dirty ? '<span class="settings-dirty-dot" title="Unsaved changes"></span>' : '';
        var tabs = '<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;flex-wrap:wrap;gap:0.5rem"><div class="tabs" style="margin-bottom:0"><button class="' + (mode === 'form' ? 'active' : '') + '" data-settings-mode="form">Form</button><button class="' + (mode === 'models' ? 'active' : '') + '" data-settings-mode="models">Model Order</button><button class="' + (mode === 'access' ? 'active' : '') + '" data-settings-mode="access">Access Control</button></div><div style="display:flex;background:var(--surface-2);border:1px solid var(--border-ghost);border-radius:9999px;padding:2px;gap:0"><button data-settings-mode="' + (mode === 'raw' ? 'form' : 'raw') + '" style="padding:0.375rem 1rem;border-radius:9999px;border:none;font-family:var(--font-headline);font-size:0.625rem;font-weight:700;letter-spacing:0.15em;text-transform:uppercase;cursor:pointer;transition:all 0.2s;' + (mode !== 'raw' ? 'background:var(--accent);color:#000;' : 'background:transparent;color:var(--outline);') + '">Form</button><button data-settings-mode="raw" style="padding:0.375rem 1rem;border-radius:9999px;border:none;font-family:var(--font-headline);font-size:0.625rem;font-weight:700;letter-spacing:0.15em;text-transform:uppercase;cursor:pointer;transition:all 0.2s;' + (mode === 'raw' ? 'background:var(--accent);color:#000;' : 'background:transparent;color:var(--outline);') + '">TOML</button></div></div>';
        var actions = '<div class="settings-actions"><button class="btn" id="settings-save" ' + (dirty ? '' : 'disabled style="opacity:0.4;pointer-events:none"') + '>Save</button><button class="btn secondary" id="settings-apply" ' + (dirty ? '' : 'disabled style="opacity:0.4;pointer-events:none"') + '>Apply</button><button class="btn secondary" id="settings-cancel" ' + (dirty ? '' : 'disabled style="opacity:0.4;pointer-events:none"') + '>Cancel</button>' + dirtyDot + '</div>';

        if (mode === 'raw') {
          var rawText = self._settingsRawText != null ? self._settingsRawText : String(rawToml || '');
          return tabs + '<div class="settings-editor settings-editor-fullheight"><div class="json-editor-wrap"><pre class="json-highlight" id="settings-raw-highlight" aria-hidden="true"></pre><textarea id="settings-raw-editor" spellcheck="false">' + esc(rawText) + '</textarea><div class="json-editor-focus-ring"></div></div><div class="settings-lint ok">\u2713 Raw TOML from disk-backed config</div></div>' + actions;
        }

        if (mode === 'models') {
          if (!self._settingsModelOrder) self._initModelOrderFromDraft();
          var modelOrder = (self._settingsModelOrder || []).slice();
          var modelsImmutable = !!immutableSet.models;
          var modelRows = modelOrder.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-model-order-make-primary="' + idx + '">Make primary</button>';
            var removeBtn = '<button class="model-order-btn" data-model-order-remove="' + idx + '">Remove</button>';
            var dragAttrs = modelsImmutable ? '' : (' draggable="true" data-model-order-item="' + idx + '"');
            return '<div class="model-order-item"' + dragAttrs + '>'
              + '<div class="model-order-handle" title="' + (modelsImmutable ? 'Read only' : '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>'
              + (modelsImmutable ? '' : removeBtn)
              + '</div>';
          }).join('');
          if (!modelRows) modelRows = '<div class="card" style="padding:0.75rem;color:var(--muted)">No models configured. Add one below to set a primary model.</div>';
          var knownModelOptions = self._renderModelOptionsHtml({
            config: draft,
            includeControlModes: false
          });
          var immutableNote = modelsImmutable
            ? '<div style="font-size:0.75rem;color:var(--muted);margin:0.35rem 0 0.85rem">Model order is immutable at runtime for this instance. Edit `roboticus.toml` and restart.</div>'
            : '<div style="font-size:0.75rem;color:var(--muted);margin:0.35rem 0 0.85rem">Drag rows to reorder preference. Top row is primary; all others are fallback order.</div>';
          var addRow = modelsImmutable ? '' : ('<div class="model-order-add"><input id="model-order-add-input" class="settings-input" list="model-order-known-list" type="text" placeholder="Add model (provider/name)"><datalist id="model-order-known-list">' + knownModelOptions + '</datalist><button class="btn secondary" id="model-order-add-btn">Add model</button></div>');
          var providerAlerts = self._renderProviderDiscoveryAlerts();
          var autoOrder = draft.models && draft.models.auto_order;
          var autoCheckbox = '<div style="margin-top:0.75rem;display:flex;align-items:center;gap:0.5rem">'
            + '<input type="checkbox" id="model-order-auto"' + (autoOrder ? ' checked' : '') + '>'
            + '<label for="model-order-auto" style="font-size:0.8125rem;color:var(--text)">Auto</label>'
            + '<span title="When enabled, the model order is treated as a default and may be adjusted dynamically at runtime based on availability and performance" style="cursor:help;color:var(--muted);font-size:0.65rem">(?)</span>'
            + '</div>';
          return tabs
            + '<div class="settings-form"><div class="settings-section"><div class="settings-section-title">Model Order</div>'
            + providerAlerts
            + immutableNote
            + '<div class="model-order-list" id="model-order-list">' + modelRows + '</div>'
            + addRow
            + autoCheckbox
            + '</div></div>'
            + actions;
        }

        if (mode === 'access') {
          var sec = draft.security || {};
          var authLevels = [['External','External \u2014 safe tools only'],['Peer','Peer \u2014 safe + caution'],['SelfGenerated','SelfGenerated \u2014 safe + caution + dangerous'],['Creator','Creator \u2014 full access']];
          var ceilLevels = [['External','External \u2014 safe tools only'],['Peer','Peer \u2014 safe + caution'],['SelfGenerated','SelfGenerated \u2014 safe + caution + dangerous']];
          var mkOpts = function(levels, cur) { return levels.map(function(l) { return '<option value="' + l[0] + '"' + (cur === l[0] ? ' selected' : '') + '>' + l[1] + '</option>'; }).join(''); };
          var accHtml = '';
          accHtml += '<div class="settings-section"><div class="settings-section-title">Authority Matrix</div>'
            + '<div style="font-size:0.75rem;color:var(--muted);margin-bottom:0.75rem">What each authority level can do:</div>'
            + '<table style="width:100%;font-size:0.72rem;border-collapse:collapse">'
            + '<thead><tr style="border-bottom:1px solid var(--border)"><th style="text-align:left;padding:0.35rem 0.5rem">Level</th><th style="text-align:center;padding:0.35rem">Safe</th><th style="text-align:center;padding:0.35rem">Caution</th><th style="text-align:center;padding:0.35rem">Dangerous</th><th style="text-align:center;padding:0.35rem">Forbidden</th></tr></thead>'
            + '<tbody>'
            + '<tr style="border-bottom:1px solid var(--border)"><td style="padding:0.35rem 0.5rem"><strong>Creator</strong></td><td style="text-align:center">\u2705</td><td style="text-align:center">\u2705</td><td style="text-align:center">\u2705</td><td style="text-align:center">\u274c</td></tr>'
            + '<tr style="border-bottom:1px solid var(--border)"><td style="padding:0.35rem 0.5rem"><strong>SelfGenerated</strong></td><td style="text-align:center">\u2705</td><td style="text-align:center">\u2705</td><td style="text-align:center">\u2705</td><td style="text-align:center">\u274c</td></tr>'
            + '<tr style="border-bottom:1px solid var(--border)"><td style="padding:0.35rem 0.5rem"><strong>Peer</strong></td><td style="text-align:center">\u2705</td><td style="text-align:center">\u2705</td><td style="text-align:center">\u274c</td><td style="text-align:center">\u274c</td></tr>'
            + '<tr><td style="padding:0.35rem 0.5rem"><strong>External</strong></td><td style="text-align:center">\u2705</td><td style="text-align:center">\u274c</td><td style="text-align:center">\u274c</td><td style="text-align:center">\u274c</td></tr>'
            + '</tbody></table>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin-top:0.5rem">'
            + '<strong>Safe:</strong> conversation, summarization, web search. '
            + '<strong>Caution:</strong> read_file, write_file, delegation. '
            + '<strong>Dangerous:</strong> run_script, shell execution.'
            + '</div></div>';
          accHtml += '<div class="settings-section"><div class="settings-section-title">Security Policy</div>'
            + '<div style="font-size:0.75rem;color:var(--muted);margin-bottom:0.75rem">'
            + 'Authority resolution: effective = min(max(positive grants\u2026), min(negative ceilings\u2026))'
            + '</div>';
          var denyEmpty = sec.deny_on_empty_allowlist !== false;
          accHtml += '<div class="settings-row"><div class="settings-label">Deny on empty allow-list</div>'
            + '<div class="settings-toggle-wrap"><label class="toggle">'
            + '<input type="checkbox" data-settings-path="security.deny_on_empty_allowlist"' + (denyEmpty ? ' checked' : '') + '>'
            + '<span class="toggle-track"></span></label>'
            + '<span class="settings-toggle-label">' + (denyEmpty ? 'On' : 'Off') + '</span></div></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'When enabled, channels with no allow-list entries reject all messages (secure default). When disabled, empty allow-lists permit all senders.'
            + '</div>';
          var alAuth = sec.allowlist_authority || 'Peer';
          accHtml += '<div class="settings-row"><div class="settings-label">Allow-list authority</div>'
            + '<select class="settings-input" data-settings-path="security.allowlist_authority" style="max-width:280px">'
            + mkOpts(authLevels, alAuth) + '</select></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Authority granted to senders who pass a channel\u2019s allow-list check.'
            + '</div>';
          var trAuth = sec.trusted_authority || 'Creator';
          accHtml += '<div class="settings-row"><div class="settings-label">Trusted sender authority</div>'
            + '<select class="settings-input" data-settings-path="security.trusted_authority" style="max-width:280px">'
            + mkOpts(authLevels, trAuth) + '</select></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Authority granted to senders listed in channels.trusted_sender_ids.'
            + '</div>';
          var apiAuth = sec.api_authority || 'Creator';
          accHtml += '<div class="settings-row"><div class="settings-label">API authority</div>'
            + '<select class="settings-input" data-settings-path="security.api_authority" style="max-width:280px">'
            + mkOpts(authLevels, apiAuth) + '</select></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Authority for HTTP API and WebSocket requests.'
            + '</div>';
          var threatCeil = sec.threat_caution_ceiling || 'External';
          accHtml += '<div class="settings-row"><div class="settings-label">Threat scanner ceiling</div>'
            + '<select class="settings-input" data-settings-path="security.threat_caution_ceiling" style="max-width:280px">'
            + mkOpts(ceilLevels, threatCeil) + '</select></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Maximum authority when the threat scanner flags input as Caution. Must be below Creator.'
            + '</div>';
          accHtml += '</div>';

          // ── Filesystem Security section ────────────────────────────
          var fsSec = (sec.filesystem || {});
          var wsOnly = fsSec.workspace_only !== false;
          var scriptConf = fsSec.script_fs_confinement !== false;
          var protectedPaths = (fsSec.protected_paths || []).join('\n');
          var extraProtected = (fsSec.extra_protected_paths || []).join('\n');
          var scriptAllowed = (fsSec.script_allowed_paths || []).join('\n');

          accHtml += '<div class="settings-section"><div class="settings-section-title">Filesystem Security</div>'
            + '<div style="font-size:0.75rem;color:var(--muted);margin-bottom:0.75rem">'
            + 'Controls filesystem access for agent tools and skill scripts.'
            + '</div>';

          accHtml += '<div class="settings-row"><div class="settings-label">Workspace-only mode</div>'
            + '<div class="settings-toggle-wrap"><label class="toggle">'
            + '<input type="checkbox" data-settings-path="security.filesystem.workspace_only"' + (wsOnly ? ' checked' : '') + '>'
            + '<span class="toggle-track"></span></label>'
            + '<span class="settings-toggle-label">' + (wsOnly ? 'On' : 'Off') + '</span></div></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Restrict agent file tools to the workspace directory. Absolute paths outside workspace are denied.'
            + '</div>';

          accHtml += '<div class="settings-row"><div class="settings-label">Script FS confinement</div>'
            + '<div class="settings-toggle-wrap"><label class="toggle">'
            + '<input type="checkbox" data-settings-path="security.filesystem.script_fs_confinement"' + (scriptConf ? ' checked' : '') + '>'
            + '<span class="toggle-track"></span></label>'
            + '<span class="settings-toggle-label">' + (scriptConf ? 'On' : 'Off') + '</span></div></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'OS-level sandbox for skill scripts (macOS sandbox-exec). Confines writes to workspace + /tmp.'
            + '</div>';

          accHtml += '<div class="settings-row" style="align-items:start"><div class="settings-label" style="padding-top:0.3rem">Protected paths</div>'
            + '<textarea class="settings-input" data-settings-path="security.filesystem.protected_paths" data-settings-array="1" '
            + 'rows="6" style="font-family:var(--font-mono);font-size:0.7rem;resize:vertical"'
            + ' placeholder="One pattern per line">' + esc(protectedPaths) + '</textarea></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Blacklisted path patterns (case-insensitive substring match). One per line.'
            + '</div>';

          accHtml += '<div class="settings-row" style="align-items:start"><div class="settings-label" style="padding-top:0.3rem">Extra protected paths</div>'
            + '<textarea class="settings-input" data-settings-path="security.filesystem.extra_protected_paths" data-settings-array="1" '
            + 'rows="3" style="font-family:var(--font-mono);font-size:0.7rem;resize:vertical"'
            + ' placeholder="Your custom patterns (merged with defaults)">' + esc(extraProtected) + '</textarea></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Additional patterns merged with the default list above.'
            + '</div>';

          accHtml += '<div class="settings-row" style="align-items:start"><div class="settings-label" style="padding-top:0.3rem">Script allowed paths</div>'
            + '<textarea class="settings-input" data-settings-path="security.filesystem.script_allowed_paths" data-settings-array="1" '
            + 'rows="2" style="font-family:var(--font-mono);font-size:0.7rem;resize:vertical"'
            + ' placeholder="Absolute paths scripts may write to (one per line)">' + esc(scriptAllowed) + '</textarea></div>'
            + '<div style="font-size:0.68rem;color:var(--muted);margin:-0.25rem 0 0.6rem 0;padding-left:0.5rem">'
            + 'Additional absolute paths that sandboxed scripts may access for writing. One per line.'
            + '</div>';

          accHtml += '</div>';

          accHtml += '<div class="settings-section"><div class="settings-section-title">How Claims Are Resolved</div>'
            + '<div style="font-size:0.72rem;line-height:1.6;color:var(--text)">'
            + '<strong>Positive grants</strong> OR across layers \u2014 any layer can grant authority (best grant wins):<br>'
            + '<span style="color:var(--muted)">\u2022 Channel allow-list \u2192 allow-list authority &nbsp;\u2022 trusted_sender_ids \u2192 trusted authority &nbsp;\u2022 API key \u2192 API authority</span><br><br>'
            + '<strong>Negative ceilings</strong> AND across layers \u2014 strictest restriction wins:<br>'
            + '<span style="color:var(--muted)">\u2022 Threat scanner (Caution) \u2192 threat ceiling</span><br><br>'
            + '<strong>Final authority</strong> = min(best grant, strictest ceiling)'
            + '</div></div>';
          return tabs + '<div class="settings-form">' + accHtml + '</div>' + actions;
        }

        // Ensure all known config sections appear in the form even when not yet configured.
        var CONFIG_SCHEMA = {
          agent: { name: '', id: '', workspace: '', log_level: 'info' },
          server: { bind: 'localhost', port: 18789 },
          database: { path: '' },
          models: { primary: '', fallbacks: [] },
          providers: {},
          channels: { telegram: null, discord: null, whatsapp: null, signal: null, email: null, web: null, voice: null },
          personality: { tone: '', greeting: '' },
          skills: { enabled: true },
          wallet: {},
          treasury: {},
          context_budget: { l0: 4000, l1: 8000, l2: 16000, l3: 32000, channel_minimum: 'L1' }
        };
        Object.keys(CONFIG_SCHEMA).forEach(function(k) {
          if (draft[k] === undefined || draft[k] === null) draft[k] = JSON.parse(JSON.stringify(CONFIG_SCHEMA[k]));
        });

        var fieldTooltips = {
            'name': 'The agent\'s display name, shown in chat and dashboard headers',
            'id': 'Unique identifier for this agent instance',
            'workspace': 'Root directory for agent file operations',
            'log_level': 'Logging verbosity: trace, debug, info, warn, error',
            'port': 'HTTP server port. Default: 18789. Requires restart.',
            'bind': 'Network interface to bind. 127.0.0.1 = local only, 0.0.0.0 = all interfaces.',
            'primary': 'Primary LLM model in provider/model format',
            'path': 'Database file path. Use :memory: for in-memory (dev only).',
            'l0': 'Token budget for trivial queries. Min: 512.',
            'l1': 'Token budget for low complexity queries.',
            'l2': 'Token budget for moderate complexity queries.',
            'l3': 'Token budget for high complexity queries.',
            'soul_max_context_pct': 'Max context budget percentage (0.0-1.0) for personality. Default: 0.4.',
            'channel_minimum': 'Minimum budget tier for channel messages: L0, L1, L2, L3.',
            'tone': 'Personality tone directive for the agent',
            'greeting': 'Initial greeting message for new conversations'
        };
        var html = tabs + '<div class="settings-form">';
        var specialSections = ['models', 'providers', 'wallet'];
        var sections = Object.keys(draft);
        sections.forEach(function(section) {
          var sectionData = draft[section];
          if (typeof sectionData !== 'object' || sectionData === null) return;
          if (section === 'security') return; // rendered in Access Control tab
          var isImmutableSection = !!immutableSet[section];

          if (isImmutableSection) {
            html += '<div class="settings-section"><div class="settings-section-title">' + esc(section) + ' <span class="badge muted" style="font-size:0.6rem">restart required</span></div>'
              + '<div style="font-size:0.75rem;color:var(--muted);margin-bottom:0.5rem">This section is immutable at runtime. Edit &#96;roboticus.toml&#96; and restart.</div>';
            // Render as disabled form fields instead of raw JSON
            Object.keys(sectionData).forEach(function(key) {
              var val = sectionData[key];
              var displayVal = typeof val === 'object' && val !== null ? JSON.stringify(val) : String(val != null ? val : '');
              if (typeof val === 'boolean') {
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div>'
                  + '<div style="display:flex;align-items:center;gap:0.5rem"><div class="toggle ' + (val ? 'on' : '') + '" style="opacity:0.5;pointer-events:none"></div><span style="font-size:0.8125rem;color:var(--muted)">' + (val ? 'On' : 'Off') + '</span></div></div>';
              } else {
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div>'
                  + '<input type="text" class="settings-input" value="' + esc(displayVal) + '" disabled style="opacity:0.6;cursor:not-allowed"></div>';
              }
            });
            html += '</div>';
            return;
          }

          if (section === 'models') {
            html += '<div class="settings-section"><div class="settings-section-title">Models</div>';
            html += self._renderProviderDiscoveryAlerts();
            var routingMode = (sectionData.routing && sectionData.routing.mode) || 'auto';
            html += '<div class="settings-row"><div class="settings-label">Routing</div><select class="settings-input" data-settings-path="models.routing.mode" style="max-width:200px">'
              + '<option value="primary"' + (routingMode === 'primary' ? ' selected' : '') + '>Primary Only</option>'
              + '<option value="fallback"' + (routingMode === 'fallback' ? ' selected' : '') + '>Fallback</option>'
              + '<option value="auto"' + (routingMode === 'auto' || routingMode === 'metascore' || routingMode === 'routed' ? ' selected' : '') + '>Auto / Routed</option>'
              + '</select></div>';
            var primaryVal = sectionData.primary || '';
            var modelOpts = self._renderModelOptionsHtml({
              config: draft,
              includeControlModes: false
            });
            html += '<div class="settings-row"><div class="settings-label">Primary</div><div style="position:relative;width:100%"><input class="settings-input" list="model-list" type="text" data-settings-path="models.primary" value="' + esc(primaryVal) + '" placeholder="Select or type a model"><datalist id="model-list">' + modelOpts + '</datalist></div></div>';
            Object.keys(sectionData).forEach(function(key) {
              if (key === 'mode' || key === 'routing' || key === 'primary') return;
              var val = sectionData[key]; var path = 'models.' + key;
              if (typeof val === 'boolean') {
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(path) + '"' + (val ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (val ? 'On' : 'Off') + '</span></div></div>';
              } else if (typeof val !== 'object') {
                var type = typeof val === 'number' ? 'number' : 'text';
                var sv = (val == null || val === '') ? '' : esc(String(val));
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(path) + '" value="' + sv + '" placeholder="none"></div>';
              }
            });
            html += '</div>';
            return;
          }

          if (section === 'providers') {
            var keystoreLocked = !!cfg._keystore_locked;
            html += '<div class="settings-section"><div class="settings-section-title">Providers</div>';
            if (keystoreLocked) {
              html += '<div style="background:var(--surface-2);border:1px solid var(--warning, #f59e0b);border-radius:0.5rem;padding:0.75rem 1rem;margin-bottom:1rem;display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap">'
                + '<span style="font-size:0.8rem;color:var(--warning, #f59e0b)">\u26a0 Keystore is locked \u2014 key status may be inaccurate and saves will fail.</span>'
                + '<button class="btn secondary" id="keystore-unlock-btn" style="font-size:0.7rem;padding:0.25rem 0.75rem">Unlock keystore</button>'
                + '</div>';
            }
            var providerKeys = Object.keys(sectionData);
            var knownProviders = ['anthropic', 'openai', 'google', 'ollama', 'mistral', 'cohere', 'deepseek', 'groq', 'xai', 'together', 'openrouter'];
            providerKeys.forEach(function(pName) {
              var pData = sectionData[pName];
              if (typeof pData !== 'object' || pData === null) return;
              var keyStatus = pData._key_status || 'missing';
              var keySource = pData._key_source || 'unknown';
              var keyBadge = '';
              if (keystoreLocked && keyStatus === 'missing') {
                keyBadge = '<span class="badge" style="font-size:0.6rem;padding:0.1rem 0.4rem;background:var(--warning, #f59e0b);color:#000">Locked</span>';
              } else if (keyStatus === 'configured') {
                keyBadge = '<span class="badge success" style="font-size:0.6rem;padding:0.1rem 0.4rem">Key: ' + esc(keySource) + '</span>';
              } else if (keyStatus === 'not_required') {
                keyBadge = '<span class="badge muted" style="font-size:0.6rem;padding:0.1rem 0.4rem">Local</span>';
              } else {
                keyBadge = '<span class="badge error" style="font-size:0.6rem;padding:0.1rem 0.4rem">Key missing</span>';
              }
              html += '<div class="settings-provider-card"><div class="settings-provider-header"><span class="settings-provider-name">' + esc(pName) + '</span>' + keyBadge
                + '<button class="btn secondary remove-provider-btn" data-provider="' + esc(pName) + '" style="margin-left:auto;font-size:0.65rem;padding:0.15rem 0.5rem;color:var(--error, #ef4444)" title="Remove provider">\u2715 Remove</button>'
                + '</div>';
              Object.keys(pData).forEach(function(pKey) {
                if (pKey.charAt(0) === '_') return;
                var pVal = pData[pKey]; var pPath = 'providers.' + pName + '.' + pKey;
                if (typeof pVal === 'boolean') {
                  html += '<div class="settings-row"><div class="settings-label">' + esc(pKey) + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(pPath) + '"' + (pVal ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (pVal ? 'On' : 'Off') + '</span></div></div>';
                } else if (typeof pVal === 'object' && pVal !== null) {
                  html += '<div class="settings-row"><div class="settings-label">' + esc(pKey) + '</div><span class="badge muted" style="font-size:0.6rem">' + Object.keys(pVal).length + ' entries</span></div>';
                } else {
                  var type = typeof pVal === 'number' ? 'number' : 'text';
                  var sv = (pVal == null || pVal === '') ? '' : esc(String(pVal));
                  html += '<div class="settings-row"><div class="settings-label">' + esc(pKey) + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(pPath) + '" value="' + sv + '" placeholder="none"></div>';
                }
              });
              if (keyStatus === 'missing' && !keystoreLocked) {
                html += '<div class="key-manage-row" data-provider="' + esc(pName) + '">'
                  + '<input type="password" placeholder="Paste API key\u2026" class="key-input" autocomplete="off">'
                  + '<button class="key-manage-btn save" data-action="save-key">Save to keystore</button>'
                  + '<span class="key-manage-msg"></span></div>';
              } else if (keyStatus === 'configured' && keySource === 'keystore') {
                html += '<div class="key-manage-row" data-provider="' + esc(pName) + '">'
                  + '<span style="font-size:0.7rem;color:var(--muted)">Key stored in encrypted keystore</span>'
                  + '<button class="key-manage-btn remove" data-action="remove-key">Remove</button>'
                  + '<span class="key-manage-msg"></span></div>';
              }
              html += '</div>';
            });
            var providerSuggestions = {};
            knownProviders.forEach(function(name) { providerSuggestions[name] = true; });
            providerKeys.forEach(function(name) { providerSuggestions[name] = true; });
            var providerOptions = Object.keys(providerSuggestions).sort().map(function(name) {
              return '<option value="' + esc(name) + '"></option>';
            }).join('');
            html += '<div class="model-order-add" style="margin-top:0.5rem">'
              + '<input id="add-provider-input" class="settings-input" list="provider-known-list" type="text" placeholder="Add provider (e.g. mistral, cohere, deepseek)">'
              + '<datalist id="provider-known-list">' + providerOptions + '</datalist>'
              + '<button class="btn secondary" id="add-provider">Add Provider</button>'
              + '</div></div>';
            return;
          }

          if (section === 'channels') {
            html += '<div class="settings-section"><div class="settings-section-title">channels</div>';

            // ── Channel metadata: the ONLY place channel-specific formatting lives ──
            var CHANNEL_META = {
              telegram:  { listField: 'allowed_chat_ids',   listLabel: 'Allowed Chat IDs',   placeholder: 'Add chat id (e.g. 123456789)',    validate: function(v) { return /^-?[0-9]+$/.test(v) ? null : 'Chat id must be an integer'; }, coerce: Number },
              whatsapp:  { listField: 'allowed_numbers',    listLabel: 'Allowed Numbers',    placeholder: 'Add number (e.g. +15551234567)',  validate: null, coerce: String },
              signal:    { listField: 'allowed_numbers',    listLabel: 'Allowed Numbers',    placeholder: 'Add number (e.g. +15551234567)',  validate: null, coerce: String },
              discord:   { listField: 'allowed_guild_ids',  listLabel: 'Allowed Guild IDs',  placeholder: 'Add guild id',                    validate: null, coerce: String },
              email:     { listField: 'allowed_senders',    listLabel: 'Allowed Senders',    placeholder: 'Add sender email',                validate: null, coerce: String },
              matrix:    { listField: 'allowed_rooms',      listLabel: 'Allowed Rooms',      placeholder: 'Add room ID (e.g. !abc123:matrix.org)', validate: null, coerce: String }
            };

            // ── Shared list-editor renderer (unchanged logic, channel-agnostic) ──
            var renderListEditor = function(channel, listField, values, suggestions, label, placeholder) {
              var uniqMap = {};
              (Array.isArray(values) ? values : []).forEach(function(v) {
                var asStr = String(v == null ? '' : v).trim();
                if (asStr) uniqMap[asStr] = true;
              });
              var vals = Object.keys(uniqMap);
              var suggMap = {};
              (Array.isArray(suggestions) ? suggestions : []).forEach(function(v) {
                var asStr = String(v == null ? '' : v).trim();
                if (asStr) suggMap[asStr] = true;
              });
              vals.forEach(function(v) { suggMap[v] = true; });
              var optionHtml = Object.keys(suggMap).sort().map(function(v) { return '<option value="' + esc(v) + '"></option>'; }).join('');
              var chipsHtml = vals.map(function(v) {
                return '<span class="badge muted" style="display:inline-flex;align-items:center;gap:0.3rem;margin:0.15rem 0.25rem 0.15rem 0">'
                  + '<span>' + esc(v) + '</span>'
                  + '<button class="btn secondary ch-list-remove" data-ch-name="' + esc(channel) + '" data-ch-field="' + esc(listField) + '" data-ch-value="' + esc(v) + '" style="font-size:0.6rem;padding:0.05rem 0.25rem;line-height:1">x</button>'
                  + '</span>';
              }).join('');
              return '<div class="settings-row"><div class="settings-label">' + esc(label) + '</div><div>'
                + '<div class="model-order-add" style="margin-bottom:0.35rem">'
                + '<input class="settings-input ch-list-input" data-ch-name="' + esc(channel) + '" data-ch-field="' + esc(listField) + '" type="text" placeholder="' + esc(placeholder || 'Add item') + '">'
                + '<button class="btn secondary ch-list-add" data-ch-name="' + esc(channel) + '" data-ch-field="' + esc(listField) + '">Add</button>'
                + '</div>'
                + '<div>' + (chipsHtml || '<span style="font-size:0.72rem;color:var(--muted)">No values configured.</span>') + '</div>'
                + '</div></div>';
            };

            // ── Global channel settings: startup announcements ──
            var startupRaw = sectionData.startup_announcements;
            var startupSelected = [];
            if (Array.isArray(startupRaw)) startupSelected = startupRaw.map(function(v) { return String(v || '').trim().toLowerCase(); }).filter(Boolean);
            else if (typeof startupRaw === 'string') startupSelected = startupRaw.split(',').map(function(v) { return String(v || '').trim().toLowerCase(); }).filter(Boolean);
            var startupSet = {};
            startupSelected.forEach(function(v) { startupSet[v] = true; });
            var startupKnown = Object.keys(CHANNEL_META);
            var startupChecks = startupKnown.map(function(ch) {
              return '<label style="display:inline-flex;align-items:center;gap:0.35rem;margin:0.2rem 0.5rem 0.2rem 0"><input type="checkbox" class="startup-ann-check" data-startup-channel="' + esc(ch) + '"' + (startupSet[ch] ? ' checked' : '') + '> <span style="font-size:0.75rem">' + esc(ch) + '</span></label>';
            }).join('');
            html += '<div class="settings-row"><div class="settings-label">startup_announcements</div><div>'
              + '<div style="display:flex;flex-wrap:wrap">' + startupChecks + '</div>'
              + '<div style="font-size:0.7rem;color:var(--muted);margin-top:0.35rem">Select channels to announce on startup. Leave all unchecked to disable announcements.</div>'
              + '</div></div>';

            // ── Global channel settings: trusted sender IDs ──
            var trustedList = Array.isArray(sectionData.trusted_sender_ids) ? sectionData.trusted_sender_ids.map(function(v) { return String(v || '').trim(); }).filter(Boolean) : [];
            var suggestionSet = {};
            Object.keys(CHANNEL_META).forEach(function(ch) {
              var chData = sectionData[ch] || {};
              var meta = CHANNEL_META[ch];
              if (meta && meta.listField && Array.isArray(chData[meta.listField])) {
                chData[meta.listField].forEach(function(v) { suggestionSet[String(v)] = true; });
              }
            });
            var suggestionOptions = Object.keys(suggestionSet).sort().map(function(v) { return '<option value="' + esc(v) + '"></option>'; }).join('');
            var trustedBadges = trustedList.map(function(v) {
              return '<span class="badge muted" style="display:inline-flex;align-items:center;gap:0.3rem;margin:0.15rem 0.25rem 0.15rem 0">'
                + '<span>' + esc(v) + '</span>'
                + '<button class="btn secondary trusted-sender-remove" data-trusted-sender="' + esc(v) + '" style="font-size:0.6rem;padding:0.05rem 0.25rem;line-height:1">x</button>'
                + '</span>';
            }).join('');
            html += '<div class="settings-row"><div class="settings-label">trusted_sender_ids</div><div>'
              + '<div class="model-order-add" style="margin-bottom:0.35rem">'
              + '<input id="trusted-sender-input" class="settings-input" list="trusted-sender-known-list" type="text" placeholder="Add sender id and click Add">'
              + '<datalist id="trusted-sender-known-list">' + suggestionOptions + '</datalist>'
              + '<button class="btn secondary" id="trusted-sender-add">Add</button>'
              + '</div>'
              + '<div id="trusted-sender-chiplist">' + (trustedBadges || '<span style="font-size:0.72rem;color:var(--muted)">No trusted senders configured.</span>') + '</div>'
              + '</div></div>';

            html += '<hr class="channels-divider">';

            // ── Per-channel subsections: one unified loop ──
            var handledChannelKeys = {};
            var knownChannels = Object.keys(CHANNEL_META);
            knownChannels.forEach(function(ch) {
              handledChannelKeys[ch] = true;
              var meta = CHANNEL_META[ch];
              var chData = sectionData[ch];
              var configured = chData && typeof chData === 'object';

              if (!configured) {
                html += '<div class="channel-subsection disabled">'
                  + '<div class="channel-subsection-title">' + esc(ch) + ' <span class="badge muted">not configured</span></div>'
                  + '<button class="btn secondary channel-enable-btn" data-enable-channel="' + esc(ch) + '" style="font-size:0.72rem">Enable ' + esc(ch.charAt(0).toUpperCase() + ch.slice(1)) + '</button>'
                  + '</div>';
                return;
              }

              html += '<div class="channel-subsection">'
                + '<div class="channel-subsection-title">' + esc(ch) + '</div>';

              // Render the allow-list editor if this channel type has one
              if (meta.listField && chData) {
                var listVals = Array.isArray(chData[meta.listField]) ? chData[meta.listField] : [];
                var listSuggestions = trustedList.concat(listVals.map(function(v) { return String(v); }));
                html += renderListEditor(ch, meta.listField, listVals, listSuggestions, meta.listLabel, meta.placeholder);
              }

              // Render all other properties generically
              if (chData) {
                Object.keys(chData).forEach(function(subKey) {
                  if (meta.listField && subKey === meta.listField) return;
                  var subVal = chData[subKey]; var subPath = 'channels.' + ch + '.' + subKey;
                  var type = typeof subVal === 'number' ? 'number' : 'text';
                  if (typeof subVal === 'boolean') {
                    html += '<div class="settings-row"><div class="settings-label">' + esc(subKey) + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(subPath) + '"' + (subVal ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (subVal ? 'On' : 'Off') + '</span></div></div>';
                  } else if (subVal !== null && typeof subVal !== 'object') {
                    var ssv = (subVal == null || subVal === '') ? '' : esc(String(subVal));
                    html += '<div class="settings-row"><div class="settings-label">' + esc(subKey) + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(subPath) + '" value="' + ssv + '" placeholder="none"></div>';
                  }
                });
              }

              html += '</div>';
            });

            // Render any remaining non-channel keys in the channels section
            Object.keys(sectionData).forEach(function(key) {
              if (key === 'startup_announcements' || key === 'trusted_sender_ids' || handledChannelKeys[key]) return;
              var val = sectionData[key];
              var path = section + '.' + key;
              if (typeof val === 'boolean') {
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(path) + '"' + (val ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (val ? 'On' : 'Off') + '</span></div></div>';
              } else if (typeof val === 'object' && val !== null) {
                html += '<div class="channel-subsection">';
                html += '<div class="channel-subsection-title">' + esc(key) + '</div>';
                Object.keys(val).forEach(function(subKey) {
                  var subVal = val[subKey]; var subPath = path + '.' + subKey;
                  var type = typeof subVal === 'number' ? 'number' : 'text';
                  if (typeof subVal === 'boolean') {
                    html += '<div class="settings-row"><div class="settings-label">' + esc(subKey) + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(subPath) + '"' + (subVal ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (subVal ? 'On' : 'Off') + '</span></div></div>';
                  } else {
                    var ssv = (subVal == null || subVal === '') ? '' : esc(String(subVal));
                    html += '<div class="settings-row"><div class="settings-label">' + esc(subKey) + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(subPath) + '" value="' + ssv + '" placeholder="none"></div>';
                  }
                });
                html += '</div>';
              } else {
                var type = typeof val === 'number' ? 'number' : 'text';
                var sv = (val == null || val === '') ? '' : esc(String(val));
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(path) + '" value="' + sv + '" placeholder="none"></div>';
              }
            });
            html += '</div>';
            return;
          }

          if (section === 'wallet') {
            html += '<div class="settings-section"><div class="settings-section-title">Wallet</div>';
            var chainPresetBtns = '<div style="display:flex;gap:0.375rem;flex-wrap:wrap;margin-bottom:0.75rem">';
            Object.keys(self._CHAIN_PRESETS).forEach(function(name) {
              var preset = self._CHAIN_PRESETS[name];
              var isActive = String(sectionData.chain_id) === String(preset.chain_id);
              chainPresetBtns += '<button class="chain-preset-btn' + (isActive ? ' active' : '') + '" data-chain-preset="' + esc(name) + '">' + esc(name) + '</button>';
            });
            chainPresetBtns += '</div>';
            html += '<div class="settings-row"><div class="settings-label">Chain</div><div style="width:100%">' + chainPresetBtns + '</div></div>';
            Object.keys(sectionData).forEach(function(key) {
              var val = sectionData[key]; var path = 'wallet.' + key;
              if (key === 'private_key' || key === 'mnemonic') return;
              var isRpc = key.toLowerCase().indexOf('rpc') !== -1;
              if (typeof val === 'boolean') {
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(path) + '"' + (val ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (val ? 'On' : 'Off') + '</span></div></div>';
              } else {
                var type = typeof val === 'number' ? 'number' : 'text';
                var sv = (val == null || val === '') ? '' : esc(String(val));
                var ph = isRpc ? 'Auto-populated from chain preset' : 'none';
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(path) + '" value="' + sv + '" placeholder="' + ph + '"></div>';
              }
            });
            html += '</div>';
            return;
          }

          if (section === 'treasury') {
            html += '<div class="settings-section"><div class="settings-section-title">Treasury</div>';
            Object.keys(sectionData).forEach(function(key) {
              if (key === 'revenue_swap') return;
              var val = sectionData[key];
              var path = section + '.' + key;
              if (typeof val === 'boolean') {
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(path) + '"' + (val ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (val ? 'On' : 'Off') + '</span></div></div>';
              } else if (typeof val !== 'object') {
                var type = typeof val === 'number' ? 'number' : 'text';
                var sv = (val == null || val === '') ? '' : esc(String(val));
                html += '<div class="settings-row"><div class="settings-label">' + esc(key) + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(path) + '" value="' + sv + '" placeholder="none"></div>';
              }
            });
            html += '</div>';
            html += self._renderRevenueSwapSettings(sectionData.revenue_swap || {});
            return;
          }

          var ENUM_FIELDS = {
            'agent.log_level': ['trace', 'debug', 'info', 'warn', 'error'],
            'agent.composition_policy': ['autonomous', 'propose', 'manual'],
            'agent.skill_creation_rigor': ['generate', 'validate', 'full'],
            'agent.output_validation_policy': ['strict', 'sample', 'off'],
            'session.scope_mode': ['agent', 'peer', 'group']
          };

          html += '<div class="settings-section"><div class="settings-section-title">' + esc(section) + '</div>';
          Object.keys(sectionData).forEach(function(key) {
            var val = sectionData[key];
            var path = section + '.' + key;
            var tooltipHtml = fieldTooltips[key] ? ' <span title="' + esc(fieldTooltips[key]) + '" style="cursor:help;color:var(--muted);font-size:0.65rem">(?)</span>' : '';
            if (typeof val === 'boolean') {
              html += '<div class="settings-row"><div class="settings-label">' + esc(key) + tooltipHtml + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(path) + '"' + (val ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (val ? 'On' : 'Off') + '</span></div></div>';
            } else if (typeof val === 'object' && val !== null) {
              html += '<div class="settings-nested"><div style="font-size:0.625rem;text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);margin-bottom:0.5rem">' + esc(key) + '</div>';
              Object.keys(val).forEach(function(subKey) {
                var subVal = val[subKey]; var subPath = path + '.' + subKey;
                var type = typeof subVal === 'number' ? 'number' : 'text';
                var subTooltipHtml = fieldTooltips[subKey] ? ' <span title="' + esc(fieldTooltips[subKey]) + '" style="cursor:help;color:var(--muted);font-size:0.65rem">(?)</span>' : '';
                if (typeof subVal === 'boolean') {
                  html += '<div class="settings-row"><div class="settings-label">' + esc(subKey) + subTooltipHtml + '</div><div class="settings-toggle-wrap"><label class="toggle"><input type="checkbox" data-settings-path="' + esc(subPath) + '"' + (subVal ? ' checked' : '') + '><span class="toggle-track"></span></label><span class="settings-toggle-label">' + (subVal ? 'On' : 'Off') + '</span></div></div>';
                } else {
                  var ssv = (subVal == null || subVal === '') ? '' : esc(String(subVal));
                  html += '<div class="settings-row"><div class="settings-label">' + esc(subKey) + subTooltipHtml + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(subPath) + '" value="' + ssv + '" placeholder="none"></div>';
                }
              });
              html += '</div>';
            } else if (ENUM_FIELDS[path]) {
              var opts = ENUM_FIELDS[path];
              var sv = (val == null || val === '') ? '' : String(val);
              var selectHtml = '<select class="settings-input" data-settings-path="' + esc(path) + '">';
              opts.forEach(function(opt) {
                selectHtml += '<option value="' + esc(opt) + '"' + (opt === sv ? ' selected' : '') + '>' + esc(opt) + '</option>';
              });
              selectHtml += '</select>';
              html += '<div class="settings-row"><div class="settings-label">' + esc(key) + tooltipHtml + '</div>' + selectHtml + '</div>';
            } else {
              var type = typeof val === 'number' ? 'number' : 'text';
              var sv = (val == null || val === '') ? '' : esc(String(val));
              html += '<div class="settings-row"><div class="settings-label">' + esc(key) + tooltipHtml + '</div><input class="settings-input" type="' + type + '" data-settings-path="' + esc(path) + '" value="' + sv + '" placeholder="none"></div>';
            }
          });
          html += '</div>';
        });
        html += '</div>' + actions;
        return html;
      });
};
App._setNestedValue = function(obj, path, value) { var parts = path.split('.'); var cur = obj; for (var i = 0; i < parts.length - 1; i++) { if (!cur[parts[i]]) cur[parts[i]] = {}; cur = cur[parts[i]]; } cur[parts[parts.length - 1]] = value; };
App._getNestedValue = function(obj, path) { var parts = path.split('.'); var cur = obj; for (var i = 0; i < parts.length; i++) { if (cur == null) return undefined; cur = cur[parts[i]]; } return cur; };
