App.renderSkills = function() {
      var self = this;
      var tab = self._skillsTab || 'installed';
      if (tab !== 'installed' && tab !== 'catalog') tab = 'installed';
      var tabs = '<div class="tabs" style="margin-bottom:1rem">'
        + '<button class="' + (tab === 'installed' ? 'active' : '') + '" data-skills-tab="installed">Installed</button>'
        + '<button class="' + (tab === 'catalog' ? 'active' : '') + '" data-skills-tab="catalog">Catalog</button>'
        + '</div>';
      return Promise.all([
        api('/api/skills'),
        fetchWithFallback('/api/skills/catalog', { items: [] }, 'skills-catalog'),
        api('/api/plugins').catch(function() { return { plugins: [] }; })
      ]).then(function(results) {
        var data = results[0] || {};
        var catalogData = (results[1] && results[1].data) ? results[1].data : { items: [] };
        var installedPlugins = (results[2] && results[2].plugins) || [];
        var skills = data.skills || [];
        var catalogItems = (catalogData.items || []).filter(function(i) { return (i.source || '') === 'registry'; });
        var installedSkillSet = {};
        skills.forEach(function(s) {
          var skillName = (s && s.name) ? String(s.name).trim().toLowerCase() : '';
          if (skillName) installedSkillSet[skillName] = true;
        });

        var catalogHtml = '';
        if (catalogItems.length > 0) {
          var catRows = catalogItems.map(function(item) {
            var name = item.name || item.filename || '';
            var description = String(item.description || '').trim();
            var summary = description || 'No description provided for this catalog entry yet.';
            var metaParts = [];
            if (item.kind) metaParts.push(String(item.kind));
            if (item.version) metaParts.push('v' + String(item.version));
            if (item.tags && item.tags.length) metaParts.push(String(item.tags.slice(0, 4).join(', ')));
            var metaLine = metaParts.length ? metaParts.join(' • ') : '';
            var searchText = [
              name,
              summary,
              item.kind || '',
              item.version || '',
              (item.tags && item.tags.length) ? item.tags.join(' ') : ''
            ].join(' ').toLowerCase();
            var isInstalled = !!installedSkillSet[String(name).trim().toLowerCase()];
            var installedMark = isInstalled
              ? '<span class="badge success" style="font-size:0.62rem;padding:0.08rem 0.35rem">installed</span>'
              : '';
            return '<label data-catalog-row="1" data-catalog-search="' + esc(searchText) + '" style="display:flex;align-items:flex-start;gap:0.6rem;padding:0.6rem 0.65rem;border:1px solid var(--border-soft);border-radius:6px;background:rgba(255,255,255,0.01)">'
              + '<input type="checkbox" class="cat-skill-check" value="' + esc(name) + '"' + (isInstalled ? ' checked' : '') + ' data-installed="' + (isInstalled ? '1' : '0') + '">'
              + '<div style="min-width:0;flex:1">'
              + '<div style="display:flex;align-items:center;gap:0.4rem;flex-wrap:wrap">'
              + '<span style="font-family:var(--font-mono);font-size:0.82rem;color:var(--text)">' + esc(name) + '</span>'
              + installedMark
              + '</div>'
              + '<div style="margin-top:0.18rem;font-size:0.75rem;color:var(--muted);line-height:1.35">' + esc(summary) + '</div>'
              + (metaLine ? '<div style="margin-top:0.2rem;font-size:0.68rem;color:var(--text-dim);text-transform:uppercase;letter-spacing:0.04em">' + esc(metaLine) + '</div>' : '')
              + '</div>'
              + '</label>';
          }).join('');
          catalogHtml = ''
            + '<div class="card" style="margin-bottom:1rem">'
            + '  <div style="display:flex;justify-content:space-between;align-items:center;gap:0.75rem;flex-wrap:wrap">'
            + '    <div><div class="card-title">catalog</div><div style="font-size:0.75rem;color:var(--muted)">registry skills (' + catalogItems.length + ')</div></div>'
            + '    <div style="display:flex;gap:0.5rem;flex-wrap:wrap">'
            + '      <button class="btn secondary" id="btn-catalog-refresh">' + uiBtnLabel('refresh', 'Refresh Catalog') + '</button>'
            + '      <button class="btn secondary" id="btn-catalog-install">' + uiBtnLabel('download', 'Install selected') + '</button>'
            + '      <button class="btn secondary" id="btn-catalog-activate">' + uiBtnLabel('power', 'Activate selected') + '</button>'
            + '      <button class="btn" id="btn-catalog-install-activate">' + uiBtnLabel('spark', 'Install + Activate') + '</button>'
            + '    </div>'
            + '  </div>'
            + '  <div style="margin-top:0.75rem">'
            + '    <input type="text" id="catalog-filter-input" placeholder="Search catalog by name, description, tags..."'
            + '      style="width:100%;padding:0.5rem 0.65rem;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-family:var(--font)">'
            + '  </div>'
            + '  <div style="margin-top:0.75rem;max-height:min(62vh,680px);overflow:auto;padding-right:0.25rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(360px,1fr));gap:0.55rem">' + catRows + '</div>'
            + '  <div id="catalog-filter-empty" style="display:none;margin-top:0.5rem;font-size:0.78rem;color:var(--muted)">No catalog skills match this filter.</div>'
            + '</div>';
        } else {
          catalogHtml = ''
            + '<div class="card" style="margin-bottom:1rem">'
            + '  <div style="display:flex;justify-content:space-between;align-items:center;gap:0.75rem;flex-wrap:wrap">'
            + '    <div><div class="card-title">catalog</div><div style="font-size:0.75rem;color:var(--muted)">registry skills (0)</div></div>'
            + '    <div style="display:flex;gap:0.5rem;flex-wrap:wrap">'
            + '      <button class="btn secondary" id="btn-catalog-refresh">' + uiBtnLabel('refresh', 'Refresh Catalog') + '</button>'
            + '    </div>'
            + '  </div>'
            + '  <div style="margin-top:0.75rem;font-size:0.8125rem;color:var(--muted)">No registry catalog skills available.</div>'
            + '</div>';
        }

        // ── Plugins section ──
        // Merge catalog plugins with locally installed plugins
        var pluginItems = catalogData.plugins || [];
        var catalogPluginNames = {};
        pluginItems.forEach(function(p) { catalogPluginNames[(p.name || '').toLowerCase()] = true; });
        // Add installed plugins not already in catalog
        installedPlugins.forEach(function(p) {
          if (!catalogPluginNames[(p.name || '').toLowerCase()]) {
            pluginItems.push({
              name: p.name,
              description: (p.tools && p.tools.length > 0 ? p.tools.map(function(t) { return t.name; }).join(', ') : 'Installed plugin'),
              version: '',
              tier: 'installed',
              author: '',
              status: p.status,
              tools: p.tools,
            });
          }
        });
        var pluginsHtml = '';
        if (pluginItems.length > 0) {
          var pluginRows = pluginItems.map(function(p) {
            var name = p.name || '';
            var desc = p.description || '';
            var version = p.version || '';
            var tier = p.tier || '';
            var author = p.author || '';
            var tierBadge = tier === 'official'
              ? '<span class="badge success" style="font-size:0.62rem;padding:0.08rem 0.35rem">official</span>'
              : tier === 'community'
                ? '<span class="badge" style="font-size:0.62rem;padding:0.08rem 0.35rem;background:var(--accent);color:#fff">community</span>'
                : '<span class="badge" style="font-size:0.62rem;padding:0.08rem 0.35rem">' + esc(tier) + '</span>';
            return '<div style="display:flex;align-items:flex-start;gap:0.6rem;padding:0.6rem 0.65rem;border:1px solid var(--border-soft);border-radius:6px;background:rgba(255,255,255,0.01)">'
              + '<div style="min-width:0;flex:1">'
              + '<div style="display:flex;align-items:center;gap:0.4rem;flex-wrap:wrap">'
              + '<span style="font-family:var(--font-mono);font-size:0.82rem;color:var(--text)">' + esc(name) + '</span>'
              + tierBadge
              + '<span style="font-size:0.68rem;color:var(--text-dim)">v' + esc(version) + '</span>'
              + '</div>'
              + '<div style="margin-top:0.18rem;font-size:0.75rem;color:var(--muted);line-height:1.35">' + esc(desc) + '</div>'
              + (author ? '<div style="margin-top:0.15rem;font-size:0.68rem;color:var(--text-dim)">by ' + esc(author) + '</div>' : '')
              + '</div>'
              + (tier === 'installed'
                ? '<span class="badge success" style="font-size:0.7rem;padding:0.25rem 0.5rem">\u2714 Installed</span>'
                : '<button class="btn secondary" data-plugin-install="' + esc(name) + '" style="font-size:0.75rem;padding:0.3rem 0.6rem;white-space:nowrap">' + uiBtnLabel('download', 'Install') + '</button>')
              + '</div>';
          }).join('');
          pluginsHtml = ''
            + '<div class="card" style="margin-bottom:1rem">'
            + '  <div style="display:flex;justify-content:space-between;align-items:center;gap:0.75rem;flex-wrap:wrap">'
            + '    <div><div class="card-title">plugins</div><div style="font-size:0.75rem;color:var(--muted)">available plugins (' + pluginItems.length + ')</div></div>'
            + '  </div>'
            + '  <div style="margin-top:0.75rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(360px,1fr));gap:0.55rem">' + pluginRows + '</div>'
            + '</div>';
        }

        if (tab === 'catalog') {
          return tabs + catalogHtml + pluginsHtml;
        }

        if (skills.length === 0) {
          return tabs + '<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap"><button class="btn" id="btn-reload-skills">' + uiBtnLabel('refresh', 'Reload skills') + '</button><button class="btn secondary" id="btn-create-skill">' + uiBtnLabel('add', 'Create Skill') + '</button><span style="font-size:0.8125rem;color:var(--muted)">0 / 0 enabled</span></div><div class="card" style="margin-top:1rem;color:var(--muted)">No skills registered. Add skills on disk and click Reload skills.</div>';
        }
        var enabledCount = skills.filter(function(s) { return s.enabled; }).length;
        var sortedSkills = skills.slice().sort(function(a, b) {
          var aBuiltIn = !!a.built_in || String(a.kind || '').toLowerCase() === 'builtin';
          var bBuiltIn = !!b.built_in || String(b.kind || '').toLowerCase() === 'builtin';
          if (aBuiltIn !== bBuiltIn) return aBuiltIn ? 1 : -1;
          return String(a.name || '').localeCompare(String(b.name || ''), undefined, { sensitivity: 'base' });
        });
        var cards = sortedSkills.map(function(s) {
          var kindColor = s.kind === 'tool' ? 'var(--accent)' : s.kind === 'cognitive' ? 'var(--success)' : s.kind === 'multimodal' ? 'var(--warning)' : 'var(--muted)';
          var actions = '';
          var builtIn = !!s.built_in || String(s.kind || '').toLowerCase() === 'builtin';
          var kindLabel = builtIn ? 'builtin' : (s.kind || '');
          var builtInBadge = '';
          var builtInNameTag = '';
          var toggleDisabled = '';
          var toggleTitle = '';
          var toggleClass = 'toggle';
          if (s.id && !builtIn) {
            actions = '<div style="display:flex;justify-content:flex-end;gap:0.5rem;margin-top:0.75rem">'
              + '<button class="btn secondary" data-skill-edit-open="' + esc(s.id || '') + '" data-skill-name="' + esc(s.name || '') + '" style="font-size:0.75rem;padding:0.25rem 0.6rem">' + uiBtnLabel('edit', 'Edit') + '</button>'
              + '<button class="btn secondary" data-skill-delete="' + esc(s.id || '') + '" data-skill-name="' + esc(s.name || '') + '" style="border-color:var(--error);color:var(--error);font-size:0.75rem;padding:0.25rem 0.6rem">' + uiBtnLabel('trash', 'Delete') + '</button>'
              + '</div>';
          }
          var usageCount = s.usage_count != null ? s.usage_count : 0;
          var lastUsedLabel = formatTimestampLabel(s.last_used_at);
          var toggleHtml = builtIn
            ? '<span class="badge" style="background:var(--success);color:#fff;font-size:0.7rem;padding:0.15rem 0.5rem;border-radius:0.25rem">ALWAYS ON</span>'
            : '<label class="' + toggleClass + '"><input type="checkbox" data-skill-toggle="' + esc(s.id || '') + '" data-skill-name="' + esc(s.name || '') + '"' + (s.enabled ? ' checked' : '') + toggleDisabled + toggleTitle + '><span class="toggle-track"></span></label>';
          return '<div class="card skill-card" data-skill-open="' + esc(s.id || '') + '" data-skill-name="' + esc(s.name || '') + '"><div class="skill-header"><div class="skill-info"><div class="card-title" style="color:' + kindColor + '">' + esc(kindLabel) + '</div><div class="card-value">' + esc(s.name || '') + '</div></div>' + toggleHtml + '</div><p style="font-size:0.8125rem;color:var(--muted);margin-top:0.5rem">' + esc(s.description || '') + '</p><div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0.5rem;margin-top:0.75rem"><div style="padding:0.55rem 0.65rem;background:var(--surface);border:1px solid var(--border-soft);border-radius:6px"><div style="font-size:0.65rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em">Usage</div><div style="font-size:1rem;font-weight:700;color:var(--text)">' + esc(String(usageCount)) + '</div></div><div style="padding:0.55rem 0.65rem;background:var(--surface);border:1px solid var(--border-soft);border-radius:6px"><div style="font-size:0.65rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em">Last Used</div><div style="font-size:0.78rem;font-weight:600;color:var(--text)">' + esc(lastUsedLabel) + '</div></div></div>' + actions + '</div>';
        }).join('');
        return tabs + '<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap"><button class="btn" id="btn-reload-skills">' + uiBtnLabel('refresh', 'Reload skills') + '</button><button class="btn secondary" id="btn-create-skill">' + uiBtnLabel('add', 'Create Skill') + '</button><span style="font-size:0.8125rem;color:var(--muted)">' + enabledCount + ' / ' + skills.length + ' enabled</span></div><div class="skills-grid" style="margin-top:1rem">' + cards + '</div>';
      });
};

App._closeSkillDeleteModal = function() {
      var modal = document.getElementById('skill-delete-modal');
      if (modal) modal.remove();
};

App._closeSkillDetailModal = function() {
      var modal = document.getElementById('skill-detail-modal');
      if (modal) modal.remove();
};

App._openSkillDetailModal = function(skillId, skillName) {
      var self = this;
      if (!skillId) {
        toast('Skill definition is unavailable for this entry');
        return;
      }
      self._closeSkillDetailModal();
      api('/api/skills/' + encodeURIComponent(skillId)).then(function(skill) {
        var modal = document.createElement('div');
        modal.id = 'skill-detail-modal';
        modal.style.position = 'fixed';
        modal.style.inset = '0';
        modal.style.zIndex = '1000';
        modal.style.background = 'rgba(0,0,0,0.65)';
        modal.style.display = 'flex';
        modal.style.alignItems = 'center';
        modal.style.justifyContent = 'center';
        modal.style.padding = '1.5rem';

        var sourceContent = skill.source_content || '';
        var sourcePath = skill.source_path || '';
        var summary = skill.description || 'No description available.';
        var kindLabel = skill.built_in ? 'builtin' : (skill.kind || 'skill');
        var canEdit = !!sourcePath && !skill.built_in;
        var metaBadges = ''
          + '<span class="badge muted">' + esc(kindLabel) + '</span>'
          + '<span class="badge">' + esc(skill.risk_level || 'Unknown') + '</span>'
          + '<span class="badge ' + (skill.enabled ? 'success' : 'muted') + '">' + (skill.enabled ? 'enabled' : 'disabled') + '</span>';

        var html = ''
          + '<div style="width:min(960px, 100%);max-height:calc(100vh - 3rem);overflow:hidden;background:var(--bg);border:1px solid var(--border);border-radius:8px;box-shadow:0 20px 40px rgba(0,0,0,0.45);display:flex;flex-direction:column">'
          + '  <div style="padding:1rem 1.25rem;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;justify-content:space-between;gap:1rem">'
          + '    <div style="min-width:0">'
          + '      <div style="font-weight:700;font-size:1rem">' + esc(skill.name || skillName || 'Skill') + '</div>'
          + '      <div style="margin-top:0.35rem;display:flex;gap:0.4rem;flex-wrap:wrap">' + metaBadges + '</div>'
          + '      <div style="margin-top:0.6rem;font-size:0.85rem;color:var(--muted);line-height:1.45">' + esc(summary) + '</div>'
          + '    </div>'
          + '    <div style="display:flex;gap:0.5rem;align-items:center">'
          + (canEdit ? '<button class="btn secondary" data-skill-edit-start style="padding:0.25rem 0.6rem">Edit</button>' : '')
          + '      <button class="btn secondary" data-skill-detail-close style="padding:0.25rem 0.6rem">Close</button>'
          + '    </div>'
          + '  </div>'
          + '  <div style="padding:1rem 1.25rem;overflow:auto">'
          + (sourcePath ? '<div style="margin-bottom:0.75rem;font-size:0.75rem;color:var(--muted)">Source: <span style="font-family:var(--font-mono);color:var(--text)">' + esc(sourcePath) + '</span></div>' : '')
          + (sourceContent
            ? '<div data-skill-source-view>'
              + '<div style="padding:1rem;background:var(--surface);border:1px solid var(--border);border-radius:6px;line-height:1.6;font-size:0.85rem" class="md-content">' + renderSafeMarkdown(sourceContent) + '</div>'
              + '</div>'
            : '<div class="card" style="color:var(--muted)">No source definition is available for this skill.</div>')
          + (canEdit
            ? '<div data-skill-source-editor style="display:none">'
              + '<textarea id="skill-detail-editor" style="width:100%;min-height:420px;padding:1rem;background:var(--surface);border:1px solid var(--border);border-radius:6px;white-space:pre;font-family:var(--font-mono);font-size:0.78rem;line-height:1.45;color:var(--text);resize:vertical">' + esc(sourceContent) + '</textarea>'
              + '<div style="display:flex;justify-content:flex-end;gap:0.5rem;margin-top:0.75rem">'
              + '<button class="btn secondary" data-skill-edit-cancel>Cancel</button>'
              + '<button class="btn" data-skill-edit-save="' + esc(skill.id || '') + '">Save</button>'
              + '</div>'
              + '</div>'
            : '')
          + '  </div>'
          + '</div>';
        setHtml(modal, html);
        document.body.appendChild(modal);
        modal.addEventListener('click', function(ev) {
          if (ev.target.closest('[data-skill-edit-start]')) {
            var view = modal.querySelector('[data-skill-source-view]');
            var editor = modal.querySelector('[data-skill-source-editor]');
            var editBtn = modal.querySelector('[data-skill-edit-start]');
            if (view) view.style.display = 'none';
            if (editor) editor.style.display = 'block';
            if (editBtn) editBtn.style.display = 'none';
            var textarea = document.getElementById('skill-detail-editor');
            if (textarea) textarea.focus();
            return;
          }
          if (ev.target.closest('[data-skill-edit-cancel]')) {
            var viewCancel = modal.querySelector('[data-skill-source-view]');
            var editorCancel = modal.querySelector('[data-skill-source-editor]');
            var editBtnCancel = modal.querySelector('[data-skill-edit-start]');
            if (viewCancel) viewCancel.style.display = '';
            if (editorCancel) editorCancel.style.display = 'none';
            if (editBtnCancel) editBtnCancel.style.display = '';
            return;
          }
          var saveBtn = ev.target.closest('[data-skill-edit-save]');
          if (saveBtn) {
            var skillEditId = saveBtn.getAttribute('data-skill-edit-save') || '';
            var textareaSave = document.getElementById('skill-detail-editor');
            var nextSource = textareaSave ? textareaSave.value : '';
            saveBtn.disabled = true;
            api('/api/skills/' + encodeURIComponent(skillEditId), {
              method: 'PUT',
              headers: authHeaders({ 'Content-Type': 'application/json', 'Accept': 'application/json' }),
              body: JSON.stringify({ source_content: nextSource })
            }).then(function() {
              toast('Skill updated');
              self._closeSkillDetailModal();
              App.navigate('skills');
              self._openSkillDetailModal(skillEditId, skill.name || skillName);
            }).catch(function(err) {
              toast(err.message || 'Failed to update skill');
            }).finally(function() {
              saveBtn.disabled = false;
            });
            return;
          }
          if (ev.target === modal || ev.target.closest('[data-skill-detail-close]')) {
            self._closeSkillDetailModal();
          }
        });
      }).catch(function(err) {
        toast(err.message || 'Failed to load skill definition');
      });
};

App._closeSkillEditorModal = function() {
      var modal = document.getElementById('skill-editor-modal');
      if (modal) modal.remove();
};

App._openSkillEditorModal = function(skillId, existingContent) {
      // skillId === null  → create mode (POST)
      // skillId !== null  → edit mode   (PUT)
      var self = this;
      self._closeSkillEditorModal();
      var isCreate = !skillId;
      var defaultTemplate = '---\n# Skill name: lowercase, hyphenated (e.g., "combat-tactics")\nname: my-new-skill\n\n# Brief description of what this skill teaches the agent\ndescription: Describe what this skill does\n\n# Kind: "instruction" (behavioral guidance), "operational" (tool/process), or "knowledge" (reference material)\nkind: instruction\n\n# Triggers: keywords that activate this skill when they appear in user messages\ntriggers:\n  keywords:\n    - "keyword1"\n    - "keyword2"\n---\n\n# Skill Title\n\nWrite your skill instructions here. The agent will follow these\ninstructions when the trigger keywords match a user\\\'s message.\n\n## Section Heading\n\nUse markdown formatting for structure. The agent reads this content\nas guidance for how to behave, what to prioritize, and what patterns\nto follow.\n';
      var initialContent = existingContent != null ? existingContent : defaultTemplate;

      var modal = document.createElement('div');
      modal.id = 'skill-editor-modal';
      modal.style.position = 'fixed';
      modal.style.inset = '0';
      modal.style.zIndex = '1000';
      modal.style.background = 'rgba(0,0,0,0.65)';
      modal.style.display = 'flex';
      modal.style.alignItems = 'center';
      modal.style.justifyContent = 'center';
      modal.style.padding = '1.5rem';

      var html = ''
        + '<div style="width:min(780px,100%);max-height:calc(100vh - 3rem);overflow:hidden;background:var(--bg);border:1px solid var(--border);border-radius:8px;box-shadow:0 20px 40px rgba(0,0,0,0.45);display:flex;flex-direction:column">'
        + '  <div style="padding:1rem 1.25rem;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:1rem">'
        + '    <div style="font-weight:700;font-size:1rem">' + (isCreate ? 'Create Skill' : 'Edit Skill') + '</div>'
        + '    <button class="btn secondary" data-skill-editor-close style="padding:0.25rem 0.6rem">Close</button>'
        + '  </div>'
        + '  <div style="padding:1rem 1.25rem;overflow:auto;display:flex;flex-direction:column;gap:0.75rem">'
        + '    <div style="font-size:0.78rem;color:var(--muted);line-height:1.45">Write your skill in YAML+Markdown format. The YAML frontmatter (between <code>---</code> markers) defines metadata; the body below is the skill content.</div>'
        + '    <textarea id="skill-editor-textarea"'
        + '      style="width:100%;min-height:420px;padding:1rem;background:var(--surface);border:1px solid var(--border);border-radius:6px;font-family:var(--font-mono);font-size:0.78rem;line-height:1.55;color:var(--text);resize:vertical;tab-size:2"'
        + '    >' + esc(initialContent) + '</textarea>'
        + '    <div style="display:flex;justify-content:flex-end;gap:0.5rem">'
        + '      <button class="btn secondary" data-skill-editor-cancel>Cancel</button>'
        + '      <button class="btn" data-skill-editor-save="' + esc(skillId || '') + '" data-skill-editor-mode="' + (isCreate ? 'create' : 'edit') + '">' + (isCreate ? 'Create' : 'Save') + '</button>'
        + '    </div>'
        + '  </div>'
        + '</div>';

      setHtml(modal, html);
      document.body.appendChild(modal);

      var textarea = document.getElementById('skill-editor-textarea');
      if (textarea) textarea.focus();

      modal.addEventListener('click', function(ev) {
        if (ev.target === modal || ev.target.closest('[data-skill-editor-close]') || ev.target.closest('[data-skill-editor-cancel]')) {
          self._closeSkillEditorModal();
          return;
        }
        var saveBtn = ev.target.closest('[data-skill-editor-save]');
        if (saveBtn) {
          var editId = saveBtn.getAttribute('data-skill-editor-save') || '';
          var mode = saveBtn.getAttribute('data-skill-editor-mode') || 'create';
          var ta = document.getElementById('skill-editor-textarea');
          var content = ta ? ta.value : '';
          saveBtn.disabled = true;
          var url = mode === 'create' ? '/api/skills' : '/api/skills/' + encodeURIComponent(editId);
          var method = mode === 'create' ? 'POST' : 'PUT';
          fetch(url, {
            method: method,
            headers: authHeaders({ 'Content-Type': 'application/json', 'Accept': 'application/json' }),
            body: JSON.stringify({ source_content: content })
          }).then(function(resp) {
            if (!resp.ok) {
              return resp.text().then(function(t) { throw new Error(t || resp.statusText); });
            }
            return resp.json();
          }).then(function() {
            toast(mode === 'create' ? 'Skill created' : 'Skill updated');
            self._closeSkillEditorModal();
            App.refreshSkills();
          }).catch(function(err) {
            toast(err.message || 'Failed to save skill');
          }).finally(function() {
            saveBtn.disabled = false;
          });
          return;
        }
      });
};

App._openSkillDeleteModal = function(skillId, skillName) {
      var self = this;
      self._closeSkillDeleteModal();
      var modal = document.createElement('div');
      modal.id = 'skill-delete-modal';
      modal.style.position = 'fixed';
      modal.style.inset = '0';
      modal.style.zIndex = '1000';
      modal.style.background = 'rgba(0,0,0,0.65)';
      modal.style.display = 'flex';
      modal.style.alignItems = 'center';
      modal.style.justifyContent = 'center';
      modal.style.padding = '1.5rem';

      var html = ''
        + '<div style="width:min(520px, 100%);background:var(--bg);border:1px solid var(--border);border-radius:8px;box-shadow:0 20px 40px rgba(0,0,0,0.45)">'
        + '  <div style="padding:1rem 1.25rem;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:1rem">'
        + '    <div style="font-weight:700">Delete Skill</div>'
        + '    <button class="btn secondary" data-skill-delete-cancel style="padding:0.25rem 0.6rem">Close</button>'
        + '  </div>'
        + '  <div style="padding:1rem 1.25rem;color:var(--muted);font-size:0.9rem;line-height:1.45">'
        + '    This action permanently removes the skill record from the runtime database.'
        + '  </div>'
        + '  <div style="padding:0 1.25rem 1rem 1.25rem">'
        + '    <div style="margin-bottom:0.45rem;font-size:0.75rem;text-transform:uppercase;color:var(--muted);letter-spacing:0.05em">Confirm skill name</div>'
        + '    <div style="margin-bottom:0.6rem;font-family:var(--font-mono);font-size:0.9rem;color:var(--text)">' + esc(skillName) + '</div>'
        + '    <input id="skill-delete-confirm-input" type="text" placeholder="Type exact skill name"'
        + '      style="width:100%;padding:0.55rem 0.7rem;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-family:var(--font-mono)">'
        + '    <div id="skill-delete-confirm-hint" style="margin-top:0.5rem;font-size:0.75rem;color:var(--muted)">Type the exact name to enable deletion.</div>'
        + '  </div>'
        + '  <div style="padding:0.9rem 1.25rem;border-top:1px solid var(--border);display:flex;gap:0.6rem;justify-content:flex-end">'
        + '    <button class="btn secondary" data-skill-delete-cancel>Cancel</button>'
        + '    <button class="btn" id="skill-delete-confirm-btn" disabled style="border-color:var(--error);color:var(--error);opacity:0.45;pointer-events:none">Delete Skill</button>'
        + '  </div>'
        + '</div>';
      setHtml(modal, html);
      document.body.appendChild(modal);

      var input = document.getElementById('skill-delete-confirm-input');
      var btn = document.getElementById('skill-delete-confirm-btn');
      var hint = document.getElementById('skill-delete-confirm-hint');
      if (input) input.focus();

      function syncState() {
        if (!input || !btn) return;
        var matches = input.value === skillName;
        btn.disabled = !matches;
        btn.style.opacity = matches ? '' : '0.45';
        btn.style.pointerEvents = matches ? '' : 'none';
        if (hint) {
          hint.style.color = matches ? 'var(--success)' : 'var(--muted)';
          hint.textContent = matches ? 'Name matches. You can delete this skill.' : 'Type the exact name to enable deletion.';
        }
      }

      if (input) {
        input.addEventListener('input', syncState);
        input.addEventListener('keydown', function(ev) {
          if (ev.key === 'Enter' && btn && !btn.disabled) btn.click();
        });
      }

      modal.addEventListener('click', function(ev) {
        if (ev.target === modal || ev.target.closest('[data-skill-delete-cancel]')) {
          self._closeSkillDeleteModal();
          return;
        }
        if (ev.target.closest('#skill-delete-confirm-btn')) {
          api('/api/skills/' + encodeURIComponent(skillId), { method: 'DELETE' })
            .then(function(resp) {
              self._closeSkillDeleteModal();
              toast('Deleted skill: ' + (resp.name || skillName));
              App.navigate('skills');
            })
            .catch(function(err) {
              toast(err.message || 'Failed to delete skill');
            });
        }
      });
};

