/**
 * ICD Test Runner — UI
 * Render functions, suite dispatcher, provider setup panel, fixtures panel, export.
 */
(function () {
  'use strict';
  var T = window.ICD;
  if (!T) return;
  var $ = T.$;
  var esc = T.esc;

  // ─── Provider Setup UI Panel ──────────────────────────────

  T.renderProviderSetup = function () {
    if (!T.el.providerSetup) return;
    T.el.providerSetup.innerHTML = '';

    var cardStyle = {
      background: 'var(--bg-surface)', border: '1px solid var(--border)',
      borderRadius: 'var(--radius-lg)', padding: '16px', marginBottom: '0'
    };

    var card = $('div', { style: cardStyle });

    // Header row
    var hdr = $('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '12px' } }, [
      $('div', { style: { fontSize: '13px', fontWeight: '600', color: 'var(--text)' } }, 'Provider Test Setup'),
      T.providerSetup.configured
        ? $('span', { className: 'badge badge-success', style: { fontSize: '11px' } },
            esc(T.providerSetup.provider) + ' configured')
        : $('span', { style: { fontSize: '11px', color: 'var(--text-3)' } }, 'Not configured — optional')
    ]);
    card.appendChild(hdr);

    // Input row
    var inputRow = $('div', { style: { display: 'flex', gap: '10px', alignItems: 'flex-end', flexWrap: 'wrap' } });

    // Provider select
    var selWrap = $('div', { style: { display: 'flex', flexDirection: 'column', gap: '4px' } }, [
      $('label', { style: { fontSize: '11px', color: 'var(--text-3)', fontWeight: '600', textTransform: 'uppercase', letterSpacing: '0.5px' } }, 'Provider')
    ]);
    var sel = $('select', {
      style: {
        background: 'var(--bg-raised)', color: 'var(--text)', border: '1px solid var(--border)',
        borderRadius: 'var(--radius)', padding: '7px 10px', fontSize: '13px', fontFamily: 'var(--font)',
        minWidth: '140px'
      }
    });
    ['openai', 'anthropic', 'venice', 'openrouter'].forEach(function (p) {
      var opt = $('option', { value: p }, p);
      if (p === T.providerSetup.provider) opt.selected = true;
      sel.appendChild(opt);
    });
    sel.addEventListener('change', function () { T.providerSetup.provider = sel.value; });
    selWrap.appendChild(sel);
    inputRow.appendChild(selWrap);

    // API Key input
    var keyWrap = $('div', { style: { display: 'flex', flexDirection: 'column', gap: '4px', flex: '1', minWidth: '240px' } }, [
      $('label', { style: { fontSize: '11px', color: 'var(--text-3)', fontWeight: '600', textTransform: 'uppercase', letterSpacing: '0.5px' } }, 'API Key')
    ]);
    var keyInput = $('input', {
      type: 'password',
      placeholder: 'sk-... / anthropic-... / paste key',
      style: {
        background: 'var(--bg-raised)', color: 'var(--text)', border: '1px solid var(--border)',
        borderRadius: 'var(--radius)', padding: '7px 10px', fontSize: '13px', fontFamily: 'var(--mono)',
        width: '100%'
      }
    });
    keyInput.value = T.providerSetup.apiKey;
    keyInput.addEventListener('input', function () { T.providerSetup.apiKey = keyInput.value; });
    keyWrap.appendChild(keyInput);
    inputRow.appendChild(keyWrap);

    // Endpoint override (collapsed by default)
    var epWrap = $('div', { style: { display: 'flex', flexDirection: 'column', gap: '4px', minWidth: '200px' } }, [
      $('label', { style: { fontSize: '11px', color: 'var(--text-3)', fontWeight: '600', textTransform: 'uppercase', letterSpacing: '0.5px' } }, 'Endpoint (optional)')
    ]);
    var epInput = $('input', {
      type: 'text',
      placeholder: T.PROVIDER_DEFAULTS[T.providerSetup.provider] || 'default',
      style: {
        background: 'var(--bg-raised)', color: 'var(--text)', border: '1px solid var(--border)',
        borderRadius: 'var(--radius)', padding: '7px 10px', fontSize: '12px', fontFamily: 'var(--mono)',
        width: '100%'
      }
    });
    epInput.value = T.providerSetup.endpoint;
    epInput.addEventListener('input', function () { T.providerSetup.endpoint = epInput.value; });
    epWrap.appendChild(epInput);
    inputRow.appendChild(epWrap);

    // Configure button
    var cfgBtn = $('button', {
      className: T.providerSetup.configured ? 'btn-ghost' : 'btn-primary',
      style: { alignSelf: 'flex-end', whiteSpace: 'nowrap' },
      onClick: function () {
        if (!T.providerSetup.apiKey) {
          if (window.sw && sw.toast) sw.toast('Paste an API key first', 'warning');
          return;
        }
        T.providerSetup.configured = true;
        T.renderProviderSetup();
        T.renderControls();
        if (window.sw && sw.toast) sw.toast(T.providerSetup.provider + ' configured — run Provider tier', 'success');
      }
    }, T.providerSetup.configured ? 'Reconfigure' : 'Configure');
    inputRow.appendChild(cfgBtn);

    if (T.providerSetup.configured) {
      var clearBtn = $('button', {
        className: 'btn-ghost',
        style: { alignSelf: 'flex-end', color: 'var(--danger)', whiteSpace: 'nowrap' },
        onClick: function () {
          T.providerSetup.apiKey = '';
          T.providerSetup.configured = false;
          T.renderProviderSetup();
          T.renderControls();
        }
      }, 'Clear Key');
      inputRow.appendChild(clearBtn);
    }

    card.appendChild(inputRow);

    // Security note
    card.appendChild($('div', {
      style: { fontSize: '11px', color: 'var(--text-3)', marginTop: '8px', fontStyle: 'italic' }
    }, 'Key stays in browser memory only — never persisted, cleaned up on page close. Creates temporary configs and deletes on teardown.'));

    T.el.providerSetup.appendChild(card);
  }



  T.renderShell = function () {
    T.mount.innerHTML = '';
    T.mount.style.padding = '24px 32px';
    T.mount.style.maxWidth = '1000px';
    T.mount.style.margin = '0 auto';

    // Top bar with user menu
    var topbar = $('div', { style: { display: 'flex', justifyContent: 'flex-end', marginBottom: '8px' } });
    T.mount.appendChild(topbar);
    if (window.sw?.userMenu) window.sw.userMenu(topbar, { flyout: 'down' });

    // Header
    var user = window.sw?.auth?.user || T.user || {};
    var hdr = $('div', { style: { marginBottom: '24px' } }, [
      $('h1', { style: { fontSize: '22px', fontWeight: '700', color: 'var(--text)', margin: '0 0 4px 0' } },
        'ICD Test Runner'),
      $('div', { style: { fontSize: '13px', color: 'var(--text-3)' } },
        'v' + (T.manifest.version || '0.28.0') + '  ·  ' + esc(user.username || 'unknown') + (user.role === 'admin' ? ' (admin)' : ' (user)'))
    ]);
    T.mount.appendChild(hdr);

    // Controls
    T.el.controls = $('div', { style: { display: 'flex', gap: '10px', marginBottom: '16px', flexWrap: 'wrap', alignItems: 'center' } });
    T.mount.appendChild(T.el.controls);

    // Fixtures panel (admin only)
    if (T.user.role === 'admin') {
      T.el.fixtures = $('div', { style: { marginBottom: '16px' } });
      T.mount.appendChild(T.el.fixtures);
    }

    // Provider setup panel
    T.el.providerSetup = $('div', { style: { marginBottom: '16px' } });
    T.mount.appendChild(T.el.providerSetup);

    // Progress
    T.el.progress = $('div', { style: { marginBottom: '16px' } });
    T.mount.appendChild(T.el.progress);

    // Summary
    T.el.summary = $('div', { style: { marginBottom: '20px' } });
    T.mount.appendChild(T.el.summary);

    // Detail table
    T.el.detail = $('div', {});
    T.mount.appendChild(T.el.detail);

    T.renderControls();
    T.renderFixtures();
    T.renderProviderSetup();
  }

  T.renderControls = function () {
    T.el.controls.innerHTML = '';
    var btnSmoke = $('button', {
      className: 'btn-primary',
      onClick: function () { T.runSuite('smoke'); }
    }, 'Smoke');
    var btnCrud = $('button', {
      className: 'btn-secondary',
      onClick: function () { T.runSuite('crud'); }
    }, 'CRUD');
    var btnAll = $('button', {
      className: 'btn-primary',
      onClick: function () { T.runSuite('all'); }
    }, 'Run All');
    var btnClear = $('button', {
      className: 'btn-ghost',
      onClick: function () { T.results = []; T.renderProgress(); T.renderSummary(); T.renderDetail(); }
    }, 'Clear');
    T.el.controls.appendChild(btnSmoke);
    T.el.controls.appendChild(btnCrud);

    // AuthZ button — only useful if admin (needs fixtures)
    if (T.user.role === 'admin') {
      var btnAuthz = $('button', {
        className: T.fixtures.ready ? 'btn-secondary' : 'btn-ghost',
        style: { opacity: T.fixtures.ready ? '1' : '0.5' },
        onClick: function () { T.runSuite('authz'); }
      }, 'AuthZ');
      T.el.controls.appendChild(btnAuthz);

      var btnSecurity = $('button', {
        className: T.fixtures.ready ? 'btn-secondary' : 'btn-ghost',
        style: {
          opacity: T.fixtures.ready ? '1' : '0.5',
          borderColor: T.fixtures.ready ? 'var(--danger)' : undefined,
          color: T.fixtures.ready ? 'var(--danger)' : undefined
        },
        onClick: function () { T.runSuite('security'); }
      }, 'Security');
      T.el.controls.appendChild(btnSecurity);
    }

    // Provider tier button — needs provider setup
    var btnProv = $('button', {
      className: T.providerSetup.configured ? 'btn-secondary' : 'btn-ghost',
      style: { opacity: T.providerSetup.configured ? '1' : '0.5' },
      onClick: function () { T.runSuite('provider'); }
    }, 'Providers');
    T.el.controls.appendChild(btnProv);

    // SDK tier button — tests switchboard-sdk.js contract
    var btnSdk = $('button', {
      className: (typeof window.sw !== 'undefined') ? 'btn-secondary' : 'btn-ghost',
      style: { opacity: (typeof window.sw !== 'undefined') ? '1' : '0.5' },
      onClick: function () { T.runSuite('sdk'); }
    }, 'SDK');
    T.el.controls.appendChild(btnSdk);

    // Packaging tier button — extension permission lifecycle (v0.29.0)
    var btnPkg = $('button', {
      className: 'btn-secondary',
      onClick: function () { T.runSuite('packaging'); }
    }, 'Packaging');
    T.el.controls.appendChild(btnPkg);

    T.el.controls.appendChild(btnAll);
    T.el.controls.appendChild(btnClear);

    // Separator
    T.el.controls.appendChild($('div', { style: { width: '1px', height: '28px', background: 'var(--border)', flexShrink: '0' } }));

    // Export buttons
    var btnCopy = $('button', {
      className: 'btn-ghost',
      onClick: function () { T.exportReport('clipboard'); }
    }, 'Copy Report');
    var btnDownload = $('button', {
      className: 'btn-ghost',
      onClick: function () { T.exportReport('download'); }
    }, 'Download');
    T.el.controls.appendChild(btnCopy);
    T.el.controls.appendChild(btnDownload);

    // Fixture controls (admin)
    if (T.user.role === 'admin') {
      T.el.controls.appendChild($('div', { style: { width: '1px', height: '28px', background: 'var(--border)', flexShrink: '0' } }));
      if (!T.fixtures.ready) {
        T.el.controls.appendChild($('button', {
          className: 'btn-secondary',
          onClick: async function () { await T.provisionFixtures(); T.renderControls(); }
        }, 'Provision Test Users'));
      } else {
        T.el.controls.appendChild($('button', {
          className: 'btn-ghost',
          style: { color: 'var(--danger)' },
          onClick: async function () { await T.teardownFixtures(); T.renderControls(); }
        }, 'Tear Down Fixtures'));
      }
    }
  }

  // ─── Fixtures Panel ─────────────────────────────────────────

  T.renderFixtures = function () {
    if (!T.el.fixtures) return;
    T.el.fixtures.innerHTML = '';
    if (!T.fixtures.ready) return;

    var panel = $('div', {
      style: {
        background: 'var(--bg-surface)', border: '1px solid var(--border)',
        borderRadius: 'var(--radius-lg)', padding: '14px 18px',
        borderLeft: '3px solid var(--accent)'
      }
    });

    panel.appendChild($('div', {
      style: { fontSize: '13px', fontWeight: '600', color: 'var(--text)', marginBottom: '10px' }
    }, 'Test Fixtures'));

    // Users table
    var tbl = $('table', {
      style: { width: '100%', borderCollapse: 'collapse', fontSize: '12px', fontFamily: 'var(--mono)' }
    });

    var hdr = $('tr', { style: { borderBottom: '1px solid var(--border)' } });
    ['Role', 'Username', 'Password', 'Token', 'ID'].forEach(function (h) {
      hdr.appendChild($('th', { style: { textAlign: 'left', padding: '4px 8px', color: 'var(--text-3)', fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.5px' } }, h));
    });
    tbl.appendChild(hdr);

    T.fixtures.users.forEach(function (u) {
      var tr = $('tr', { style: { borderBottom: '1px solid var(--border)' } });
      tr.appendChild($('td', { style: { padding: '4px 8px', color: 'var(--accent)' } }, u.role));
      tr.appendChild($('td', { style: { padding: '4px 8px', color: 'var(--text)' } }, u.username));

      // Password with copy button
      var pwTd = $('td', { style: { padding: '4px 8px' } });
      var pwSpan = $('span', { style: { color: 'var(--warning)', cursor: 'pointer' }, title: 'Click to copy' }, u.password);
      pwSpan.addEventListener('click', function () {
        navigator.clipboard.writeText(u.password);
        if (window.sw && sw.toast) sw.toast('Password copied', 'info');
      });
      pwTd.appendChild(pwSpan);
      tr.appendChild(pwTd);

      // Token status
      var tokenStatus = u.token ? 'yes' : 'FAILED';
      var tokenColor = u.token ? 'var(--success)' : 'var(--danger)';
      tr.appendChild($('td', { style: { padding: '4px 8px', color: tokenColor } }, tokenStatus));

      // ID (truncated)
      tr.appendChild($('td', { style: { padding: '4px 8px', color: 'var(--text-3)' } }, (u.id || '').substring(0, 8) + '…'));

      tbl.appendChild(tr);
    });
    panel.appendChild(tbl);

    // Team/group info
    var meta = [];
    if (T.fixtures.team) meta.push('Team: ' + T.fixtures.team.name + ' (' + T.fixtures.team.id.substring(0, 8) + '…)');
    if (T.fixtures.group) meta.push('Group: ' + T.fixtures.group.name + ' (' + T.fixtures.group.id.substring(0, 8) + '…)');
    if (meta.length > 0) {
      panel.appendChild($('div', {
        style: { marginTop: '8px', fontSize: '11px', color: 'var(--text-3)' }
      }, meta.join('  ·  ')));
    }

    T.el.fixtures.appendChild(panel);
  }

  // ─── Export ─────────────────────────────────────────────────

  function buildReport() {
    var now = new Date().toISOString();
    var pass = T.results.filter(function (r) { return r.status === 'pass'; }).length;
    var fail = T.results.filter(function (r) { return r.status === 'fail'; }).length;
    var skip = T.results.filter(function (r) { return r.status === 'skip'; }).length;
    var total = T.results.length;
    var totalMs = T.results.reduce(function (s, r) { return s + r.duration; }, 0);

    var lines = [];
    lines.push('=== Switchboard Core ICD Test Report ===');
    lines.push('Generated: ' + now);
    lines.push('Platform:  v' + (T.manifest.version || '0.28.0'));
    lines.push('User:      ' + T.user.username + ' (' + T.user.role + ')');
    lines.push('URL:       ' + location.href);
    lines.push('');
    lines.push('--- Summary ---');
    lines.push('Total: ' + total + '  Pass: ' + pass + '  Fail: ' + fail + (skip > 0 ? '  Skip: ' + skip : '') + '  Rate: ' + (total > 0 ? Math.round((pass / (total - skip)) * 100) : 0) + '%  Duration: ' + totalMs + 'ms');

    // Critical failures callout
    var criticals = T.results.filter(function (r) { return r.status === 'fail' && r.detail && r.detail.indexOf('CRITICAL') !== -1; });
    if (criticals.length > 0) {
      lines.push('');
      lines.push('!!! ' + criticals.length + ' CRITICAL SECURITY FAILURE(S) !!!');
      criticals.forEach(function (r) {
        lines.push('  → [' + r.tier + '/' + r.domain + '] ' + r.name);
        lines.push('    ' + r.detail);
      });
    }

    // Fixture info
    if (T.fixtures.ready) {
      lines.push('');
      lines.push('--- Test Fixtures ---');
      T.fixtures.users.forEach(function (u) {
        lines.push('  ' + pad(u.role, 8) + pad(u.username, 40) + (u.token ? 'token:OK' : 'token:FAIL'));
      });
      if (T.fixtures.team) lines.push('  Team:  ' + T.fixtures.team.name + ' (' + T.fixtures.team.id + ')');
      if (T.fixtures.group) lines.push('  Group: ' + T.fixtures.group.name + ' (' + T.fixtures.group.id + ')');
    }

    // Provider config info
    if (T.providerSetup.configured) {
      lines.push('');
      lines.push('--- Provider Setup ---');
      lines.push('  Type:     ' + T.providerSetup.provider);
      lines.push('  Endpoint: ' + (T.providerSetup.endpoint || T.PROVIDER_DEFAULTS[T.providerSetup.provider] || 'default'));
      lines.push('  Key:      ' + (T.providerSetup.apiKey ? T.providerSetup.apiKey.slice(0, 8) + '...' : 'none'));
    }
    lines.push('');

    // Domain breakdown
    var domains = {};
    T.results.forEach(function (r) {
      var k = r.domain;
      if (!domains[k]) domains[k] = { pass: 0, fail: 0, skip: 0, total: 0, ms: 0 };
      domains[k].total++;
      domains[k].ms += r.duration;
      if (r.status === 'pass') domains[k].pass++;
      else if (r.status === 'skip') domains[k].skip++;
      else domains[k].fail++;
    });

    lines.push('--- By Domain ---');
    Object.keys(domains).forEach(function (d) {
      var s = domains[d];
      var flag = s.fail > 0 ? 'FAIL' : (s.skip > 0 && s.pass === 0 ? 'SKIP' : 'OK');
      lines.push('  ' + pad(d, 16) + pad(flag, 6) + pad(s.pass + '/' + s.total, 8) + s.ms + 'ms');
    });
    lines.push('');

    // Failures only (if any)
    var failures = T.results.filter(function (r) { return r.status === 'fail'; });
    if (failures.length > 0) {
      lines.push('--- Failures (' + failures.length + ') ---');
      failures.forEach(function (r, i) {
        lines.push('  ' + (i + 1) + '. [' + r.tier + '/' + r.domain + '] ' + r.name);
        lines.push('     ' + r.detail);
      });
      lines.push('');
    }

    // Skipped (if any)
    var skipped = T.results.filter(function (r) { return r.status === 'skip'; });
    if (skipped.length > 0) {
      lines.push('--- Skipped (' + skipped.length + ') ---');
      skipped.forEach(function (r, i) {
        lines.push('  ' + (i + 1) + '. [' + r.tier + '/' + r.domain + '] ' + r.name);
        lines.push('     ' + r.detail);
      });
      lines.push('');
    }

    // Full detail table
    lines.push('--- Full Results ---');
    lines.push(pad('STATUS', 8) + pad('TIER', 8) + pad('DOMAIN', 16) + pad('MS', 6) + 'TEST');
    lines.push(repeat('-', 90));
    T.results.forEach(function (r) {
      var statusStr = r.status === 'pass' ? 'PASS' : (r.status === 'skip' ? 'SKIP' : 'FAIL');
      var line = pad(statusStr, 8) + pad(r.tier, 8) + pad(r.domain, 16) + pad(String(r.duration), 6) + r.name;
      if (r.status === 'fail' && r.detail) {
        line += '\n' + repeat(' ', 38) + '↳ ' + r.detail;
      }
      if (r.status === 'skip' && r.detail) {
        line += '\n' + repeat(' ', 38) + '↳ SKIP: ' + r.detail;
      }
      lines.push(line);
    });
    lines.push('');
    lines.push('=== END ===');

    return lines.join('\n');
  }

  function pad(s, w) {
    s = String(s);
    while (s.length < w) s += ' ';
    return s;
  }

  function repeat(ch, n) {
    var s = '';
    for (var i = 0; i < n; i++) s += ch;
    return s;
  }

  T.exportReport = function (mode) {
    if (T.results.length === 0) {
      if (window.sw && sw.toast) sw.toast('No T.results to export — run a suite first', 'warning');
      return;
    }

    var text = buildReport();

    if (mode === 'clipboard') {
      navigator.clipboard.writeText(text).then(function () {
        if (window.sw && sw.toast) sw.toast('Report copied to clipboard', 'success');
      }).catch(function () {
        // Fallback: select-all textarea
        clipboardFallback(text);
      });
    } else if (mode === 'download') {
      var ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
      var filename = 'icd-report-' + ts + '.txt';
      var blob = new Blob([text], { type: 'text/plain' });
      var url = URL.createObjectURL(blob);
      var a = document.createElement('a');
      a.href = url;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
      if (window.sw && sw.toast) sw.toast('Downloaded ' + filename, 'success');
    }
  }

  function clipboardFallback(text) {
    var ta = document.createElement('textarea');
    ta.value = text;
    ta.style.cssText = 'position:fixed;left:-9999px;top:0;';
    document.body.appendChild(ta);
    ta.select();
    try { document.execCommand('copy'); } catch (e) { /* give up */ }
    document.body.removeChild(ta);
    if (window.sw && sw.toast) sw.toast('Report copied (fallback)', 'info');
  }

  

  T.runSuite = async function (which) {
    if (T.running) return;
    if (which === 'authz' && !T.fixtures.ready) {
      if (window.sw && sw.toast) sw.toast('Provision test fixtures first (button on the right)', 'warning');
      return;
    }
    if (which === 'security' && !T.fixtures.ready) {
      if (window.sw && sw.toast) sw.toast('Provision test fixtures first (button on the right)', 'warning');
      return;
    }
    if (which === 'provider' && !T.providerSetup.configured) {
      if (window.sw && sw.toast) sw.toast('Configure a provider (type + API key) in the panel above', 'warning');
      return;
    }
    T.running = true;
    T.results = [];

    // Capture auth token before any test (needed for DELETE fallback)
    await T.captureAuthToken();

    T.renderProgress();
    T.renderSummary();
    T.renderDetail();

    try {
      if (which === 'smoke' || which === 'all') await T.runSmoke();
      if (which === 'crud' || which === 'all') await T.runCrud();
      if (which === 'authz' || (which === 'all' && T.fixtures.ready)) await T.runAuthz();
      if (which === 'security' || (which === 'all' && T.fixtures.ready)) await T.runSecurity();
      if (which === 'provider' || (which === 'all' && T.providerSetup.configured)) await T.runProviders();
      if (which === 'sdk' || which === 'all') { if (typeof T.runSdk === 'function') await T.runSdk(); }
      if (which === 'packaging' || which === 'all') { if (typeof T.runPackaging === 'function') await T.runPackaging(); }
    } catch (e) {
      T.results.push({ tier: '?', domain: 'runner', name: 'FATAL', status: 'fail', duration: 0, detail: String(e) });
    }

    // Always T.cleanup (CRUD-created resources, not fixtures)
    await T.runCleanup();

    T.running = false;
    T.renderSummary();
    T.renderDetail();
    T.renderProgress();
    if (typeof UI !== 'undefined') {
      var pass = T.results.filter(function (r) { return r.status === 'pass'; }).length;
      var fail = T.results.filter(function (r) { return r.status === 'fail'; }).length;
      var critical = T.results.filter(function (r) { return r.status === 'fail' && r.detail && r.detail.indexOf('CRITICAL') !== -1; }).length;
      var msg = pass + ' passed, ' + fail + ' failed';
      if (critical > 0) msg += ' (' + critical + ' CRITICAL)';
      if (window.sw && sw.toast) sw.toast(msg, critical > 0 ? 'error' : fail > 0 ? 'warning' : 'success');
    }
  }

  T.renderProgress = function () {
    if (!T.el.progress) return;
    var pass = T.results.filter(function (r) { return r.status === 'pass'; }).length;
    var fail = T.results.filter(function (r) { return r.status === 'fail'; }).length;
    var skip = T.results.filter(function (r) { return r.status === 'skip'; }).length;
    var total = T.results.length;
    var countable = total - skip;
    var pct = countable > 0 ? Math.round((pass / countable) * 100) : 0;

    T.el.progress.innerHTML = '';
    if (total === 0) return;

    // Progress bar
    var barOuter = $('div', {
      style: {
        height: '8px', borderRadius: '4px', background: 'var(--bg-raised)',
        overflow: 'hidden', marginBottom: '8px'
      }
    });
    var barInner = $('div', {
      style: {
        height: '100%', borderRadius: '4px',
        width: pct + '%',
        background: fail > 0 ? 'var(--warning)' : 'var(--success)',
        transition: 'width 200ms ease'
      }
    });
    barOuter.appendChild(barInner);
    T.el.progress.appendChild(barOuter);

    var label = $('div', { style: { fontSize: '12px', color: 'var(--text-2)' } },
      (T.running ? 'Running... ' : '') + total + ' tests · ' + pass + ' pass · ' + fail + ' fail' + (skip > 0 ? ' · ' + skip + ' skip' : '') + ' · ' + pct + '%');
    T.el.progress.appendChild(label);
  }

  T.renderSummary = function () {
    if (!T.el.summary) return;
    T.el.summary.innerHTML = '';
    if (T.results.length === 0) return;

    // Group by domain
    var domains = {};
    T.results.forEach(function (r) {
      var k = r.domain;
      if (!domains[k]) domains[k] = { pass: 0, fail: 0, skip: 0, total: 0, ms: 0 };
      domains[k].total++;
      domains[k].ms += r.duration;
      if (r.status === 'pass') domains[k].pass++;
      else if (r.status === 'skip') domains[k].skip++;
      else domains[k].fail++;
    });

    var grid = $('div', {
      style: {
        display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(160px, 1fr))',
        gap: '8px'
      }
    });

    Object.keys(domains).forEach(function (d) {
      var s = domains[d];
      var borderColor = s.fail > 0 ? 'var(--danger)' : (s.skip > 0 ? 'var(--warning)' : 'var(--success)');
      var label = s.pass + '/' + s.total + ' pass';
      if (s.skip > 0) label += ' · ' + s.skip + ' skip';
      label += ' · ' + s.ms + 'ms';
      var card = $('div', {
        style: {
          background: 'var(--bg-surface)', border: '1px solid var(--border)',
          borderRadius: 'var(--radius)', padding: '10px 14px',
          borderLeft: '3px solid ' + borderColor
        }
      }, [
        $('div', { style: { fontSize: '13px', fontWeight: '600', color: 'var(--text)', marginBottom: '4px' } }, d),
        $('div', { style: { fontSize: '12px', color: 'var(--text-2)' } }, label)
      ]);
      grid.appendChild(card);
    });

    T.el.summary.appendChild(grid);
  }

  T.renderDetail = function () {
    if (!T.el.detail) return;
    T.el.detail.innerHTML = '';
    if (T.results.length === 0) return;

    var table = $('table', {
      style: {
        width: '100%', borderCollapse: 'collapse', fontSize: '13px',
        fontFamily: 'var(--mono)', background: 'var(--bg-surface)',
        border: '1px solid var(--border)', borderRadius: 'var(--radius)'
      }
    });

    // Header
    var thead = $('thead');
    var trh = $('tr', { style: { borderBottom: '1px solid var(--border)' } });
    ['', 'Tier', 'Domain', 'Test', 'ms', 'Detail'].forEach(function (h) {
      trh.appendChild($('th', {
        style: {
          textAlign: 'left', padding: '8px 10px', fontSize: '11px',
          fontWeight: '600', color: 'var(--text-3)', textTransform: 'uppercase',
          letterSpacing: '0.5px'
        }
      }, h));
    });
    thead.appendChild(trh);
    table.appendChild(thead);

    var tbody = $('tbody');
    T.results.forEach(function (r) {
      var isPass = r.status === 'pass';
      var isSkip = r.status === 'skip';
      var rowBg = isPass ? 'transparent' : (isSkip ? 'rgba(234,179,8,0.04)' : 'rgba(239,68,68,0.04)');
      var dotColor = isPass ? 'var(--success)' : (isSkip ? 'var(--warning)' : 'var(--danger)');
      var detailColor = isPass ? 'var(--text-3)' : (isSkip ? 'var(--warning)' : 'var(--danger)');
      var tr = $('tr', {
        style: {
          borderBottom: '1px solid var(--border)',
          background: rowBg
        }
      });

      // Status dot
      tr.appendChild($('td', { style: { padding: '6px 10px', width: '20px' } }, [
        $('span', {
          style: {
            display: 'inline-block', width: '8px', height: '8px',
            borderRadius: '50%', background: dotColor
          }
        })
      ]));

      tr.appendChild($('td', { style: { padding: '6px 10px', color: 'var(--text-3)' } }, r.tier));
      tr.appendChild($('td', { style: { padding: '6px 10px', color: 'var(--accent)' } }, r.domain));
      tr.appendChild($('td', { style: { padding: '6px 10px', color: 'var(--text)' } }, r.name));
      tr.appendChild($('td', { style: { padding: '6px 10px', color: 'var(--text-3)', textAlign: 'right' } }, String(r.duration)));

      var detailText = isSkip ? ('SKIP: ' + (r.detail || '')) : (r.detail || '');
      var detailTd = $('td', {
        style: {
          padding: '6px 10px', color: detailColor,
          maxWidth: '320px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          cursor: detailText ? 'pointer' : 'default'
        },
        title: detailText || ''
      }, detailText ? detailText.substring(0, 120) : '✓');

      if (detailText && detailText.length > 120) {
        detailTd.addEventListener('click', function () {
          if (detailTd.style.whiteSpace === 'nowrap') {
            detailTd.style.whiteSpace = 'pre-wrap';
            detailTd.style.wordBreak = 'break-all';
            detailTd.textContent = detailText;
          } else {
            detailTd.style.whiteSpace = 'nowrap';
            detailTd.textContent = r.detail.substring(0, 120);
          }
        });
      }

      tr.appendChild(detailTd);
      tbody.appendChild(tr);
    });
    table.appendChild(tbody);

    T.el.detail.appendChild(table);
  }

})();
