  // ── Dynamic Theme System ──────────────────────────────────────────────
  var builtinThemeManifests = [
    {
      id: 'ai-purple',
      name: 'AI Purple (Default)',
      description: 'The original Roboticus control-room palette with cool blues, violet accents, and a crisp operator feel.',
      author: 'Roboticus',
      source: 'builtin',
      swatch: '#c180ff',
      variables: {
        '--bg': '#060e20',
        '--surface': '#081329',
        '--surface-2': '#0c1934',
        '--accent': '#c180ff',
        '--text': '#dee5ff',
        '--muted': '#9baad6',
        '--border': '#38476d'
      }
    },
    {
      id: 'crt-orange',
      name: 'CRT Orange',
      description: 'A warm phosphor console look with amber text, dark glass panels, and bunker-terminal energy.',
      author: 'Roboticus',
      source: 'builtin',
      swatch: '#ff8c00',
      variables: {
        '--bg': '#120b04',
        '--surface': '#1f1307',
        '--surface-2': '#2b1a09',
        '--accent': '#ff8c00',
        '--text': '#ffd9a8',
        '--muted': '#c9a26d',
        '--border': '#6e4720'
      }
    },
    {
      id: 'crt-green',
      name: 'CRT Green',
      description: 'Classic monochrome terminal phosphor with green glow, dense contrast, and old-school ops-room restraint.',
      author: 'Roboticus',
      source: 'builtin',
      swatch: '#00ff41',
      variables: {
        '--bg': '#040d06',
        '--surface': '#07170a',
        '--surface-2': '#0c2110',
        '--accent': '#00ff41',
        '--text': '#bbffcb',
        '--muted': '#71c786',
        '--border': '#1d6a2f'
      }
    },
    {
      id: 'psychedelic',
      name: 'Psychedelic Freakout',
      description: 'A maximalist neon chaos mode with saturated gradients and unmistakably unserious operator vibes.',
      author: 'Roboticus',
      source: 'builtin',
      swatch: 'linear-gradient(135deg,#ff00ff,#00ffcc,#ffff00)',
      variables: {
        '--bg': '#14061d',
        '--surface': '#270d38',
        '--surface-2': '#3a1553',
        '--accent': '#ff4ff8',
        '--text': '#fff4a8',
        '--muted': '#8fffe5',
        '--border': '#ff8ef2'
      }
    }
  ];
  var themeNames = {};
  var loadedThemes = {};  // id -> ThemeManifest
  var themeStyleEl = null; // injected <style> for external themes
  var themeFontEls = {};   // id -> <link> for font loading

  function applyTheme(theme) {
    document.documentElement.setAttribute('data-theme', theme);
    document.querySelectorAll('.theme-option').forEach(function(o) { o.classList.toggle('active', o.getAttribute('data-theme-set') === theme); });
    var nameEl = document.getElementById('theme-name'); if (nameEl) nameEl.textContent = themeNames[theme] || theme;
    // Apply textures if this is an external theme with textures
    applyTextures(theme);
    // Load fonts if needed
    var ext = loadedThemes[theme];
    if (ext && ext.fonts && ext.fonts.length > 0 && !themeFontEls[theme]) {
      ext.fonts.forEach(function(url) {
        var link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = url;
        document.head.appendChild(link);
      });
      themeFontEls[theme] = true;
    }
  }

  function applyTextures(themeId) {
    // Remove any previous texture overlays
    document.querySelectorAll('.theme-texture-overlay').forEach(function(el) { el.remove(); });
    document.body.style.removeProperty('background-image');
    var ext = loadedThemes[themeId];
    if (!ext || !ext.textures) return;
    // Body texture
    if (ext.textures.body && ext.textures.body.type === 'css') {
      document.body.style.backgroundImage = ext.textures.body.value;
    }
    // Surface texture — inject a subtle pattern on card/surface elements via CSS
    if (ext.textures.surface && ext.textures.surface.type === 'css') {
      var style = document.getElementById('theme-texture-css');
      if (!style) { style = document.createElement('style'); style.id = 'theme-texture-css'; document.head.appendChild(style); }
      style.textContent = '[data-theme="' + themeId + '"] .card, [data-theme="' + themeId + '"] .surface { background-image: ' + ext.textures.surface.value + '; }';
    } else {
      var existing = document.getElementById('theme-texture-css');
      if (existing) existing.textContent = '';
    }
  }

  function injectExternalThemeCSS(manifest) {
    if (!themeStyleEl) {
      themeStyleEl = document.createElement('style');
      themeStyleEl.id = 'external-themes-css';
      document.head.appendChild(themeStyleEl);
    }
    var css = '[data-theme="' + manifest.id + '"] {\n';
    Object.keys(manifest.variables).forEach(function(key) {
      css += '  ' + key + ': ' + manifest.variables[key] + ';\n';
    });
    css += '}\n';
    themeStyleEl.textContent += css;
  }

  function buildThemePreview(vars) {
    var bg = vars['--bg'] || '#060e20';
    var surface = vars['--surface'] || '#081329';
    var surface2 = vars['--surface-2'] || '#0c1934';
    var accent = vars['--accent'] || '#c180ff';
    var text = vars['--text'] || '#dee5ff';
    var muted = vars['--muted'] || '#9baad6';
    var border = vars['--border'] || '#38476d';
    var div = document.createElement('div');
    div.className = 'theme-preview';
    var inner = document.createElement('div');
    inner.className = 'theme-preview-inner';
    inner.style.background = bg;
    // Header bar
    var header = document.createElement('div');
    header.className = 'theme-preview-header';
    header.style.cssText = 'background:' + surface + ';border-bottom:1px solid ' + border;
    [accent, muted, muted].forEach(function(c) {
      var dot = document.createElement('div');
      dot.className = 'theme-preview-dot';
      dot.style.background = c;
      header.appendChild(dot);
    });
    inner.appendChild(header);
    // Body with sidebar + content
    var body = document.createElement('div');
    body.className = 'theme-preview-body';
    var sidebar = document.createElement('div');
    sidebar.className = 'theme-preview-sidebar';
    sidebar.style.background = surface;
    body.appendChild(sidebar);
    var content = document.createElement('div');
    content.className = 'theme-preview-content';
    content.style.background = surface2;
    [[text, '70%', 0.6], [muted, '50%', 0.4], [accent, '35%', 0.8]].forEach(function(l) {
      var line = document.createElement('div');
      line.className = 'theme-preview-line';
      line.style.cssText = 'background:' + l[0] + ';width:' + l[1] + ';opacity:' + l[2];
      content.appendChild(line);
    });
    body.appendChild(content);
    inner.appendChild(body);
    div.appendChild(inner);
    return div;
  }

  function addThemeToDropdown(manifest) {
    var dropdown = document.getElementById('theme-dropdown');
    if (!dropdown) return;
    if (dropdown.querySelector('[data-theme-set="' + manifest.id + '"]')) return;
    var btn = document.createElement('button');
    btn.className = 'theme-option';
    btn.setAttribute('data-theme-set', manifest.id);
    // Preview thumbnail — prefer author-provided image, fall back to generated
    var vars = manifest.variables || {};
    if (manifest.thumbnail) {
      var thumb = document.createElement('div');
      thumb.className = 'theme-preview';
      thumb.style.backgroundImage = 'url(' + manifest.thumbnail + ')';
      thumb.style.backgroundSize = 'cover';
      thumb.style.backgroundPosition = 'center';
      btn.appendChild(thumb);
    } else if (!vars['--bg']) {
      var simple = document.createElement('div');
      simple.className = 'theme-preview';
      simple.style.background = manifest.swatch || vars['--accent'] || '#888';
      btn.appendChild(simple);
    } else {
      btn.appendChild(buildThemePreview(vars));
    }
    // Info block
    var info = document.createElement('div');
    info.className = 'theme-info';
    var nameEl = document.createElement('div');
    nameEl.className = 'theme-info-name';
    nameEl.textContent = manifest.name;
    info.appendChild(nameEl);
    if (manifest.description) {
      var desc = document.createElement('div');
      desc.className = 'theme-info-desc';
      desc.textContent = manifest.description;
      info.appendChild(desc);
    }
    if (manifest.author || manifest.source) {
      var meta = document.createElement('div');
      meta.className = 'theme-info-meta';
      if (manifest.author) meta.textContent = 'by ' + manifest.author;
      if (manifest.source) {
        meta.textContent += (meta.textContent ? ' \u00b7 ' : '') + manifest.source;
      }
      info.appendChild(meta);
    }
    btn.appendChild(info);
    dropdown.appendChild(btn);
  }

  function registerThemeManifest(manifest, options) {
    options = options || {};
    loadedThemes[manifest.id] = manifest;
    themeNames[manifest.id] = manifest.name;
    if (options.injectCss) injectExternalThemeCSS(manifest);
    addThemeToDropdown(manifest);
  }

  builtinThemeManifests.forEach(function(theme) {
    registerThemeManifest(theme, { injectCss: false });
  });

  // Load external themes from API
  function loadExternalThemes() {
    fetch('/api/themes').then(function(r) { return r.json(); }).then(function(themes) {
      themes.forEach(function(t) {
        if (themeNames[t.id]) return;
        registerThemeManifest(t, { injectCss: true });
      });
      var saved = localStorage.getItem('roboticus-theme');
      if (saved && loadedThemes[saved]) applyTheme(saved);
    }).catch(function(e) {
      console.warn('Failed to load external themes:', e);
    });
  }

  // ── Theme Catalog (marketplace) ──────────────────────────────────────
  var catalogCache = null;

  function loadCatalog(cb) {
    if (catalogCache) return cb(catalogCache);
    fetch('/api/themes/catalog').then(function(r) { return r.json(); }).then(function(data) {
      catalogCache = data.catalog || [];
      cb(catalogCache);
    }).catch(function(e) {
      console.warn('Failed to load theme catalog:', e);
      cb([]);
    });
  }

  function buildCatalogModalDom() {
    var overlay = document.createElement('div');
    overlay.id = 'theme-catalog-overlay';
    overlay.className = 'theme-catalog-overlay';
    var modal = document.createElement('div');
    modal.className = 'theme-catalog-modal';
    var header = document.createElement('div');
    header.className = 'theme-catalog-header';
    var h3 = document.createElement('h3');
    h3.textContent = 'Theme Catalog';
    header.appendChild(h3);
    var closeBtn = document.createElement('button');
    closeBtn.className = 'theme-catalog-close';
    closeBtn.title = 'Close';
    closeBtn.textContent = '\u00d7';
    closeBtn.addEventListener('click', closeCatalogModal);
    header.appendChild(closeBtn);
    modal.appendChild(header);
    var grid = document.createElement('div');
    grid.className = 'theme-catalog-grid';
    grid.id = 'theme-catalog-grid';
    modal.appendChild(grid);
    overlay.appendChild(modal);
    overlay.addEventListener('click', function(e) { if (e.target === overlay) closeCatalogModal(); });
    document.body.appendChild(overlay);
    return overlay;
  }

  function buildCatalogCard(entry) {
    var card = document.createElement('div');
    card.className = 'theme-catalog-card';
    var swatch = document.createElement('div');
    swatch.className = 'theme-catalog-swatch';
    swatch.style.background = entry.preview_swatch || entry.manifest.swatch || 'var(--surface-2)';
    card.appendChild(swatch);
    var info = document.createElement('div');
    info.className = 'theme-catalog-card-info';
    var name = document.createElement('div');
    name.className = 'theme-catalog-card-name';
    name.textContent = entry.manifest.name || entry.name || entry.manifest.id;
    info.appendChild(name);
    var desc = document.createElement('div');
    desc.className = 'theme-catalog-card-desc';
    desc.textContent = entry.manifest.description || entry.description || '';
    info.appendChild(desc);
    var meta = document.createElement('div');
    meta.className = 'theme-catalog-card-meta';
    meta.textContent = 'v' + (entry.version || '1.0.0') + ' by ' + (entry.manifest.author || entry.author || 'unknown');
    info.appendChild(meta);
    card.appendChild(info);
    var btn = document.createElement('button');
    btn.className = 'theme-catalog-install-btn';
    var isInstalled = entry.installed || !!themeNames[entry.manifest.id || entry.id];
    if (isInstalled) {
      btn.textContent = 'Installed';
      btn.classList.add('installed');
    } else {
      btn.textContent = 'Install';
      btn.addEventListener('click', function() {
        var themeId = entry.manifest.id || entry.id;
        btn.textContent = '...';
        btn.disabled = true;
        fetch('/api/themes/catalog/install', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ id: themeId })
        }).then(function(r) { return r.json(); }).then(function(data) {
          if (data.ok && data.theme) {
            registerThemeManifest(data.theme, { injectCss: true });
            btn.textContent = 'Installed';
            btn.classList.add('installed');
            btn.disabled = false;
            entry.installed = true;
          } else {
            btn.textContent = data.error || 'Error';
            btn.disabled = false;
          }
        }).catch(function() {
          btn.textContent = 'Error';
          btn.disabled = false;
        });
      });
    }
    card.appendChild(btn);
    return card;
  }

  function openCatalogModal() {
    var overlay = document.getElementById('theme-catalog-overlay');
    if (!overlay) overlay = buildCatalogModalDom();
    var grid = document.getElementById('theme-catalog-grid');
    grid.textContent = '';
    var loading = document.createElement('div');
    loading.style.cssText = 'text-align:center;padding:2rem;color:var(--muted);font-size:0.75rem;';
    loading.textContent = 'Loading catalog...';
    grid.appendChild(loading);
    overlay.classList.add('open');

    loadCatalog(function(catalog) {
      grid.textContent = '';
      if (!catalog.length) {
        var empty = document.createElement('div');
        empty.style.cssText = 'text-align:center;padding:2rem;color:var(--muted);font-size:0.75rem;';
        empty.textContent = 'No catalog themes available.';
        grid.appendChild(empty);
        return;
      }
      catalog.forEach(function(entry) {
        grid.appendChild(buildCatalogCard(entry));
      });
    });
  }

  function closeCatalogModal() {
    var overlay = document.getElementById('theme-catalog-overlay');
    if (overlay) overlay.classList.remove('open');
  }

  // ── Init ──────────────────────────────────────────────────────────────

  var savedTheme = localStorage.getItem('roboticus-theme');
  if (savedTheme && themeNames[savedTheme]) applyTheme(savedTheme);

  loadExternalThemes();

  var themeToggle = document.getElementById('theme-toggle');
  var themeDropdown = document.getElementById('theme-dropdown');
  if (themeToggle && themeDropdown) {
    themeToggle.addEventListener('click', function(e) { e.stopPropagation(); themeDropdown.classList.toggle('open'); });
    document.addEventListener('click', function(e) {
      if (!e.target.closest('#theme-catalog-overlay')) themeDropdown.classList.remove('open');
    });
    themeDropdown.addEventListener('click', function(e) {
      var opt = e.target.closest('[data-theme-set]'); if (!opt) return;
      var theme = opt.getAttribute('data-theme-set');
      applyTheme(theme);
      localStorage.setItem('roboticus-theme', theme);
      themeDropdown.classList.remove('open');
      if (App.page === 'overview') App.navigate('overview');
    });

    // Add "Browse catalog" button at bottom of theme dropdown
    var browseBtn = document.createElement('button');
    browseBtn.className = 'theme-catalog-browse-btn';
    browseBtn.textContent = '+ Browse catalog';
    browseBtn.addEventListener('click', function(e) {
      e.stopPropagation();
      themeDropdown.classList.remove('open');
      openCatalogModal();
    });
    themeDropdown.appendChild(browseBtn);
  }
