// AGENTFORGE-IA — Integraciones, Ajustes, Facturación, Inicio, Ejecuciones, Analítica

// ---------------------------------------------------------------------------
// Modal de conexión / configuración de integración
// ---------------------------------------------------------------------------
const IntegrationModal = ({ integration, onClose, onSaved }) => {
  const [apiKey, setApiKey] = React.useState('');
  const [accountName, setAccountName] = React.useState(integration.account_name || '');
  const [saving, setSaving] = React.useState(false);
  const [testing, setTesting] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [testResult, setTestResult] = React.useState(null);
  const [copied, setCopied] = React.useState(false);

  const token = () => sessionStorage.getItem('af_token') || '';
  const isWebhook = integration.kind === 'webhook';
  const isOauth = integration.kind === 'oauth';
  const isEdit = integration.connected;
  const canConnect = integration.can_connect !== false;

  const handleSave = async () => {
    if (!canConnect) {
      setErr(integration.blocked_reason || 'No tienes permisos para esta integración.');
      return;
    }
    if (!isWebhook && !isOauth && !apiKey && !isEdit) {
      setErr('Introduce la API key.');
      return;
    }
    setSaving(true);
    setErr('');
    try {
      const body = {};
      if (!isWebhook && !isOauth && apiKey) body.api_key = apiKey;
      if (accountName) body.account_name = accountName;

      const res = await fetch(`/api/v1/integrations/${integration.slug}/connect`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token() },
        body: JSON.stringify(body),
      });
      if (!res.ok) {
        const d = await res.json().catch(() => ({}));
        setErr(d.detail || `Error ${res.status}`);
        return;
      }
      const saved = await res.json();
      onSaved(saved);
      onClose();
    } catch (_) {
      setErr('Error de red. Inténtalo de nuevo.');
    } finally {
      setSaving(false);
    }
  };

  const handleOauthConnect = async () => {
    if (!canConnect) {
      setErr(integration.blocked_reason || 'No tienes permisos para esta integración.');
      return;
    }
    setSaving(true);
    setErr('');
    try {
      const res = await fetch(`/api/v1/integrations/${integration.slug}/oauth/start`, {
        method: 'POST',
        headers: { Authorization: 'Bearer ' + token() },
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok || !data.auth_url) {
        setErr(data.detail || 'No se pudo iniciar OAuth.');
        return;
      }
      window.location.href = data.auth_url;
    } catch (_) {
      setErr('Error de red al iniciar OAuth.');
    } finally {
      setSaving(false);
    }
  };

  const handleTest = async () => {
    if (!canConnect) {
      setTestResult({ ok: false, message: integration.blocked_reason || 'No tienes permisos para probar esta integración.' });
      return;
    }
    setTesting(true);
    setTestResult(null);
    try {
      const res = await fetch(`/api/v1/integrations/${integration.slug}/test`, {
        method: 'POST',
        headers: { Authorization: 'Bearer ' + token() },
      });
      const d = await res.json().catch(() => ({ ok: false, message: 'Error de red.' }));
      setTestResult(d);
    } catch (_) {
      setTestResult({ ok: false, message: 'Error de red.' });
    } finally {
      setTesting(false);
    }
  };

  const copyWebhook = () => {
    navigator.clipboard.writeText(integration.webhook_url || '').then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };

  return (
    <div
      style={{ position: 'fixed', inset: 0, zIndex: 990, background: 'rgba(0,0,0,0.72)', backdropFilter: 'blur(4px)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}
    >
      <div style={{ background: '#1a1a1a', border: '1px solid #2a2a2a', borderRadius: 12, padding: '28px 24px', width: 440, boxShadow: '0 20px 60px rgba(0,0,0,0.6)' }}>
        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 20 }}>
          <div style={{ width: 38, height: 38, borderRadius: 8, background: integration.color, color: '#fff', display: 'grid', placeItems: 'center', fontSize: 13, fontWeight: 700, flexShrink: 0 }}>
            {integration.label}
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontWeight: 700, fontSize: 15 }}>{isEdit ? 'Configurar' : 'Conectar'} {integration.name}</div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>{isWebhook ? 'Integración por webhook' : 'Integración por API key'}</div>
          </div>
          {integration.scope === 'global_admin' && (
            <span className="tag warn" title="Conector de nivel organizacional">
              Global/Admin
            </span>
          )}
          <button onClick={onClose} style={{ background: 'none', border: 'none', color: '#888', cursor: 'pointer', padding: 4 }}>
            <Icon name="close" size={16} />
          </button>
        </div>

        {integration.blocked_reason && (
          <div style={{ marginBottom: 12, padding: '8px 12px', background: 'rgba(255,59,59,0.08)', border: '1px solid rgba(255,59,59,0.25)', borderRadius: 7, fontSize: 12.5, color: 'var(--bad)' }}>
            {integration.blocked_reason}
          </div>
        )}

        {/* Webhook kind — solo mostrar URL para copiar */}
        {isWebhook ? (
          <div>
            <p style={{ margin: '0 0 10px', fontSize: 13, color: 'var(--ink-3)' }}>
              Usa esta URL en {integration.name} para enviar eventos a AgentForge:
            </p>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <code style={{ flex: 1, background: '#111', border: '1px solid #2a2a2a', borderRadius: 6, padding: '8px 10px', fontSize: 11.5, color: '#e5e5e5', fontFamily: 'var(--mono)', wordBreak: 'break-all' }}>
                {integration.webhook_url}
              </code>
              <button className="btn btn-sm" onClick={copyWebhook} style={{ flexShrink: 0 }}>
                {copied ? <Icon name="check" size={12} /> : <Icon name="copy" size={12} />}
              </button>
            </div>
          </div>
        ) : isOauth ? (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            <p style={{ margin: 0, fontSize: 13, color: 'var(--ink-3)' }}>
              Esta integración se conecta por OAuth seguro.
            </p>
            <p style={{ margin: 0, fontSize: 12, color: 'var(--ink-4)' }}>
              Cuenta sugerida: <b>adminforge@adminforge-ia.pro</b>
            </p>
          </div>
        ) : (
          /* API key kind */
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <div>
              <label style={{ fontSize: 12, color: 'var(--ink-3)', display: 'block', marginBottom: 5 }}>
                API Key{isEdit ? ' (deja vacío para mantener la actual)' : ''}
              </label>
              <input
                type="password"
                autoComplete="off"
                className="input"
                style={{ width: '100%', fontFamily: 'var(--mono)', letterSpacing: '0.05em' }}
                placeholder={isEdit ? integration.api_key_masked || '••••••••' : 'sk-…'}
                value={apiKey}
                onChange={e => { setApiKey(e.target.value); setErr(''); }}
              />
              {isEdit && integration.api_key_masked && (
                <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 4, fontFamily: 'var(--mono)' }}>
                  Actual: {integration.api_key_masked}
                </div>
              )}
            </div>
            <div>
              <label style={{ fontSize: 12, color: 'var(--ink-3)', display: 'block', marginBottom: 5 }}>
                Nombre de cuenta (opcional)
              </label>
              <input
                type="text"
                className="input"
                style={{ width: '100%' }}
                placeholder={`p. ej. ${integration.name} · Producción`}
                value={accountName}
                onChange={e => setAccountName(e.target.value)}
              />
            </div>
          </div>
        )}

        {/* Último test */}
        {isEdit && integration.last_tested_at && (
          <div style={{ marginTop: 14, padding: '8px 12px', background: integration.last_test_ok ? 'rgba(52,211,153,0.08)' : 'rgba(255,59,59,0.08)', border: `1px solid ${integration.last_test_ok ? 'rgba(52,211,153,0.25)' : 'rgba(255,59,59,0.25)'}`, borderRadius: 7, fontSize: 12 }}>
            <span style={{ color: integration.last_test_ok ? 'var(--ok)' : 'var(--bad)' }}>
              {integration.last_test_ok ? 'Último test OK' : 'Último test fallido'}
            </span>
            <span style={{ color: 'var(--ink-4)', marginLeft: 8, fontFamily: 'var(--mono)', fontSize: 11 }}>
              {new Date(integration.last_tested_at).toLocaleString('es-ES', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' })}
            </span>
          </div>
        )}

        {/* Resultado de test en tiempo real */}
        {testResult && (
          <div style={{ marginTop: 10, padding: '8px 12px', background: testResult.ok ? 'rgba(52,211,153,0.08)' : 'rgba(255,59,59,0.08)', border: `1px solid ${testResult.ok ? 'rgba(52,211,153,0.25)' : 'rgba(255,59,59,0.25)'}`, borderRadius: 7, fontSize: 12.5 }}>
            <span style={{ color: testResult.ok ? 'var(--ok)' : 'var(--bad)' }}>
              {testResult.ok ? 'Conexion OK: ' : 'Error: '}{testResult.message}
            </span>
          </div>
        )}

        {err && (
          <div style={{ marginTop: 10, padding: '8px 12px', background: 'rgba(255,59,59,0.08)', border: '1px solid rgba(255,59,59,0.25)', borderRadius: 7, fontSize: 12.5, color: 'var(--bad)' }}>
            {err}
          </div>
        )}

        {/* Acciones */}
        <div style={{ display: 'flex', gap: 8, marginTop: 20, justifyContent: 'flex-end' }}>
          {isEdit && !isWebhook && !isOauth && (
            <button className="btn btn-ghost btn-sm" onClick={handleTest} disabled={testing || !canConnect}>
              {testing ? '…' : <><Icon name="play" size={11} /> Probar</>}
            </button>
          )}
          <button className="btn btn-ghost btn-sm" onClick={onClose}>
            Cancelar
          </button>
          {isOauth && (
            <button className="btn btn-primary btn-sm" onClick={handleOauthConnect} disabled={saving || !canConnect}>
              {saving ? '…' : <><Icon name="plug" size={11} /> {isEdit ? 'Reconectar OAuth' : 'Conectar OAuth'}</>}
            </button>
          )}
          {!isWebhook && !isOauth && (
            <button className="btn btn-primary btn-sm" onClick={handleSave} disabled={saving || !canConnect}>
              {saving ? '…' : isEdit ? 'Guardar cambios' : 'Conectar'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// Modal de confirmación de desconexión
// ---------------------------------------------------------------------------
const DisconnectConfirmModal = ({ integration, onClose, onConfirm }) => {
  const [loading, setLoading] = React.useState(false);

  const handleConfirm = async () => {
    setLoading(true);
    await onConfirm();
    setLoading(false);
  };

  return (
    <div
      style={{ position: 'fixed', inset: 0, zIndex: 991, background: 'rgba(0,0,0,0.72)', backdropFilter: 'blur(4px)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}
    >
      <div style={{ background: '#1a1a1a', border: '1px solid #2a2a2a', borderRadius: 12, padding: '28px 24px', width: 380, boxShadow: '0 20px 60px rgba(0,0,0,0.6)' }}>
        <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 10 }}>Desconectar {integration.name}</div>
        <p style={{ margin: '0 0 20px', fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.6 }}>
          Se eliminarán las credenciales guardadas. Los agentes que usen esta integración dejarán de tener acceso.
          Esta acción no se puede deshacer.
        </p>
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button className="btn btn-ghost btn-sm" onClick={onClose}>Cancelar</button>
          <button className="btn btn-sm" style={{ background: 'var(--bad)', color: '#fff', border: 'none' }} onClick={handleConfirm} disabled={loading}>
            {loading ? '…' : 'Sí, desconectar'}
          </button>
        </div>
      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// Catálogo de integraciones disponibles (enriquecido con desc, reqs y deployTime)
// ---------------------------------------------------------------------------
const INTEGRATIONS_CATALOG = [
  { slug: 'salesforce', name: 'Salesforce', icon: 'salesforce', desc: 'Sincroniza leads, contactos y oportunidades con tu CRM Salesforce en tiempo real.', reqs: ['API key Salesforce', 'Perfil con permisos API'], deployTime: '~30 min' },
  { slug: 'hubspot', name: 'HubSpot', icon: 'hubspot', desc: 'Conecta contactos, pipelines y workflows de HubSpot con tus agentes IA.', reqs: ['Cuenta HubSpot con API access', 'Token privado de app'], deployTime: '~20 min' },
  { slug: 'slack', name: 'Slack', icon: 'slack', desc: 'Envía notificaciones y recibe comandos desde canales de Slack.', reqs: ['Workspace Slack admin', 'App Slack configurada'], deployTime: '~15 min' },
  { slug: 'whatsapp', name: 'WhatsApp', icon: 'whatsapp', desc: 'Canal de atención vía WhatsApp Business API con respuestas automáticas.', reqs: ['Número WhatsApp Business verificado', 'Meta Business Manager'], deployTime: '~2 h' },
  { slug: 'gmail', name: 'Gmail', icon: 'gmail', desc: 'Procesa emails entrantes y envía respuestas automáticas desde Gmail.', reqs: ['Cuenta Google Workspace', 'OAuth2 habilitado'], deployTime: '~20 min' },
  { slug: 'notion', name: 'Notion', icon: 'notion', desc: 'Lee y escribe en bases de datos Notion para gestión de conocimiento.', reqs: ['Integration token Notion', 'Permisos de página'], deployTime: '~15 min' },
  { slug: 'zapier', name: 'Zapier', icon: 'zapier', desc: 'Conecta con 5.000+ apps a través de Zaps automatizados.', reqs: ['Cuenta Zapier (plan Team o superior)'], deployTime: '~10 min' },
  { slug: 'stripe', name: 'Stripe', icon: 'stripe', desc: 'Gestiona pagos, suscripciones y eventos de facturación.', reqs: ['API keys Stripe', 'Webhook configurado'], deployTime: '~25 min' },
  { slug: 'mailchimp', name: 'Mailchimp', icon: 'mailchimp', desc: 'Sincroniza listas de contactos y automatiza campañas de email marketing.', reqs: ['API key Mailchimp', 'Audience ID'], deployTime: '~15 min' },
  { slug: 'make', name: 'Make', icon: 'make', desc: 'Orquesta flujos complejos entre múltiples apps con Make (ex-Integromat).', reqs: ['Cuenta Make', 'Webhook de entrada'], deployTime: '~20 min' },
  { slug: 'pipedrive', name: 'Pipedrive', icon: 'pipedrive', desc: 'Actualiza deals y actividades en tu pipeline de ventas Pipedrive.', reqs: ['API token Pipedrive'], deployTime: '~20 min' },
  { slug: 'telegram', name: 'Telegram', icon: 'telegram', desc: 'Bot de Telegram para soporte y notificaciones automatizadas.', reqs: ['Bot token de @BotFather'], deployTime: '~15 min' },
];

// Enriquece un objeto de integración del API con los metadatos del catálogo
const enrichIntegration = (i) => {
  const meta = INTEGRATIONS_CATALOG.find(c => c.slug === i.slug) || {};
  return { ...meta, ...i };
};

// ---------------------------------------------------------------------------
// Metadatos de redes sociales OAuth personal
// ---------------------------------------------------------------------------
const SOCIAL_PROVIDERS = {
  instagram: { name: 'Instagram', color: '#e1306c', label: 'IG', desc: 'Conecta tu cuenta de Instagram para que el agente gestione mensajes y publicaciones.' },
  facebook:  { name: 'Facebook',  color: '#1877f2', label: 'FB', desc: 'Conecta tu cuenta de Facebook para responder mensajes y publicar contenido.' },
  tiktok:    { name: 'TikTok',    color: '#010101', label: 'TT', desc: 'Conecta tu cuenta de TikTok para gestionar comentarios y mensajes directos.' },
};

// ---------------------------------------------------------------------------
// Componente principal Integrations
// ---------------------------------------------------------------------------
const Integrations = () => {
  const [ints, setInts] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [modal, setModal] = React.useState(null);      // { integration } | null
  const [confirmDisconnect, setConfirmDisconnect] = React.useState(null); // integration | null
  const [err, setErr] = React.useState('');

  // Social OAuth state
  const [socialStatus, setSocialStatus] = React.useState([]);
  const [socialLoading, setSocialLoading] = React.useState(false);
  const [socialConnecting, setSocialConnecting] = React.useState(null);

  const token = () => sessionStorage.getItem('af_token') || '';

  // Carga el estado real de integraciones desde el API
  const loadIntegrations = React.useCallback(async () => {
    setLoading(true);
    setErr('');
    try {
      const res = await fetch('/api/v1/integrations', {
        headers: { Authorization: 'Bearer ' + token() },
      });
      if (res.ok) {
        const raw = await res.json();
        setInts(raw.map(enrichIntegration));
      } else {
        setErr('No se pudieron cargar las integraciones.');
      }
    } catch (_) {
      setErr('Error de red al cargar integraciones.');
    }
    setLoading(false);
  }, []);

  React.useEffect(() => { loadIntegrations(); }, [loadIntegrations]);

  const connected = ints.filter(i => i.connected);
  const available = ints.filter(i => !i.connected);

  const handleSaved = (updated) => {
    const next = enrichIntegration(updated);
    setInts(prev => {
      const exists = prev.some(i => i.slug === next.slug);
      if (exists) return prev.map(i => i.slug === next.slug ? next : i);
      return [...prev, next];
    });
  };

  const handleDisconnect = async (integration) => {
    try {
      const res = await fetch(`/api/v1/integrations/${integration.slug}`, {
        method: 'DELETE',
        headers: { Authorization: 'Bearer ' + token() },
      });
      if (res.ok || res.status === 204) {
        setInts(prev => prev.map(i =>
          i.slug === integration.slug
            ? { ...i, connected: false, status: null, account_name: null, api_key_masked: null, last_tested_at: null, last_test_ok: null }
            : i
        ));
        setConfirmDisconnect(null);
      }
    } catch (_) {}
  };

  // Formatea la fecha del último test
  const fmtTested = (iso) => {
    if (!iso) return null;
    const d = new Date(iso);
    return d.toLocaleString('es-ES', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });
  };

  // Carga el estado de conexiones sociales del usuario
  const loadSocialStatus = React.useCallback(async () => {
    setSocialLoading(true);
    try {
      const res = await fetch('/api/v1/social/status', {
        headers: { Authorization: 'Bearer ' + token() },
      });
      if (res.ok) setSocialStatus(await res.json());
    } catch (_) {}
    setSocialLoading(false);
  }, []);

  React.useEffect(() => { loadSocialStatus(); }, [loadSocialStatus]);

  // Inicia el flujo OAuth de una red social en popup
  const handleSocialConnect = async (provider) => {
    const tk = token();
    if (!tk) return;
    setSocialConnecting(provider);
    try {
      const res = await fetch(`/api/v1/social/connect/${provider}`, {
        headers: { Authorization: 'Bearer ' + tk },
      });
      if (!res.ok) { setSocialConnecting(null); return; }
      const data = await res.json();
      const popup = window.open(
        data.auth_url,
        `social_oauth_${provider}`,
        'width=620,height=720,scrollbars=yes,resizable=yes'
      );
      if (!popup) {
        notify('Permite ventanas emergentes para conectar la red social');
        setSocialConnecting(null);
        return;
      }
      const interval = setInterval(async () => {
        try {
          if (popup.closed) { clearInterval(interval); setSocialConnecting(null); return; }
          // Throws cross-origin error while popup is on provider domain — expected
          const popupUrl = new URL(popup.location.href);
          if (popupUrl.origin !== window.location.origin) return;
          const code = popupUrl.searchParams.get('code');
          const state = popupUrl.searchParams.get('state');
          if (code && state) {
            clearInterval(interval);
            popup.close();
            const xres = await fetch(
              `/api/v1/social/exchange/${provider}?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`,
              { method: 'POST', headers: { Authorization: 'Bearer ' + tk } }
            );
            if (xres.ok) {
              const d = await xres.json();
              const provName = SOCIAL_PROVIDERS[provider]?.name || provider;
              notify(`${provName} conectado${d.username ? ': @' + d.username : ''}`);
              loadSocialStatus();
            } else {
              notify('Error al conectar la red social');
            }
            setSocialConnecting(null);
          }
        } catch (_) { /* popup aún en dominio del proveedor */ }
      }, 600);
      // Cancelar tras 5 minutos si el usuario cierra sin completar
      setTimeout(() => {
        if (!popup.closed) popup.close();
        clearInterval(interval);
        setSocialConnecting(null);
      }, 300000);
    } catch (_) { setSocialConnecting(null); }
  };

  const handleSocialDisconnect = async (provider) => {
    const tk = token();
    if (!tk) return;
    try {
      const res = await fetch(`/api/v1/social/disconnect/${provider}`, {
        method: 'DELETE',
        headers: { Authorization: 'Bearer ' + tk },
      });
      if (res.ok) {
        const provName = SOCIAL_PROVIDERS[provider]?.name || provider;
        notify(`${provName} desconectado`);
        loadSocialStatus();
      }
    } catch (_) {}
  };

  return (
    <div>
      <div className="page-head">
        <h1>Integraciones</h1>
        <span className="crumbs mono">
          administración <span>›</span>{' '}
          <span style={{ color: 'var(--ink)' }}>
            {loading ? '…' : `${connected.length} conectadas · ${available.length} disponibles`}
          </span>
        </span>
        <div className="actions">
          <button className="btn" onClick={loadIntegrations}><Icon name="refresh" size={13} /> Recargar</button>
        </div>
      </div>

      <div className="section">
        {err && (
          <div style={{ padding: '10px 14px', background: 'rgba(255,59,59,0.06)', border: '1px solid rgba(255,59,59,0.15)', borderRadius: 8, fontSize: 13, color: 'var(--bad)', marginBottom: 16 }}>
            {err}
          </div>
        )}

        {/* ── Redes Sociales (OAuth personal) ── */}
        <div className="section-title">
          <span>Redes sociales</span>
          <span className="line" />
          <span style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'var(--mono)' }}>OAuth personal por usuario</span>
        </div>
        <div className="int-grid" style={{ marginBottom: 28 }}>
          {Object.entries(SOCIAL_PROVIDERS).map(([provider, meta]) => {
            const conn = socialStatus.find(s => s.provider === provider);
            const isConnected = conn?.connected;
            const isConnecting = socialConnecting === provider;
            return (
              <div key={provider} className="int-card">
                <div className="int-card-head">
                  <div className="logo" style={{ background: meta.color, color: '#fff' }}>{meta.label}</div>
                  <div style={{ flex: 1 }}>
                    <div className="name">{meta.name}</div>
                    <div className="meta mono">
                      {socialLoading ? '…' : isConnected ? `@${conn.username || 'conectado'}` : 'No conectado'}
                    </div>
                  </div>
                  {isConnected
                    ? <span className="tag ok"><span className="status-dot live" />activa</span>
                    : <span className="tag" style={{ background: 'var(--bg-2)', color: 'var(--ink-3)' }}>disponible</span>
                  }
                </div>
                <p style={{ fontSize: 12, color: 'var(--ink-3)', margin: '8px 0 0', lineHeight: 1.5 }}>{meta.desc}</p>
                <div className="int-card-foot" style={{ marginTop: 10 }}>
                  {isConnected
                    ? (
                      <button
                        className="btn btn-ghost btn-sm"
                        style={{ color: 'var(--bad)', marginLeft: 'auto' }}
                        onClick={() => handleSocialDisconnect(provider)}
                      >
                        Desconectar
                      </button>
                    ) : (
                      <button
                        className="btn btn-sm"
                        disabled={isConnecting || socialLoading}
                        onClick={() => handleSocialConnect(provider)}
                        style={{ marginLeft: 'auto' }}
                      >
                        {isConnecting ? 'Conectando…' : 'Conectar'}
                      </button>
                    )
                  }
                </div>
              </div>
            );
          })}
        </div>

        {/* ── Integraciones de workspace ── */}
        {/* Conectadas */}
        {!loading && connected.length > 0 && (
          <>
            <div className="section-title"><span>Conectadas</span><span className="line" /></div>
            <div className="int-grid">
              {connected.map(i => (
                <div key={i.slug} className="int-card">
                  <div className="int-card-head">
                    <div className="logo" style={{ background: i.color, color: '#fff' }}>{i.label}</div>
                    <div style={{ flex: 1 }}>
                      <div className="name">{i.name}</div>
                      <div className="meta mono">{i.account_name || 'Cuenta conectada'}</div>
                    </div>
                    {i.status === 'error'
                      ? <span className="tag warn"><Icon name="clock" size={10} /> error</span>
                      : <span className="tag ok"><span className="status-dot live" />activa</span>
                    }
                  </div>
                  <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
                    <span className="tag" style={{ background: 'var(--bg-2)', color: 'var(--ink-3)', fontSize: 10 }}>
                      {i.scope === 'global_admin' ? 'Global/Admin' : 'Personal'}
                    </span>
                    {i.requires_admin_approval && (
                      <span className="tag warn" style={{ fontSize: 10 }}>Aprobación admin</span>
                    )}
                  </div>

                  {/* Info test */}
                  {i.last_tested_at && (
                    <div style={{ padding: '4px 0 0', fontSize: 11, color: 'var(--ink-4)', fontFamily: 'var(--mono)' }}>
                      Test {i.last_test_ok ? 'OK' : 'KO'} · {fmtTested(i.last_tested_at)}
                    </div>
                  )}

                  <div className="int-card-foot">
                    {i.api_key_masked && (
                      <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>
                        {i.api_key_masked}
                      </span>
                    )}
                    {!i.api_key_masked && i.kind === 'webhook' && (
                      <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>webhook</span>
                    )}
                    <div style={{ display: 'flex', gap: 6, marginLeft: 'auto' }}>
                      <button className="btn btn-ghost btn-sm" onClick={() => setModal({ integration: i })}>
                        Configurar
                      </button>
                      <button
                        className="btn btn-ghost btn-sm"
                        style={{ color: 'var(--bad)' }}
                        disabled={i.can_connect === false}
                        title={i.blocked_reason || ''}
                        onClick={() => setConfirmDisconnect(i)}
                      >
                        Desconectar
                      </button>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </>
        )}

        {/* Skeleton de carga */}
        {loading && (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 12 }}>
            {[...Array(6)].map((_, i) => (
              <div key={i} className="int-card" style={{ opacity: 0.4, minHeight: 90, background: 'var(--bg-2)' }} />
            ))}
          </div>
        )}

        {/* Disponibles */}
        {!loading && (
          <>
            <div className="section-title" style={{ marginTop: connected.length > 0 ? 28 : 0 }}>
              <span>Disponibles</span><span className="line" />
            </div>
            <div className="int-grid">
              {available.map(i => (
                <div key={i.slug} className="int-card" style={{ display: 'flex', flexDirection: 'column', gap: 10, padding: '16px', minHeight: 180 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                    <div className="logo" style={{ background: i.color, color: '#fff', flexShrink: 0 }}>{i.label}</div>
                    <strong style={{ fontSize: 14, flex: 1 }}>{i.name}</strong>
                    <span className="tag" style={{ background: 'var(--bg-2)', color: 'var(--ink-3)', fontSize: 10 }}>
                      {i.kind === 'webhook' ? 'webhook' : 'disponible'}
                    </span>
                  </div>
                  <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                    <span className="tag" style={{ background: 'var(--bg-2)', color: 'var(--ink-3)', fontSize: 10 }}>
                      {i.scope === 'global_admin' ? 'Global/Admin' : 'Personal'}
                    </span>
                    {i.requires_admin_approval && (
                      <span className="tag warn" style={{ fontSize: 10 }}>Aprobación admin</span>
                    )}
                  </div>
                  {i.desc && (
                    <p style={{ fontSize: 12, color: 'var(--ink-3)', margin: 0, lineHeight: 1.5 }}>{i.desc}</p>
                  )}
                  {i.blocked_reason && (
                    <div style={{ fontSize: 11.5, color: 'var(--bad)' }}>{i.blocked_reason}</div>
                  )}
                  {i.reqs && i.reqs.length > 0 && (
                    <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>
                      <span style={{ fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>Requisitos:</span>{' '}
                      {i.reqs.join(' · ')}
                    </div>
                  )}
                  {i.deployTime && (
                    <div style={{ fontSize: 11, color: 'var(--ok)', marginTop: 'auto' }}>&#9201; Despliegue: {i.deployTime}</div>
                  )}
                  <div style={{ marginTop: i.deployTime ? 0 : 'auto' }}>
                    <button className="btn btn-sm" disabled={i.can_connect === false} title={i.blocked_reason || ''} onClick={() => setModal({ integration: i })}>
                      {i.kind === 'webhook' ? 'Ver URL' : 'Conectar'}
                    </button>
                  </div>
                </div>
              ))}
            </div>
          </>
        )}
      </div>

      {/* Modal de conexión/configuración */}
      {modal && (
        <IntegrationModal
          integration={modal.integration}
          onClose={() => setModal(null)}
          onSaved={(updated) => { handleSaved(updated); setModal(null); }}
        />
      )}

      {/* Modal de confirmación de desconexión */}
      {confirmDisconnect && (
        <DisconnectConfirmModal
          integration={confirmDisconnect}
          onClose={() => setConfirmDisconnect(null)}
          onConfirm={() => handleDisconnect(confirmDisconnect)}
        />
      )}
    </div>
  );
};

window.cargarDetallesFacturacion = async function cargarDetallesFacturacion(clientId) {
  const container = document.getElementById('billing-dynamic-card');
  if (!container) return;

  const tok = sessionStorage.getItem('af_token') || '';
  const resolvedClientId = String(
    clientId ||
    sessionStorage.getItem('af_customer_id') ||
    (() => {
      try {
        const user = JSON.parse(sessionStorage.getItem('af_user') || 'null');
        return user && user.id ? user.id : '';
      } catch (_) {
        return '';
      }
    })()
  );
  if (!tok) {
    container.innerHTML = '<div style="color:var(--bad)">Sesión no válida.</div>';
    return;
  }
  if (!resolvedClientId) {
    container.innerHTML = '<div style="color:var(--bad)">No se encontró el cliente en sesión.</div>';
    return;
  }

  try {
    const res = await fetch('/api/v1/billing/details/' + encodeURIComponent(resolvedClientId), {
      headers: { Authorization: 'Bearer ' + tok },
    });
    if (!res.ok) {
      container.innerHTML = '<div style="color:var(--bad)">No se pudo cargar facturación.</div>';
      return;
    }
    const data = await res.json();
    const isActive = data.status === 'active';
    const badgeClass = isActive ? 'ok' : 'bad';
    const planLabel = String(data.plan || '').toLowerCase();
    const periodEnd = data.current_period_end ? new Date(data.current_period_end).toLocaleDateString('es-ES') : '—';
    const usedFmt = Number(data.tokens_used_this_month || 0).toLocaleString('es-ES');
    const allowedFmt = Number(data.tokens_allowed || 0).toLocaleString('es-ES');
    const pct = Math.max(0, Math.min(100, Number(data.tokens_usage_pct || 0)));

    container.innerHTML = `
      <div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;">
        <div>
          <div class="mono" style="font-size:10.5px;color:var(--ink-3);text-transform:uppercase;">Estado de suscripción</div>
          <div style="font-size:22px;font-weight:600;margin-top:4px;">
            ${planLabel.charAt(0).toUpperCase() + planLabel.slice(1)}
            <span class="tag ${badgeClass}" style="margin-left:6px;">${data.status}</span>
          </div>
          <div style="font-size:12px;color:var(--ink-3);margin-top:4px;">Renovación: ${periodEnd}</div>
        </div>
        <div>
          <div class="mono" style="font-size:10.5px;color:var(--ink-3);text-transform:uppercase;">Uso mensual de tokens</div>
          <div style="font-size:22px;font-weight:600;margin-top:4px;">${usedFmt}
            <span style="color:var(--ink-3);font-size:13px;font-weight:400;"> / ${allowedFmt}</span>
          </div>
          <div style="height:6px;background:var(--bg-3);border-radius:999px;margin-top:8px;overflow:hidden;">
            <div style="width:${pct}%;height:100%;background:linear-gradient(90deg,var(--brand),var(--brand-2));"></div>
          </div>
          <div style="font-size:12px;color:var(--ink-3);margin-top:4px;">${pct.toFixed(1)}%</div>
        </div>
        <div>
          <div class="mono" style="font-size:10.5px;color:var(--ink-3);text-transform:uppercase;">Plan contratado</div>
          <div style="display:flex;gap:8px;align-items:center;margin-top:8px;">
            <select id="billing-plan-select" class="select" style="min-width:180px;">
              <option value="starter" ${planLabel === 'starter' ? 'selected' : ''}>Starter</option>
              <option value="professional" ${planLabel === 'professional' ? 'selected' : ''}>Professional</option>
              <option value="enterprise" ${planLabel === 'enterprise' ? 'selected' : ''}>Enterprise</option>
            </select>
            <button id="billing-plan-update-btn" class="btn btn-primary">Actualizar</button>
          </div>
          <div style="font-size:12px;color:${data.is_paused ? 'var(--bad)' : 'var(--ok)'};margin-top:6px;">
            ${data.is_paused ? 'Ejecuciones pausadas' : 'Ejecuciones habilitadas'}
          </div>
        </div>
      </div>
    `;

    const btn = document.getElementById('billing-plan-update-btn');
    const sel = document.getElementById('billing-plan-select');
    if (btn && sel) {
      btn.onclick = async () => {
        btn.disabled = true;
        btn.textContent = 'Actualizando...';
        try {
          const putRes = await fetch('/api/v1/billing/details/' + encodeURIComponent(resolvedClientId) + '/plan', {
            method: 'PUT',
            headers: {
              Authorization: 'Bearer ' + tok,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ plan: sel.value }),
          });
          if (!putRes.ok) {
            window.afNotify && window.afNotify('No se pudo actualizar el plan');
            return;
          }
          window.afNotify && window.afNotify('Plan actualizado');
          await window.cargarDetallesFacturacion(resolvedClientId);
        } finally {
          btn.disabled = false;
          btn.textContent = 'Actualizar';
        }
      };
    }
  } catch (_) {
    container.innerHTML = '<div style="color:var(--bad)">Error de red cargando facturación.</div>';
  }
};

const Billing = () => {
  const plans = [
    {
      n: 'Starter', p: '97 €', per: 'agente / mes', d: 'Ideal para empezar a automatizar',
      f: ['1 agente activo', '5.000 ejecuciones/mes', 'Web chat embed', 'Logs 30 días', 'Soporte email 48 h', '1 canal (web)'],
      buy: '890 €', buyNote: 'Compra directa (sin suscripción)'
    },
    {
      n: 'Pro', p: '297 €', per: 'agente / mes', d: 'Más volumen con mantenimiento incluido', featured: true,
      f: ['1 agente activo', '25.000 ejecuciones/mes', '3 canales (web, email, WhatsApp)', 'API + webhooks', 'Logs 90 días', 'Chat en vivo 12 h', 'Mantenimiento mensual incluido'],
      buy: '2.490 €', buyNote: 'Compra + mantenimiento 97 €/mes'
    },
    {
      n: 'Enterprise', p: 'A medida', per: 'desde 497 €/mes por agente', d: 'Gobernanza, compliance y volumen',
      f: ['Agentes ilimitados', 'Ejecuciones sin límite', 'SSO + SCIM + auditoría', 'VPC dedicada · HIPAA', 'CSM + onboarding dedicado', 'SLA 99,95 % · soporte 24/7'],
      buy: null, buyNote: 'Proyecto a medida'
    },
  ];
  React.useEffect(() => {
    const token = sessionStorage.getItem('af_token') || '';
    const customerId = sessionStorage.getItem('af_customer_id') || (() => {
      try {
        const user = JSON.parse(sessionStorage.getItem('af_user') || 'null');
        return user && user.id ? String(user.id) : '';
      } catch (_) {
        return '';
      }
    })();
    if (!token || !customerId) return;
    sessionStorage.setItem('af_customer_id', customerId);
    if (typeof window.cargarDetallesFacturacion === 'function') {
      window.cargarDetallesFacturacion(customerId);
    }
  }, []);

  return (
    <div>
      <div className="page-head">
        <h1>Facturación y planes</h1>
        <span className="crumbs mono">administración <span>›</span> <span style={{ color: 'var(--ink)' }}>AgentForge · Pro · 149 €/mes</span></span>
        <div className="actions">
          <button className="btn" onClick={async () => {
            const tok = sessionStorage.getItem('af_token') || '';
            try {
              const r = await fetch('/api/v1/billing/portal', { headers: { Authorization: 'Bearer ' + tok } });
              if (!r.ok) { window.afNotify && window.afNotify('Sin cuenta de facturación activa'); return; }
              const d = await r.json();
              if (d.portal_url) window.open(d.portal_url, '_blank');
            } catch (_) { window.afNotify && window.afNotify('Error al abrir el portal'); }
          }}><Icon name="download" size={13} /> Facturas</button>
          <button className="btn btn-primary" onClick={() => window.dispatchEvent(new CustomEvent('af:contact:open', { detail: { public: false, reason: 'ventas' } }))}>Hablar con ventas</button>
        </div>
      </div>
      <div className="section">
        <div id="billing-dynamic-card" className="card card-pad">
          <div style={{ color: 'var(--ink-3)', fontSize: 13 }}>Cargando estado de suscripción...</div>
        </div>

        <div className="section-title" style={{ marginTop: 28 }}><span>Planes</span><span className="line" /></div>
        <div className="plans-grid">
          {plans.map(p => (
            <div key={p.n} className={'plan' + (p.featured ? ' featured' : '')}>
              {p.featured && <span className="plan-tag">ACTUAL</span>}
              <h3>{p.n}</h3>
              <div className="price">{p.p} <small>/ {p.per}</small></div>
              <p className="tag-line">{p.d}</p>
              <ul>
                {p.f.map(f => <li key={f}><Icon name="check" size={12} stroke={2.4} /> <span>{f}</span></li>)}
              </ul>
              <button className={p.featured ? 'btn' : 'btn btn-primary'}>{p.featured ? 'Gestionar' : p.n === 'Enterprise' ? 'Contactar ventas' : 'Mejorar'}</button>
              {p.buy && (
                <div style={{ marginTop: 10, paddingTop: 10, borderTop: '1px solid var(--border)', fontSize: 11.5, color: 'var(--ink-3)' }}>
                  <span style={{ fontWeight: 600, color: 'var(--ink-2)' }}>{p.buy}</span> · {p.buyNote}
                </div>
              )}
            </div>
          ))}
        </div>

        <div className="section-title" style={{ marginTop: 28 }}><span>Uso por agente</span><span className="line" /></div>
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <table className="lib-table">
            <thead><tr><th style={{ paddingLeft: 16 }}>Agente</th><th>Ejec.</th><th>Tokens</th><th>Herram.</th><th>Coste</th><th style={{ paddingRight: 16 }}>% del plan</th></tr></thead>
            <tbody>
              {[
                ['Triaje Nivel 1', 12840, '4,2 M', 38400, 142.18, 28],
                ['Revisor de PRs', 4280, '2,1 M', 8200, 88.40, 17],
                ['Cualificador de Leads', 1980, '0,9 M', 3960, 32.10, 6],
                ['Investigación Profunda', 612, '0,4 M', 2448, 21.80, 4],
                ['Cazador de Insights', 320, '0,1 M', 640, 12.04, 2],
              ].map((r,i) => (
                <tr key={i}>
                  <td style={{ paddingLeft: 16, fontWeight: 600 }}>{r[0]}</td>
                  <td className="mono">{r[1].toLocaleString('es-ES')}</td>
                  <td className="mono">{r[2]}</td>
                  <td className="mono">{r[3].toLocaleString('es-ES')}</td>
                  <td className="mono">{r[4].toFixed(2).replace('.', ',')} €</td>
                  <td style={{ paddingRight: 16 }}>
                    <div style={{ display:'flex', alignItems:'center', gap: 8 }}>
                      <div style={{ width: 100, height: 4, background: 'var(--bg-3)', borderRadius: 4, overflow: 'hidden' }}>
                        <div style={{ width: r[5]+'%', height: '100%', background: 'var(--brand)' }} />
                      </div>
                      <span className="mono" style={{ fontSize: 11.5 }}>{r[5]} %</span>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
};

const TEAM_ROLE_META = {
  propietario:  { label: 'Propietario',  color: '#7c3aed', bg: 'rgba(124,58,237,0.15)' },
  administrador:{ label: 'Administrador',color: '#f472b6', bg: 'rgba(244,114,182,0.15)' },
  constructor:  { label: 'Builder',      color: '#34d399', bg: 'rgba(52,211,153,0.15)'  },
  revisor:      { label: 'Revisor',      color: '#60a5fa', bg: 'rgba(96,165,250,0.15)'  },
  lector:       { label: 'Lector',       color: '#fbbf24', bg: 'rgba(251,191,36,0.15)'  },
  operador_sensible: { label: 'Operador sensible', color: '#ef4444', bg: 'rgba(239,68,68,0.15)' },
};
const TEAM_ALL_ROLES = Object.keys(TEAM_ROLE_META);

const TeamRolesPane = () => {
  const [members, setMembers] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(null); // user id being saved
  const [showAdd, setShowAdd] = React.useState(false);
  const [addSearch, setAddSearch] = React.useState('');
  const [allUsers, setAllUsers] = React.useState([]);
  const [usersLoading, setUsersLoading] = React.useState(false);
  const [showCreate, setShowCreate] = React.useState(false);

  const getToken = () => sessionStorage.getItem('af_token') || '';

  React.useEffect(() => {
    (async () => {
      setLoading(true);
      try {
        const res = await fetch('/api/v1/admin/users', { headers: { Authorization: 'Bearer ' + getToken() } });
        if (res.ok) setMembers(await res.json());
      } catch (_) {}
      setLoading(false);
    })();
  }, []);

  const addRole = async (userId, role) => {
    setSaving(userId);
    try {
      const res = await fetch('/api/v1/admin/users/' + userId + '/roles', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + getToken() },
        body: JSON.stringify({ role }),
      });
      if (res.ok) {
        const updatedRoles = await res.json();
        setMembers(prev => prev.map(m => m.id === userId ? { ...m, roles: updatedRoles } : m));
      }
    } catch (_) {}
    setSaving(null);
  };

  const removeRole = async (userId, role) => {
    setSaving(userId);
    try {
      const res = await fetch('/api/v1/admin/users/' + userId + '/roles/' + role, {
        method: 'DELETE',
        headers: { Authorization: 'Bearer ' + getToken() },
      });
      if (res.ok) {
        const updatedRoles = await res.json();
        setMembers(prev => prev.map(m => m.id === userId ? { ...m, roles: updatedRoles } : m));
      }
    } catch (_) {}
    setSaving(null);
  };

  const initials = (m) => (m.full_name || m.email || '?').split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();
  const topRoleColor = (m) => {
    if (m.is_admin) return '#ff5b1f';
    const r = (m.roles || [])[0];
    return r ? (TEAM_ROLE_META[r]?.color || '#888') : '#555';
  };

  return (
    <>
      <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>Equipo y roles</h3>
      <p style={{ margin: '0 0 18px', color: 'var(--ink-3)', fontSize: 12.5 }}>
        {loading ? 'Cargando...' : `${members.length} miembro${members.length !== 1 ? 's' : ''}`}
      </p>

      {loading ? (
        <div style={{ color: 'var(--ink-3)', fontSize: 13 }}>Cargando equipo...</div>
      ) : (
        <div>
          {members.map(m => {
            const available = TEAM_ALL_ROLES.filter(r => !(m.roles || []).includes(r));
            return (
              <div key={m.id} className="settings-row" style={{ flexDirection: 'column', alignItems: 'flex-start', gap: 10 }}>
                <div style={{ display: 'flex', gap: 10, alignItems: 'center', width: '100%' }}>
                  <div style={{ width: 32, height: 32, borderRadius: '50%', background: topRoleColor(m), color: '#fff', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 700, flexShrink: 0 }}>{initials(m)}</div>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontWeight: 600, fontSize: 13 }}>{m.full_name || '—'}</div>
                    <div style={{ fontSize: 11.5, color: 'var(--ink-3)', fontFamily: 'var(--mono)' }}>{m.email}</div>
                  </div>
                  {m.is_admin && <span style={{ fontSize: 11, fontWeight: 700, color: '#ff5b1f', background: 'rgba(255,91,31,0.1)', padding: '2px 7px', borderRadius: 4 }}>Admin</span>}
                </div>

                {/* Role badges */}
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, paddingLeft: 42 }}>
                  {(m.roles || []).map(r => {
                    const meta = TEAM_ROLE_META[r] || { label: r, color: '#888', bg: 'rgba(255,255,255,0.05)' };
                    return (
                      <span key={r} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '3px 8px', borderRadius: 5, fontSize: 12, fontWeight: 600, color: meta.color, background: meta.bg, border: '1px solid ' + meta.color + '44' }}>
                        {meta.label}
                        <button
                          onClick={() => removeRole(m.id, r)}
                          disabled={saving === m.id}
                          style={{ background: 'none', border: 'none', color: meta.color, cursor: 'pointer', padding: 0, fontSize: 14, lineHeight: 1, opacity: saving === m.id ? 0.4 : 0.7 }}
                        >×</button>
                      </span>
                    );
                  })}
                  {available.length > 0 && (
                    <select
                      value=""
                      onChange={e => e.target.value && addRole(m.id, e.target.value)}
                      disabled={saving === m.id}
                      style={{ background: '#111', border: '1px dashed #333', borderRadius: 5, padding: '2px 8px', fontSize: 11.5, color: '#888', fontFamily: 'inherit', cursor: 'pointer', outline: 'none' }}
                    >
                      <option value="">+ Añadir rol</option>
                      {available.map(r => <option key={r} value={r}>{TEAM_ROLE_META[r]?.label || r}</option>)}
                    </select>
                  )}
                </div>
              </div>
            );
          })}
        </div>
      )}

      <button className="btn btn-primary btn-sm" style={{ marginTop: 18 }} onClick={async () => {
        setShowAdd(true); setAddSearch(''); setUsersLoading(true);
        try {
          const res = await fetch('/api/v1/admin/users', { headers: { Authorization: 'Bearer ' + getToken() } });
          if (res.ok) setAllUsers(await res.json());
        } catch (_) {}
        setUsersLoading(false);
      }}>
        <Icon name="plus" size={11} /> Añadir colaborador
      </button>

      {/* Añadir colaborador modal */}
      {showAdd && (
        <div style={{ position:'fixed', inset:0, zIndex:990, background:'rgba(0,0,0,0.7)', backdropFilter:'blur(4px)', display:'flex', alignItems:'center', justifyContent:'center' }}
          onClick={e => { if (e.target === e.currentTarget) setShowAdd(false); }}>
          <div style={{ background:'#1a1a1a', border:'1px solid #2a2a2a', borderRadius:10, padding:'24px 20px', width:420, maxHeight:'80vh', display:'flex', flexDirection:'column', boxShadow:'0 16px 48px rgba(0,0,0,0.5)' }}>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:16 }}>
              <div style={{ fontWeight:700, fontSize:15 }}>Añadir colaborador</div>
              <button onClick={() => setShowAdd(false)} style={{ background:'none', border:'none', color:'#888', cursor:'pointer', padding:4 }}><Icon name="close" size={16} /></button>
            </div>
            <input
              autoFocus
              type="text"
              placeholder="Buscar por nombre o email..."
              value={addSearch}
              onChange={e => setAddSearch(e.target.value)}
              style={{ background:'#111', border:'1px solid #2a2a2a', borderRadius:6, padding:'8px 12px', fontSize:13, color:'#e5e5e5', fontFamily:'inherit', outline:'none', marginBottom:12 }}
            />
            <div style={{ flex:1, overflowY:'auto', minHeight:0 }}>
              {usersLoading ? (
                <div style={{ color:'#888', fontSize:13, textAlign:'center', padding:'20px 0' }}>Cargando usuarios...</div>
              ) : (() => {
                const q = addSearch.toLowerCase().trim();
                const filtered = allUsers.filter(u =>
                  (u.email || '').toLowerCase().includes(q) ||
                  (u.full_name || '').toLowerCase().includes(q)
                );
                if (filtered.length === 0) return <div style={{ color:'#888', fontSize:13, textAlign:'center', padding:'20px 0' }}>Sin resultados.</div>;
                return filtered.map(u => {
                  const ini = (u.full_name || u.email || '?').split(' ').map(w => w[0]).join('').slice(0,2).toUpperCase();
                  const topRole = (u.roles || [])[0];
                  const roleColor = topRole ? (TEAM_ROLE_META[topRole]?.color || '#888') : '#555';
                  return (
                    <div key={u.id}
                      onClick={() => {
                        if (!members.find(m => m.id === u.id)) setMembers(prev => [...prev, u]);
                        setShowAdd(false);
                      }}
                      style={{ display:'flex', alignItems:'center', gap:10, padding:'9px 8px', borderRadius:7, cursor:'pointer', marginBottom:2 }}
                      onMouseEnter={e => e.currentTarget.style.background='#222'}
                      onMouseLeave={e => e.currentTarget.style.background='transparent'}
                    >
                      <div style={{ width:32, height:32, borderRadius:'50%', background:roleColor, color:'#fff', display:'grid', placeItems:'center', fontSize:11, fontWeight:700, flexShrink:0 }}>{ini}</div>
                      <div style={{ flex:1, minWidth:0 }}>
                        <div style={{ fontWeight:600, fontSize:13, color:'#e5e5e5' }}>{u.full_name || '—'}</div>
                        <div style={{ fontSize:11.5, color:'#888', fontFamily:'var(--mono)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{u.email}</div>
                      </div>
                      {(u.roles || []).length > 0 && (
                        <span style={{ fontSize:11, color: TEAM_ROLE_META[(u.roles||[])[0]]?.color || '#888', fontWeight:600 }}>
                          {TEAM_ROLE_META[(u.roles||[])[0]]?.label || u.roles[0]}
                        </span>
                      )}
                    </div>
                  );
                });
              })()}
            </div>
            <div style={{ borderTop:'1px solid #222', paddingTop:12, marginTop:8 }}>
              <button className="btn" style={{ width:'100%', justifyContent:'center' }} onClick={() => { setShowAdd(false); setShowCreate(true); }}>
                <Icon name="plus" size={12} /> Crear nuevo usuario
              </button>
            </div>
          </div>
        </div>
      )}

      {showCreate && (
        <AuthModal isOpen={true} onClose={() => setShowCreate(false)} onSuccess={(u) => { setMembers(prev => [...prev, u]); setShowCreate(false); }} />
      )}
    </>
  );
};

const MfaManager = () => {
  const me = (() => { try { return JSON.parse(sessionStorage.getItem('af_user')); } catch { return null; } })();
  const token = sessionStorage.getItem('af_token');

  const [mfaEnabled, setMfaEnabled] = React.useState(me?.mfa_enabled || false);
  const [setupMode, setSetupMode] = React.useState(false);
  const [qrSvg, setQrSvg] = React.useState('');
  const [secret, setSecret] = React.useState('');
  const [activateCode, setActivateCode] = React.useState('');
  const [disableMode, setDisableMode] = React.useState(false);
  const [disableCode, setDisableCode] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [ok, setOk] = React.useState('');

  const headers = { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' };

  const startSetup = async () => {
    setLoading(true); setErr(''); setOk('');
    try {
      const res = await fetch('/api/v1/customers/mfa/setup', { method: 'POST', headers });
      const data = await res.json();
      if (!res.ok) { setErr(data.detail || 'Error al inicializar MFA'); setLoading(false); return; }
      setQrSvg(data.qr_svg); setSecret(data.secret); setSetupMode(true);
    } catch (_) { setErr('Error de red.'); }
    setLoading(false);
  };

  const activate = async () => {
    if (activateCode.length !== 6) { setErr('Introduce el código de 6 dígitos'); return; }
    setLoading(true); setErr('');
    try {
      const res = await fetch('/api/v1/customers/mfa/activate', {
        method: 'POST', headers, body: JSON.stringify({ code: activateCode }),
      });
      const data = await res.json();
      if (!res.ok) { setErr(data.detail || 'Código incorrecto'); setLoading(false); return; }
      setMfaEnabled(true); setSetupMode(false); setActivateCode('');
      setOk('MFA activado correctamente.');
      if (me) sessionStorage.setItem('af_user', JSON.stringify({ ...me, mfa_enabled: true }));
    } catch (_) { setErr('Error de red.'); }
    setLoading(false);
  };

  const disable = async () => {
    if (disableCode.length !== 6) { setErr('Introduce el código de 6 dígitos'); return; }
    setLoading(true); setErr('');
    try {
      const res = await fetch('/api/v1/customers/mfa/disable', {
        method: 'POST', headers, body: JSON.stringify({ code: disableCode }),
      });
      const data = await res.json();
      if (!res.ok) { setErr(data.detail || 'Código incorrecto'); setLoading(false); return; }
      setMfaEnabled(false); setDisableMode(false); setDisableCode('');
      setOk('MFA desactivado.');
      if (me) sessionStorage.setItem('af_user', JSON.stringify({ ...me, mfa_enabled: false }));
    } catch (_) { setErr('Error de red.'); }
    setLoading(false);
  };

  if (!me || !token) return (
    <p style={{ color: 'var(--ink-3)', fontSize: 13 }}>Inicia sesión para gestionar MFA.</p>
  );

  return (
    <div>
      <div className="settings-row" style={{ alignItems: 'flex-start' }}>
        <div className="lbl">
          <b>Autenticación de dos factores (MFA)</b>
          <p>Protege tu cuenta con Google Authenticator, Authy u otra app TOTP.</p>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 6 }}>
          {mfaEnabled
            ? <span className="tag ok"><Icon name="shield" size={11} /> Activo</span>
            : <span className="tag"><Icon name="lock" size={11} /> Inactivo</span>
          }
          {mfaEnabled
            ? <button className="btn btn-ghost btn-sm" onClick={() => { setDisableMode(v => !v); setErr(''); setOk(''); }}>
                <Icon name="x" size={11} /> Desactivar
              </button>
            : <button className="btn btn-sm" onClick={startSetup} disabled={loading}>
                <Icon name="shield" size={11} /> {loading ? '…' : 'Activar MFA'}
              </button>
          }
        </div>
      </div>

      {ok && <div style={{ marginTop: 8, padding: '8px 12px', background: 'rgba(52,211,153,0.1)', border: '1px solid rgba(52,211,153,0.3)', borderRadius: 7, fontSize: 12.5, color: 'var(--ok)' }}>{ok}</div>}
      {err && <div style={{ marginTop: 8, padding: '8px 12px', background: 'rgba(255,59,59,0.1)', border: '1px solid rgba(255,59,59,0.3)', borderRadius: 7, fontSize: 12.5, color: '#ff6b6b' }}>{err}</div>}

      {setupMode && (
        <div style={{ marginTop: 14, padding: 16, background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 10 }}>
          <p style={{ margin: '0 0 12px', fontSize: 13, fontWeight: 600 }}>1. Escanea este QR con tu app de autenticación</p>
          <div style={{ display: 'flex', gap: 20, alignItems: 'flex-start', flexWrap: 'wrap' }}>
            <div style={{ background: '#fff', padding: 8, borderRadius: 8, flexShrink: 0, lineHeight: 0 }}
              dangerouslySetInnerHTML={{ __html: qrSvg }} />
            <div style={{ flex: 1, minWidth: 200 }}>
              <p style={{ margin: '0 0 6px', fontSize: 12, color: 'var(--ink-3)' }}>¿No puedes escanear? Introduce esta clave manualmente:</p>
              <code style={{ fontSize: 12, background: 'var(--bg-3)', padding: '4px 8px', borderRadius: 5, letterSpacing: '0.1em', wordBreak: 'break-all' }}>{secret}</code>
              <p style={{ margin: '14px 0 6px', fontSize: 12, color: 'var(--ink-3)' }}>2. Introduce el código de 6 dígitos para confirmar:</p>
              <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                <input
                  className="input"
                  style={{ width: 120, letterSpacing: '0.2em', fontSize: 20, fontFamily: 'var(--mono)', textAlign: 'center' }}
                  maxLength={6} placeholder="000000"
                  value={activateCode}
                  onChange={e => { setActivateCode(e.target.value.replace(/\D/g, '')); setErr(''); }}
                  autoFocus
                />
                <button className="btn btn-primary btn-sm" onClick={activate}
                  disabled={loading || activateCode.length !== 6}>
                  {loading ? '…' : 'Activar'}
                </button>
                <button className="btn btn-ghost btn-sm"
                  onClick={() => { setSetupMode(false); setActivateCode(''); setErr(''); }}>
                  Cancelar
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      {disableMode && (
        <div style={{ marginTop: 14, padding: 16, background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 10 }}>
          <p style={{ margin: '0 0 10px', fontSize: 13, fontWeight: 600 }}>Confirma con tu código MFA para desactivar</p>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
            <input
              className="input"
              style={{ width: 120, letterSpacing: '0.2em', fontSize: 20, fontFamily: 'var(--mono)', textAlign: 'center' }}
              maxLength={6} placeholder="000000"
              value={disableCode}
              onChange={e => { setDisableCode(e.target.value.replace(/\D/g, '')); setErr(''); }}
              autoFocus
            />
            <button className="btn btn-sm" style={{ background: 'var(--bad)', color: '#fff', border: 'none' }}
              onClick={disable} disabled={loading || disableCode.length !== 6}>
              {loading ? '…' : 'Confirmar desactivación'}
            </button>
            <button className="btn btn-ghost btn-sm"
              onClick={() => { setDisableMode(false); setDisableCode(''); setErr(''); }}>
              Cancelar
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

const SettingsInner = () => {
  const [pane, setPane] = React.useState('general');
  const [saving, setSaving] = React.useState(false);
  const [settingsForm, setSettingsForm] = React.useState({
    workspace_name: 'AgentForge-IA',
    default_region: 'eu-south',
    default_model: 'sonnet',
    theme: 'dark',
    team_invite_domain: 'agentforge-ia.pro',
    team_auto_assign_reviewer: true,
    security_sso_enabled: true,
    security_scim_enabled: true,
    security_require_mfa_org: true,
    security_ip_whitelist_enabled: true,
    security_secret_rotation_days: 90,
    governance_predeploy_eval_gate: true,
    governance_human_approval_prod: true,
    governance_pii_anonymization: true,
    governance_tool_allowlist: false,
    governance_eval_cadence: 'hourly',
    compute_default_runtime: 'shared',
    compute_max_concurrency: 10,
    compute_rate_limit_rpm: 120,
    api_enabled: true,
    api_default_scope: 'read',
    audit_retention_days: 180,
    audit_stream_webhook: '',
    notifs_email_enabled: true,
    notifs_daily_digest: true,
    notifs_alert_channel: 'email',
  });
  const STORAGE_KEY = 'af_admin_settings_v1';

  React.useEffect(() => {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return;
      const parsed = JSON.parse(raw);
      if (!parsed || typeof parsed !== 'object') return;
      setSettingsForm((prev) => ({ ...prev, ...parsed }));
    } catch (_) {}
  }, []);

  const saveSettings = async () => {
    setSaving(true);
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(settingsForm));
      window.afNotify && window.afNotify('Configuración guardada');
    } catch (_) {
      window.alert('No se pudo guardar la configuración.');
    } finally {
      setSaving(false);
    }
  };

  const Toggle = ({ checked, onChange }) => (
    <button
      type="button"
      onClick={() => onChange(!checked)}
      className={'toggle' + (checked ? ' on' : '')}
      aria-pressed={checked}
      style={{ cursor: 'pointer' }}
    />
  );

  const panes = [
    ['general','General'],['team','Equipo y roles'],['security','Seguridad'],['governance','Gobernanza y evals'],
    ['compute','Cómputo y regiones'],['api','Claves API'],['audit','Registro de auditoría'],['notifs','Notificaciones'],
  ];
  return (
    <div className="settings-grid">
      <div className="settings-nav">
        {panes.map(([id, lbl]) => (
          <button key={id} className={pane === id ? 'active' : ''} onClick={() => setPane(id)}>{lbl}</button>
        ))}
      </div>
      <div className="card card-pad">
        <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 10 }}>
          <button className="btn btn-primary" onClick={saveSettings} disabled={saving}>
            <Icon name="check" size={12} /> {saving ? 'Guardando...' : 'Guardar cambios'}
          </button>
        </div>
        {pane === 'general' && (
          <>
            <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>General</h3>
            <p style={{ margin: 0, color: 'var(--ink-3)', fontSize: 12.5 }}>Identidad del espacio de trabajo y valores por defecto.</p>
            <div style={{ marginTop: 18 }}>
              <div className="settings-row">
                <div className="lbl"><b>Nombre del espacio</b><p>Aparece en la barra superior</p></div>
                <input
                  className="input"
                  value={settingsForm.workspace_name}
                  onChange={(e) => setSettingsForm((prev) => ({ ...prev, workspace_name: e.target.value }))}
                  style={{ width: 240 }}
                />
              </div>
                  <div className="settings-row">
                    <div className="lbl"><b>Región por defecto</b><p>Dónde se despliegan los agentes nuevos</p></div>
                    <select
                      className="select"
                      value={settingsForm.default_region}
                      onChange={(e) => setSettingsForm((prev) => ({ ...prev, default_region: e.target.value }))}
                      style={{ width: 240 }}
                    >
                      <option value="eu-west">eu-west-1 (Dublín)</option>
                      <option value="eu-south">eu-south-2 (Madrid)</option>
                      <option value="us-east">us-east-1 (Virginia)</option>
                      <option value="ap-south">ap-south-1 (Bombay)</option>
                    </select>
                  </div>
                  <div className="settings-row">
                    <div className="lbl"><b>Modelo por defecto</b><p>Para agentes nuevos salvo que se sobrescriba</p></div>
                    <select
                      className="select"
                      value={settingsForm.default_model}
                      onChange={(e) => setSettingsForm((prev) => ({ ...prev, default_model: e.target.value }))}
                      style={{ width: 240 }}
                    >
                      <option value="sonnet">claude-3.5-sonnet</option>
                      <option value="haiku">claude-haiku-4.5</option>
                      <option value="opus">claude-3-opus</option>
                    </select>
                  </div>
                  <div className="settings-row">
                    <div className="lbl"><b>Tema</b><p>Claro / oscuro / sistema</p></div>
                    <div style={{ display:'flex', gap: 6 }}>
                      <button className="btn btn-sm" style={settingsForm.theme === 'system' ? { background:'var(--bg-3)' } : {}} onClick={() => setSettingsForm((prev) => ({ ...prev, theme: 'system' }))}>Sistema</button>
                      <button className="btn btn-sm" style={settingsForm.theme === 'light' ? { background:'var(--bg-3)' } : {}} onClick={() => setSettingsForm((prev) => ({ ...prev, theme: 'light' }))}>Claro</button>
                      <button className="btn btn-sm" style={settingsForm.theme === 'dark' ? { background:'var(--bg-3)' } : {}} onClick={() => setSettingsForm((prev) => ({ ...prev, theme: 'dark' }))}>Oscuro</button>
                    </div>
                  </div>
                </div>
              </>
            )}
            {pane === 'team' && <TeamRolesPane />}
            {pane === 'security' && (
              <>
                <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>Seguridad</h3>
                <p style={{ margin: 0, color: 'var(--ink-3)', fontSize: 12.5 }}>MFA personal, SSO, listas blancas de IP, gestión de secretos.</p>
                <div style={{ marginTop: 18 }}>
                  <MfaManager />
                  <div style={{ margin: '16px 0', borderTop: '1px solid var(--line)' }} />
                  <div className="settings-row"><div className="lbl"><b>SSO (SAML)</b><p>Okta · obligatorio para no-propietarios</p></div><Toggle checked={!!settingsForm.security_sso_enabled} onChange={(v) => setSettingsForm((p) => ({ ...p, security_sso_enabled: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Aprovisionamiento SCIM</b><p>Sincronización automática de grupos del directorio</p></div><Toggle checked={!!settingsForm.security_scim_enabled} onChange={(v) => setSettingsForm((p) => ({ ...p, security_scim_enabled: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Exigir MFA (org)</b><p>Para todos los roles builder y administrador</p></div><Toggle checked={!!settingsForm.security_require_mfa_org} onChange={(v) => setSettingsForm((p) => ({ ...p, security_require_mfa_org: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Lista blanca de IP</b><p>Activar control de acceso por rangos</p></div><Toggle checked={!!settingsForm.security_ip_whitelist_enabled} onChange={(v) => setSettingsForm((p) => ({ ...p, security_ip_whitelist_enabled: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Rotación de secretos (días)</b><p>Rotar claves API automáticamente</p></div><input className="input" type="number" min="30" max="365" value={settingsForm.security_secret_rotation_days} onChange={(e) => setSettingsForm((p) => ({ ...p, security_secret_rotation_days: Number(e.target.value || 90) }))} style={{ width: 120 }} /></div>
                </div>
              </>
            )}
            {pane === 'governance' && (
              <>
                <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>Gobernanza y evals</h3>
                <p style={{ margin: 0, color: 'var(--ink-3)', fontSize: 12.5 }}>Compuertas previas al despliegue y evaluaciones continuas.</p>
                <div style={{ marginTop: 18 }}>
                  <div className="settings-row"><div className="lbl"><b>Compuerta de eval antes de desplegar</b><p>Mínimo 95 % de aprobado en el set dorado</p></div><Toggle checked={!!settingsForm.governance_predeploy_eval_gate} onChange={(v) => setSettingsForm((p) => ({ ...p, governance_predeploy_eval_gate: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Aprobación humana para producción</b><p>Obligatoria en el primer despliegue</p></div><Toggle checked={!!settingsForm.governance_human_approval_prod} onChange={(v) => setSettingsForm((p) => ({ ...p, governance_human_approval_prod: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Anonimización de PII en logs</b><p>Eliminar emails, nombres, tarjetas</p></div><Toggle checked={!!settingsForm.governance_pii_anonymization} onChange={(v) => setSettingsForm((p) => ({ ...p, governance_pii_anonymization: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Lista blanca de herramientas</b><p>Bloquear herramientas no permitidas en el espacio</p></div><Toggle checked={!!settingsForm.governance_tool_allowlist} onChange={(v) => setSettingsForm((p) => ({ ...p, governance_tool_allowlist: v }))} /></div>
                  <div className="settings-row"><div className="lbl"><b>Cadencia de eval continua</b><p>Cada cuánto se evalúan las ejecuciones en sombra</p></div><select className="select" value={settingsForm.governance_eval_cadence} onChange={(e) => setSettingsForm((p) => ({ ...p, governance_eval_cadence: e.target.value }))} style={{ width: 160 }}><option value="hourly">Cada hora</option><option value="daily">Diario</option><option value="weekly">Semanal</option></select></div>
                </div>
              </>
            )}
        {pane === 'compute' && (
          <div style={{ marginTop: 8 }}>
            <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>Cómputo y regiones</h3>
            <p style={{ margin: 0, color: 'var(--ink-3)', fontSize: 12.5 }}>Configura regiones de ejecución y topes operativos.</p>
            <div style={{ marginTop: 18 }}>
              <div className="settings-row"><div className="lbl"><b>Runtime por defecto</b><p>Modo de ejecución para nuevos agentes</p></div><select className="select" value={settingsForm.compute_default_runtime} onChange={(e) => setSettingsForm((p) => ({ ...p, compute_default_runtime: e.target.value }))} style={{ width: 200 }}><option value="shared">Shared</option><option value="dedicated">Dedicated</option></select></div>
              <div className="settings-row"><div className="lbl"><b>Concurrencia máxima</b><p>Número simultáneo de ejecuciones</p></div><input className="input" type="number" min="1" max="1000" value={settingsForm.compute_max_concurrency} onChange={(e) => setSettingsForm((p) => ({ ...p, compute_max_concurrency: Number(e.target.value || 10) }))} style={{ width: 120 }} /></div>
              <div className="settings-row"><div className="lbl"><b>Rate limit (RPM)</b><p>Tope de peticiones por minuto</p></div><input className="input" type="number" min="10" max="5000" value={settingsForm.compute_rate_limit_rpm} onChange={(e) => setSettingsForm((p) => ({ ...p, compute_rate_limit_rpm: Number(e.target.value || 120) }))} style={{ width: 120 }} /></div>
            </div>
          </div>
        )}
        {pane === 'api' && (
          <div style={{ marginTop: 8 }}>
            <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>Claves API</h3>
            <p style={{ margin: 0, color: 'var(--ink-3)', fontSize: 12.5 }}>Políticas de acceso programático.</p>
            <div style={{ marginTop: 18 }}>
              <div className="settings-row"><div className="lbl"><b>API habilitada</b><p>Permite uso de tokens de API</p></div><Toggle checked={!!settingsForm.api_enabled} onChange={(v) => setSettingsForm((p) => ({ ...p, api_enabled: v }))} /></div>
              <div className="settings-row"><div className="lbl"><b>Scope por defecto</b><p>Permisos para nuevas claves</p></div><select className="select" value={settingsForm.api_default_scope} onChange={(e) => setSettingsForm((p) => ({ ...p, api_default_scope: e.target.value }))} style={{ width: 180 }}><option value="read">read</option><option value="write">write</option><option value="admin">admin</option></select></div>
            </div>
          </div>
        )}
        {pane === 'audit' && (
          <div style={{ marginTop: 8 }}>
            <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>Registro de auditoría</h3>
            <p style={{ margin: 0, color: 'var(--ink-3)', fontSize: 12.5 }}>Retención y salida de eventos.</p>
            <div style={{ marginTop: 18 }}>
              <div className="settings-row"><div className="lbl"><b>Retención (días)</b><p>Tiempo de conservación del log</p></div><input className="input" type="number" min="30" max="3650" value={settingsForm.audit_retention_days} onChange={(e) => setSettingsForm((p) => ({ ...p, audit_retention_days: Number(e.target.value || 180) }))} style={{ width: 120 }} /></div>
              <div className="settings-row"><div className="lbl"><b>Webhook de auditoría</b><p>Destino opcional para streaming</p></div><input className="input" value={settingsForm.audit_stream_webhook} onChange={(e) => setSettingsForm((p) => ({ ...p, audit_stream_webhook: e.target.value }))} placeholder="https://..." style={{ width: 320 }} /></div>
            </div>
          </div>
        )}
        {pane === 'notifs' && (
          <div style={{ marginTop: 8 }}>
            <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>Notificaciones</h3>
            <p style={{ margin: 0, color: 'var(--ink-3)', fontSize: 12.5 }}>Canales y preferencias de alertas.</p>
            <div style={{ marginTop: 18 }}>
              <div className="settings-row"><div className="lbl"><b>Email habilitado</b><p>Permite envío de alertas por email</p></div><Toggle checked={!!settingsForm.notifs_email_enabled} onChange={(v) => setSettingsForm((p) => ({ ...p, notifs_email_enabled: v }))} /></div>
              <div className="settings-row"><div className="lbl"><b>Resumen diario</b><p>Enviar digest diario automático</p></div><Toggle checked={!!settingsForm.notifs_daily_digest} onChange={(v) => setSettingsForm((p) => ({ ...p, notifs_daily_digest: v }))} /></div>
              <div className="settings-row"><div className="lbl"><b>Canal principal</b><p>Destino por defecto de alertas críticas</p></div><select className="select" value={settingsForm.notifs_alert_channel} onChange={(e) => setSettingsForm((p) => ({ ...p, notifs_alert_channel: e.target.value }))} style={{ width: 180 }}><option value="email">Email</option><option value="slack">Slack</option><option value="teams">Teams</option></select></div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

const Settings = () => (
  <div>
    <div className="page-head">
      <h1>Ajustes</h1>
      <span className="crumbs mono">administración <span>›</span> <span style={{ color:'var(--ink)' }}>AgentForge</span></span>
    </div>
    <div className="section"><SettingsInner /></div>
  </div>
);

const Home = ({ data, setRoute, openAgent }) => {
  const [liveFeed, setLiveFeed] = React.useState([]);
  const [subscription, setSubscription] = React.useState(null);
  const [recentRuns, setRecentRuns] = React.useState(null);
  const [userName, setUserName] = React.useState('');
  const token = sessionStorage.getItem('af_token') || '';

  React.useEffect(() => {
    if (!token) return;
    let fallback = '';
    try {
      const payload = JSON.parse(atob(token.split('.')[1]));
      const email = payload.sub || payload.email || '';
      const raw = email.includes('@') ? email.split('@')[0] : email;
      fallback = raw.charAt(0).toUpperCase() + raw.slice(1).replace(/[._-]/g, ' ');
    } catch (_) {}
    fetch('/api/v1/customers/me', { headers: { Authorization: 'Bearer ' + token } })
      .then((r) => (r.ok ? r.json() : null))
      .then((me) => {
        const fullName = (me && (me.full_name || me.fullName)) ? String(me.full_name || me.fullName).trim() : '';
        setUserName(fullName || fallback);
      })
      .catch(() => { if (fallback) setUserName(fallback); });
  }, [token]);

  React.useEffect(() => {
    if (!token) return;
    fetch('/api/v1/billing/my-subscription', { headers: { Authorization: 'Bearer ' + token } })
      .then(r => r.ok ? r.json() : null)
      .then(d => d && setSubscription(d))
      .catch(() => {});
  }, [token]);

  React.useEffect(() => {
    if (!token) return;
    fetch('/api/v1/runs?limit=5', { headers: { Authorization: 'Bearer ' + token } })
      .then(r => r.ok ? r.json() : [])
      .then(d => setRecentRuns(Array.isArray(d) ? d : []))
      .catch(() => setRecentRuns([]));
  }, [token]);

  React.useEffect(() => {
    if (!token) return;
    let mounted = true;
    const loadFeed = async () => {
      try {
        const res = await fetch('/api/v1/agents/live-feed?limit=10', { headers: { Authorization: 'Bearer ' + token } });
        if (!res.ok) return;
        const payload = await res.json();
        const rows = Array.isArray(payload.items) ? payload.items : [];
        const entries = rows.map((x) => {
          const agent = (x.agent_name || 'Agente').toString();
          const snippet = (x.snippet || 'Actividad reciente').toString();
          const tok = Number(x.tokens || 0).toLocaleString('es-ES');
          return `${agent}: ${snippet} · ${tok} tok`;
        }).filter(Boolean);
        if (mounted && entries.length) setLiveFeed(entries);
      } catch (_) {}
    };
    loadFeed();
    const timer = setInterval(loadFeed, 20000);
    return () => { mounted = false; clearInterval(timer); };
  }, [token]);

  const agents = Array.isArray(data?.agents) ? data.agents : [];
  const cats = Array.isArray(data?.categories) ? data.categories : [];
  const tokensUsed = subscription?.tokens_used_this_month || 0;
  const tokensLimit = subscription?.tokens_allowed || 500000;
  const usagePct = Math.min(100, Math.round((tokensUsed / Math.max(tokensLimit, 1)) * 100));
  const planLabel = { starter: 'Starter', professional: 'Professional', enterprise: 'Enterprise' }[subscription?.plan] || subscription?.plan || '';
  const greeting = (() => { const h = new Date().getHours(); return h < 12 ? 'Buenos días' : h < 20 ? 'Buenas tardes' : 'Buenas noches'; })();

  const quickStart = [
    { id: '1', title: '1) Explora y elige agente', desc: 'Filtra por categoría y abre la ficha para entender su caso de uso.', cta: 'Ir a Marketplace', route: 'marketplace', icon: 'store', eta: '1 min' },
    { id: '2', title: '2) Prueba en entorno seguro', desc: 'Valida respuestas, tono y trazas antes de desplegar en producción.', cta: 'Abrir Pruebas', route: 'playground', icon: 'play', eta: '2 min' },
    { id: '3', title: '3) Ajusta flujo en Editor', desc: 'Conecta nodos, herramientas y memoria para adaptar el agente a tu negocio.', cta: 'Abrir Editor', route: 'builder', icon: 'flow', eta: '3 min' },
    { id: '4', title: '4) Despliega y monitoriza', desc: 'Activa el agente y sigue su rendimiento en Ejecuciones y Analítica.', cta: 'Ir a Implantación', route: 'deployment', icon: 'cloud', eta: '2 min' },
  ];

  return (
    <div>
      <div className="page-head">
        <h1>Inicio</h1>
        <div className="actions">
          <button className="btn" onClick={() => setRoute('playground')}><Icon name="play" size={12} /> Pruebas rápidas</button>
          <button className="btn btn-primary" onClick={() => setRoute('onboarding')}><Icon name="plus" size={13} /> Nuevo agente</button>
        </div>
      </div>

      <div className="section">

        {/* ── HERO BANNER ── */}
        <div className="home-hero">
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 10 }}>
            <div>
              <div className="mono" style={{ fontSize: 11, fontWeight: 500, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 4 }}>{greeting}</div>
              <div style={{ fontSize: 22, fontWeight: 700, lineHeight: 1.2, letterSpacing: '-0.3px' }}>{userName || 'Tu espacio de trabajo'}</div>
              <div style={{ fontSize: 13, color: 'var(--ink-3)', marginTop: 4 }}>Tu plataforma de agentes IA está operativa</div>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
              {planLabel && <span className="tag" style={{ fontSize: 12, padding: '4px 10px' }}>Plan {planLabel}</span>}
              {subscription?.status === 'active' && (
                <span className="tag ok" style={{ fontSize: 12, padding: '4px 10px', display: 'inline-flex', alignItems: 'center', gap: 5 }}>
                  <span className="status-dot" style={{ background: 'var(--ok)' }} />Activo
                </span>
              )}
              {subscription?.is_paused && <span className="tag warn" style={{ fontSize: 12, padding: '4px 10px' }}>Pausado</span>}
              {subscription && !subscription.has_subscription && <span className="tag" style={{ fontSize: 12, padding: '4px 10px' }}>Sin plan</span>}
              <button className="btn btn-sm" onClick={() => setRoute('billing')}><Icon name="wallet" size={11} /> Facturación</button>
            </div>
          </div>

          <div className="home-kpi-strip">
            <div className="home-kpi-card">
              <div className="home-kpi-value" style={{ color: 'var(--brand)' }}>{agents.length}</div>
              <div className="home-kpi-label">agentes disponibles</div>
            </div>
            <div className="home-kpi-card">
              <div className="home-kpi-value">
                {(tokensUsed / 1000).toFixed(1)}<span style={{ fontSize: 16, fontWeight: 500, color: 'var(--ink-3)' }}>k</span>
              </div>
              <div className="home-kpi-label">tokens este mes</div>
            </div>
            <div className="home-kpi-card">
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 8 }}>
                <div>
                  <div className="home-kpi-value" style={{ color: usagePct > 90 ? 'var(--bad)' : usagePct > 70 ? 'var(--warn)' : 'var(--ok)' }}>
                    {usagePct}<span style={{ fontSize: 16, fontWeight: 500, color: 'var(--ink-3)' }}>%</span>
                  </div>
                  <div className="home-kpi-label">cuota usada</div>
                </div>
                <span className="mono" style={{ fontSize: 10.5, color: 'var(--ink-4)', lineHeight: 1 }}>{(tokensUsed / 1000).toFixed(1)}k / {(tokensLimit / 1000).toFixed(0)}k</span>
              </div>
              <div style={{ height: 4, borderRadius: 2, background: 'var(--bg-3)', overflow: 'hidden' }}>
                <div style={{ height: '100%', width: `${usagePct}%`, borderRadius: 2, background: usagePct > 90 ? 'var(--bad)' : usagePct > 70 ? 'var(--warn)' : 'var(--brand)', transition: 'width 0.5s' }} />
              </div>
            </div>
            <div className="home-kpi-card" style={{ cursor: 'pointer' }} onClick={() => setRoute('runs')}>
              <div className="home-kpi-value" style={{ color: 'var(--info)' }}>
                {recentRuns === null ? '—' : recentRuns.length}
              </div>
              <div className="home-kpi-label">ejecuciones recientes</div>
            </div>
          </div>
        </div>

        {/* ── MAIN GRID: timeline + panel lateral ── */}
        <div className="home-main-grid">

          {/* Actividad reciente — timeline */}
          <div className="card card-pad">
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
              <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>Actividad reciente</div>
              {recentRuns && recentRuns.length > 0 && (
                <button className="btn btn-ghost btn-sm" onClick={() => setRoute('runs')}>Ver todas <Icon name="arrow-right" size={10} /></button>
              )}
            </div>
            {recentRuns === null ? (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {[1, 2, 3, 4, 5].map(i => (
                  <div key={i} style={{ height: 44, borderRadius: 'var(--r-sm)', background: 'var(--bg-2)', opacity: 0.3 + i * 0.1 }} />
                ))}
              </div>
            ) : recentRuns.length === 0 ? (
              <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, padding: '32px 0', color: 'var(--ink-3)', textAlign: 'center' }}>
                <div style={{ width: 48, height: 48, borderRadius: '50%', background: 'var(--bg-2)', border: '1px solid var(--line-2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon name="play" size={22} style={{ opacity: 0.3 }} />
                </div>
                <div style={{ fontSize: 14, fontWeight: 500 }}>Sin ejecuciones todavía</div>
                <div style={{ fontSize: 12.5, color: 'var(--ink-4)' }}>Prueba un agente para ver la actividad aquí</div>
                <button className="btn btn-sm btn-primary" onClick={() => setRoute('playground')}><Icon name="play" size={11} /> Probar ahora</button>
              </div>
            ) : (
              <div style={{ paddingLeft: 10 }}>
                {recentRuns.slice(0, 5).map((run, i) => {
                  const borderCol = { ok: 'var(--ok)', error: 'var(--bad)', warning: 'var(--warn)' }[run.status] || 'var(--line-3)';
                  const ts = run.created_at ? new Date(run.created_at).toLocaleString('es-ES', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '—';
                  const dur = run.duration_ms ? `${(run.duration_ms / 1000).toFixed(1)} s` : null;
                  return (
                    <div key={run.id || i} className="home-timeline-item" style={{ borderLeftColor: borderCol }}>
                      <span className="status-dot" style={{ background: borderCol, flexShrink: 0 }} />
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 13, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{run.agent_name || '—'}</div>
                        <div className="mono" style={{ fontSize: 10.5, color: 'var(--ink-4)', marginTop: 2 }}>
                          {ts}{dur ? ` · ${dur}` : ''} · {(run.tokens_used || 0).toLocaleString('es-ES')} tok
                        </div>
                      </div>
                      {run.cost_eur > 0 && <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', flexShrink: 0 }}>{run.cost_eur.toFixed(2)} €</span>}
                    </div>
                  );
                })}
              </div>
            )}
          </div>

          {/* Panel lateral: acciones rápidas + feed en vivo */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            <div className="card card-pad">
              <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 12 }}>Acciones rápidas</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {[
                  { route: 'marketplace', icon: 'store',  label: 'Explorar catálogo',   sub: `${agents.length} agentes disponibles`,     bg: 'var(--brand-soft)',                                     color: 'var(--brand)'  },
                  { route: 'playground',  icon: 'play',   label: 'Área de pruebas',     sub: 'Valida antes de desplegar',                 bg: 'color-mix(in oklab, var(--violet) 15%, var(--bg-2))', color: 'var(--violet)' },
                  { route: 'builder',     icon: 'flow',   label: 'Editor de flujos',    sub: 'Conecta nodos y herramientas',              bg: 'color-mix(in oklab, var(--teal) 15%, var(--bg-2))',   color: 'var(--teal)'   },
                  { route: 'deployment',  icon: 'cloud',  label: 'Implantación',        sub: 'Activa y monitoriza agentes',               bg: 'color-mix(in oklab, var(--info) 15%, var(--bg-2))',   color: 'var(--info)'   },
                ].map(({ route, icon, label, sub, bg, color }) => (
                  <button key={route} className="home-qa-btn" onClick={() => setRoute(route)}>
                    <span className="home-qa-icon" style={{ background: bg, color }}><Icon name={icon} size={14} /></span>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontWeight: 500 }}>{label}</div>
                      <div style={{ fontSize: 11, color: 'var(--ink-4)' }}>{sub}</div>
                    </div>
                    <Icon name="arrow-right" size={11} style={{ color: 'var(--ink-4)', flexShrink: 0 }} />
                  </button>
                ))}
              </div>
            </div>

            {liveFeed.length > 0 && (
              <div className="card card-pad" style={{ flex: 1 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 12 }}>
                  <span className="status-dot live" />
                  <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>Feed en vivo</div>
                </div>
                {liveFeed.slice(0, 5).map((entry, idx) => {
                  const colonIdx = entry.indexOf(': ');
                  const agentPart = colonIdx > -1 ? entry.slice(0, colonIdx) : entry;
                  const rest = colonIdx > -1 ? entry.slice(colonIdx + 2) : '';
                  return (
                    <div key={idx} className="home-feed-item">
                      <span style={{ color: 'var(--brand)', fontWeight: 600, flexShrink: 0 }}>{agentPart}</span>
                      {rest && <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{rest}</span>}
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        </div>

        {/* ── AGENTES SUGERIDOS ── */}
        <div className="section-title">
          <span>Sugeridos para tu equipo</span>
          <span className="line" />
          <button className="btn btn-ghost btn-sm" onClick={() => setRoute('marketplace')}>Explorar marketplace <Icon name="arrow-right" size={11} /></button>
        </div>
        <div className="agent-grid">
          {[...agents].sort((a, b) => (Number(b.runs || 0) - Number(a.runs || 0))).slice(0, 4).map((a) => {
            const cat = cats.find(c => c.id === a.cat);
            return <AgentCard key={a.id} a={a} cat={cat} onOpen={() => openAgent(a.id)} />;
          })}
        </div>

        {/* ── CÓMO EMPEZAR ── */}
        <div className="section-title" style={{ marginTop: 22 }}><span>Cómo empezar</span><span className="line" /></div>
        <div className="home-steps-grid">
          {quickStart.map((step) => (
            <div key={step.id} className="home-step-card">
              <div className="home-step-num">{step.id}</div>
              <span className="tag info" style={{ display: 'inline-flex', marginBottom: 8 }}><Icon name={step.icon} size={10} /> {step.eta}</span>
              <div style={{ fontWeight: 600, fontSize: 13.5, marginBottom: 6, marginTop: 6 }}>{step.title}</div>
              <div style={{ color: 'var(--ink-3)', fontSize: 12, lineHeight: 1.5, minHeight: 48 }}>{step.desc}</div>
              <button className="btn btn-sm" style={{ marginTop: 12, width: '100%', justifyContent: 'center' }} onClick={() => setRoute(step.route)}>
                {step.cta} <Icon name="arrow-right" size={10} />
              </button>
            </div>
          ))}
        </div>

      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// RunDetail — panel lateral de detalle editable
// ---------------------------------------------------------------------------
const RunDetail = ({ run, onClose, onSaved }) => {
  const [notes, setNotes] = React.useState(run.notes || '');
  const [status, setStatus] = React.useState(run.status);
  const [saving, setSaving] = React.useState(false);
  const [saved, setSaved] = React.useState(false);
  const token = () => sessionStorage.getItem('af_token') || '';

  const fmtFull = (iso) => {
    if (!iso) return '—';
    return new Date(iso).toLocaleString('es-ES', {
      day: '2-digit', month: 'short', year: 'numeric',
      hour: '2-digit', minute: '2-digit', second: '2-digit',
    });
  };

  const handleSave = async () => {
    setSaving(true);
    try {
      const res = await fetch(`/api/v1/runs/${run.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token() },
        body: JSON.stringify({ notes: notes.trim() || null, status }),
      });
      if (res.ok) {
        const updated = await res.json();
        setSaved(true);
        setTimeout(() => setSaved(false), 2000);
        onSaved && onSaved(updated);
      }
    } catch (_) {}
    setSaving(false);
  };

  const statusColor = { ok: 'var(--ok)', error: 'var(--bad)', warning: 'var(--warn)', skipped: 'var(--ink-3)' };

  return (
    <div style={{
      position: 'fixed', right: 0, top: 0, bottom: 0, width: 420, zIndex: 300,
      background: 'var(--bg-2)', borderLeft: '1px solid var(--line)',
      display: 'flex', flexDirection: 'column', boxShadow: '-8px 0 32px rgba(0,0,0,0.3)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 20px', borderBottom: '1px solid var(--line)' }}>
        <div>
          <div style={{ fontWeight: 700, fontSize: 15 }}>Detalle de ejecución</div>
          <div className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 2 }}>{run.id}</div>
        </div>
        <button className="btn btn-ghost btn-icon btn-sm" onClick={onClose}><Icon name="x" size={14} /></button>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '20px' }}>
        {/* Meta */}
        <div className="card card-pad" style={{ marginBottom: 16 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px 20px' }}>
            {[
              ['Agente', run.agent_name],
              ['Disparador', run.trigger_type],
              ['Duración', run.duration_ms != null ? (run.duration_ms / 1000).toFixed(3) + ' s' : '—'],
              ['Tokens', (run.tokens_used || 0).toLocaleString('es-ES')],
              ['Coste', Number(run.cost_eur || 0).toFixed(4) + ' €'],
              ['Fecha', fmtFull(run.created_at)],
            ].map(([k, v]) => (
              <div key={k}>
                <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginBottom: 3, textTransform: 'uppercase', letterSpacing: '0.05em' }}>{k}</div>
                <div className="mono" style={{ fontSize: 12.5, wordBreak: 'break-all' }}>{v}</div>
              </div>
            ))}
          </div>
        </div>

        {/* Estado editable */}
        <div style={{ marginBottom: 16 }}>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.05em' }}>Estado</div>
          <div style={{ display: 'flex', gap: 8 }}>
            {['ok', 'error', 'warning', 'skipped'].map(s => (
              <button key={s} onClick={() => setStatus(s)} style={{
                padding: '4px 12px', borderRadius: 6, fontSize: 12, fontWeight: 600, cursor: 'pointer',
                border: status === s ? `2px solid ${statusColor[s] || 'var(--brand)'}` : '2px solid var(--line)',
                background: status === s ? (statusColor[s] || 'var(--brand)') + '22' : 'transparent',
                color: status === s ? (statusColor[s] || 'var(--brand)') : 'var(--ink-3)',
              }}>{s}</button>
            ))}
          </div>
        </div>

        {/* Error message */}
        {run.error_message && (
          <div style={{ marginBottom: 16 }}>
            <div style={{ fontSize: 11, color: 'var(--bad)', marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.05em' }}>Mensaje de error</div>
            <div style={{ background: 'rgba(255,59,59,0.06)', border: '1px solid rgba(255,59,59,0.2)', borderRadius: 8, padding: '10px 14px', fontSize: 12, fontFamily: 'var(--mono)', color: 'var(--bad)', wordBreak: 'break-word', whiteSpace: 'pre-wrap' }}>
              {run.error_message}
            </div>
          </div>
        )}

        {/* Notas editables */}
        <div style={{ marginBottom: 16 }}>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.05em' }}>Notas internas</div>
          <textarea
            value={notes}
            onChange={e => setNotes(e.target.value)}
            placeholder="Añade notas o contexto sobre esta ejecución..."
            style={{
              width: '100%', minHeight: 120, padding: '10px 12px', boxSizing: 'border-box',
              background: 'var(--bg-3)', border: '1px solid var(--line)', borderRadius: 8,
              color: 'var(--ink)', fontSize: 13, fontFamily: 'var(--mono)', resize: 'vertical',
              outline: 'none',
            }}
          />
        </div>
      </div>

      <div style={{ padding: '14px 20px', borderTop: '1px solid var(--line)', display: 'flex', gap: 10 }}>
        <button className="btn" style={{ flex: 1 }} onClick={handleSave} disabled={saving}>
          {saving ? 'Guardando…' : saved ? '✓ Guardado' : 'Guardar cambios'}
        </button>
        <button className="btn btn-ghost" onClick={onClose}>Cerrar</button>
      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// RunsLogs — lista completa con filtros y panel de detalle
// ---------------------------------------------------------------------------
const RunsLogs = ({ setRoute }) => {
  const [runs, setRuns] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [statusFilter, setStatusFilter] = React.useState('all');
  const [triggerFilter, setTriggerFilter] = React.useState('all');
  const [agentSearch, setAgentSearch] = React.useState('');
  const [daysFilter, setDaysFilter] = React.useState('all');
  const [autoRefresh, setAutoRefresh] = React.useState(false);
  const [selectedRun, setSelectedRun] = React.useState(null);
  const [sortCol, setSortCol] = React.useState('created_at');
  const [sortAsc, setSortAsc] = React.useState(false);

  const getToken = () => sessionStorage.getItem('af_token') || '';

  const loadRuns = React.useCallback(async () => {
    setLoading(true);
    try {
      const params = new URLSearchParams({ limit: '500' });
      if (statusFilter !== 'all') params.set('status', statusFilter);
      if (triggerFilter !== 'all') params.set('trigger', triggerFilter);
      if (agentSearch.trim()) params.set('agent', agentSearch.trim());
      if (daysFilter !== 'all') params.set('days', daysFilter);
      const res = await fetch('/api/v1/runs?' + params, {
        headers: { Authorization: 'Bearer ' + getToken() },
      });
      if (res.ok) setRuns(await res.json());
    } catch (_) {}
    setLoading(false);
  }, [statusFilter, triggerFilter, agentSearch, daysFilter]);

  React.useEffect(() => { loadRuns(); }, [loadRuns]);

  React.useEffect(() => {
    if (!autoRefresh) return;
    const timer = setInterval(loadRuns, 15000);
    return () => clearInterval(timer);
  }, [autoRefresh, loadRuns]);

  const fmtDur = (ms) => ms == null ? '—' : (ms / 1000).toFixed(2).replace('.', ',') + ' s';
  const fmtCost = (eur) => eur == null ? '—' : Number(eur).toFixed(4).replace('.', ',') + ' €';
  const fmtWhen = (iso) => {
    if (!iso) return '—';
    const diff = Math.round((Date.now() - new Date(iso).getTime()) / 1000);
    if (diff < 10) return 'ahora mismo';
    if (diff < 60) return 'hace ' + diff + ' s';
    if (diff < 3600) return 'hace ' + Math.round(diff / 60) + ' m';
    if (diff < 86400) return 'hace ' + Math.round(diff / 3600) + ' h';
    return new Date(iso).toLocaleString('es-ES', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });
  };

  const toggleSort = (col) => {
    if (sortCol === col) setSortAsc(a => !a);
    else { setSortCol(col); setSortAsc(false); }
  };

  const sorted = React.useMemo(() => {
    const arr = [...runs];
    arr.sort((a, b) => {
      let va = a[sortCol], vb = b[sortCol];
      if (va == null) va = sortCol === 'duration_ms' ? -1 : '';
      if (vb == null) vb = sortCol === 'duration_ms' ? -1 : '';
      if (typeof va === 'string') return sortAsc ? va.localeCompare(vb) : vb.localeCompare(va);
      return sortAsc ? va - vb : vb - va;
    });
    return arr;
  }, [runs, sortCol, sortAsc]);

  const summary = React.useMemo(() => ({
    total: runs.length,
    errors: runs.filter(r => r.status === 'error').length,
    avgMs: runs.length ? runs.reduce((s, r) => s + (r.duration_ms || 0), 0) / runs.filter(r => r.duration_ms != null).length || 0 : 0,
    totalCost: runs.reduce((s, r) => s + Number(r.cost_eur || 0), 0),
  }), [runs]);

  const exportCsv = () => {
    const header = ['id', 'agente', 'estado', 'disparador', 'duracion_ms', 'tokens', 'coste_eur', 'notas', 'cuando'];
    const rows = sorted.map((r) => [
      r.id, r.agent_name, r.status, r.trigger_type,
      String(r.duration_ms ?? ''), String(r.tokens_used), String(r.cost_eur),
      String(r.notes || ''), r.created_at,
    ]);
    const csv = [header, ...rows]
      .map((line) => line.map((v) => `"${String(v).replaceAll('"', '""')}"`).join(','))
      .join('\n');
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = 'agentforge-ejecuciones.csv';
    document.body.appendChild(a); a.click();
    document.body.removeChild(a); URL.revokeObjectURL(url);
    window.afNotify && window.afNotify('Exportación CSV completada');
  };

  const onSaved = (updated) => {
    setRuns(prev => prev.map(r => r.id === updated.id ? updated : r));
    if (selectedRun && selectedRun.id === updated.id) setSelectedRun(updated);
  };

  const thStyle = (col) => ({
    cursor: 'pointer', userSelect: 'none',
    color: sortCol === col ? 'var(--brand)' : 'var(--ink-3)',
    whiteSpace: 'nowrap',
  });
  const sortIcon = (col) => sortCol === col ? (sortAsc ? ' ↑' : ' ↓') : '';

  const statusTag = (s) => {
    const cls = s === 'ok' ? 'ok' : s === 'error' ? 'bad' : 'warn';
    return <span className={'tag ' + cls}>{s}</span>;
  };

  return (
    <div style={{ display: 'flex', height: '100%' }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div className="page-head" style={{ flexWrap: 'wrap', gap: 8 }}>
          <h1 style={{ marginRight: 8 }}>Ejecuciones y logs</h1>
          {!loading && (
            <span className="crumbs mono" style={{ fontSize: 11 }}>
              {summary.total} runs · {summary.errors} errores · coste {summary.totalCost.toFixed(4)} €
            </span>
          )}
          <div className="actions" style={{ flexWrap: 'wrap', gap: 6 }}>
            <input
              className="select"
              placeholder="Buscar agente…"
              value={agentSearch}
              onChange={e => setAgentSearch(e.target.value)}
              style={{ width: 160 }}
            />
            <select className="select" value={statusFilter} onChange={e => setStatusFilter(e.target.value)} style={{ width: 130 }}>
              <option value="all">Estado: todos</option>
              <option value="ok">OK</option>
              <option value="error">Error</option>
              <option value="warning">Warning</option>
            </select>
            <select className="select" value={triggerFilter} onChange={e => setTriggerFilter(e.target.value)} style={{ width: 140 }}>
              <option value="all">Trigger: todos</option>
              <option value="playground">playground</option>
              <option value="webhook">webhook</option>
              <option value="api">api</option>
              <option value="scheduled">programado</option>
            </select>
            <select className="select" value={daysFilter} onChange={e => setDaysFilter(e.target.value)} style={{ width: 130 }}>
              <option value="all">Todo el período</option>
              <option value="1">Hoy</option>
              <option value="7">7 días</option>
              <option value="30">30 días</option>
              <option value="90">90 días</option>
            </select>
            <button className="btn" onClick={loadRuns}><Icon name="refresh" size={12} /> Recargar</button>
            <button className="btn btn-ghost btn-sm" onClick={exportCsv}><Icon name="download" size={12} /> CSV</button>
            <button
              className={'btn btn-ghost btn-sm' + (autoRefresh ? ' active' : '')}
              onClick={() => setAutoRefresh(v => !v)}
              title="Actualización automática cada 15 s"
            >
              Auto {autoRefresh && <span className="status-dot live" style={{ marginLeft: 4 }} />}
            </button>
          </div>
        </div>

        <div className="section">
          <div className="card" style={{ overflow: 'hidden' }}>
            <table className="lib-table">
              <thead>
                <tr>
                  <th style={{ paddingLeft: 16, ...thStyle('id') }} onClick={() => toggleSort('id')}>ID{sortIcon('id')}</th>
                  <th style={thStyle('agent_name')} onClick={() => toggleSort('agent_name')}>Agente{sortIcon('agent_name')}</th>
                  <th style={thStyle('status')} onClick={() => toggleSort('status')}>Estado{sortIcon('status')}</th>
                  <th style={thStyle('trigger_type')} onClick={() => toggleSort('trigger_type')}>Disparador{sortIcon('trigger_type')}</th>
                  <th style={thStyle('duration_ms')} onClick={() => toggleSort('duration_ms')}>Duración{sortIcon('duration_ms')}</th>
                  <th style={thStyle('tokens_used')} onClick={() => toggleSort('tokens_used')}>Tokens{sortIcon('tokens_used')}</th>
                  <th style={thStyle('cost_eur')} onClick={() => toggleSort('cost_eur')}>Coste{sortIcon('cost_eur')}</th>
                  <th style={{ paddingRight: 16, ...thStyle('created_at') }} onClick={() => toggleSort('created_at')}>Cuándo{sortIcon('created_at')}</th>
                </tr>
              </thead>
              <tbody>
                {loading && (
                  <tr><td colSpan={8} style={{ textAlign: 'center', color: 'var(--ink-3)', padding: '40px 0', fontSize: 13 }}>Cargando ejecuciones…</td></tr>
                )}
                {!loading && sorted.length === 0 && (
                  <tr><td colSpan={8} style={{ textAlign: 'center', color: 'var(--ink-3)', padding: '40px 0', fontSize: 13 }}>
                    Sin ejecuciones con los filtros actuales.
                  </td></tr>
                )}
                {!loading && sorted.map(r => (
                  <tr
                    key={r.id}
                    onClick={() => setSelectedRun(r)}
                    style={{
                      cursor: 'pointer',
                      background: selectedRun?.id === r.id ? 'var(--brand)11' : undefined,
                    }}
                  >
                    <td className="mono" style={{ paddingLeft: 16, fontSize: 11.5, color: 'var(--ink-3)' }}>
                      {r.id.slice(0, 8)}
                      {r.notes && <span title={r.notes} style={{ marginLeft: 6, color: 'var(--brand)' }}>●</span>}
                    </td>
                    <td style={{ fontSize: 13, maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={r.agent_name}>{r.agent_name}</td>
                    <td>{statusTag(r.status)}</td>
                    <td className="mono" style={{ color: 'var(--ink-3)', fontSize: 12 }}>{r.trigger_type}</td>
                    <td className="mono" style={{ color: r.duration_ms > 5000 ? 'var(--warn)' : 'var(--ink)' }}>{fmtDur(r.duration_ms)}</td>
                    <td className="mono">{(r.tokens_used || 0).toLocaleString('es-ES')}</td>
                    <td className="mono">{fmtCost(r.cost_eur)}</td>
                    <td className="mono" style={{ paddingRight: 16, color: 'var(--ink-3)', fontSize: 11.5 }}>{fmtWhen(r.created_at)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <div className="mono" style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 8, textAlign: 'right' }}>
            Haz clic en una fila para ver detalles y editar notas · El punto naranja indica que tiene nota
          </div>
        </div>
      </div>

      {selectedRun && (
        <RunDetail
          run={selectedRun}
          onClose={() => setSelectedRun(null)}
          onSaved={onSaved}
        />
      )}
    </div>
  );
};

// ---------------------------------------------------------------------------
// Gráfico de barras SVG con tooltip y ejes
// ---------------------------------------------------------------------------
const BarChart = ({ data, height = 200 }) => {
  const [tooltip, setTooltip] = React.useState(null);
  if (!data || data.length === 0) {
    return (
      <div style={{ height, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--ink-3)', fontSize: 13 }}>
        Sin datos de ejecuciones todavía
      </div>
    );
  }

  const maxVal = Math.max(1, ...data.map(d => d.count));
  const svgW = 800; const svgH = height;
  const padL = 36; const padR = 8; const padT = 12; const padB = 28;
  const chartW = svgW - padL - padR;
  const chartH = svgH - padT - padB;
  const barW = Math.max(2, chartW / data.length - 2);
  const fmtDay = (day) => {
    const d = new Date(day + 'T00:00:00');
    return d.toLocaleDateString('es-ES', { day: '2-digit', month: 'short' });
  };

  // Y axis labels
  const yTicks = [0, 0.25, 0.5, 0.75, 1].map(f => ({
    val: Math.round(maxVal * f),
    y: padT + chartH - Math.round(chartH * f),
  }));

  // X labels: show first, middle, last
  const xLabels = [];
  if (data.length > 0) {
    xLabels.push(0);
    if (data.length > 2) xLabels.push(Math.floor(data.length / 2));
    if (data.length > 1) xLabels.push(data.length - 1);
  }

  return (
    <div style={{ position: 'relative' }}>
      <svg viewBox={`0 0 ${svgW} ${svgH}`} style={{ width: '100%', height }} preserveAspectRatio="none">
        {/* Grid lines */}
        {yTicks.map((t, i) => (
          <g key={i}>
            <line x1={padL} y1={t.y} x2={svgW - padR} y2={t.y} stroke="var(--line)" strokeWidth="0.5" />
            <text x={padL - 4} y={t.y + 4} fontSize="9" fill="var(--ink-4)" textAnchor="end">{t.val}</text>
          </g>
        ))}
        {/* Bars */}
        {data.map((d, i) => {
          const bh = Math.max(2, (d.count / maxVal) * chartH);
          const x = padL + (i / data.length) * chartW + 1;
          const y = padT + chartH - bh;
          return (
            <rect
              key={i}
              x={x} y={y} width={Math.max(1, barW)} height={bh}
              fill="var(--brand)" rx="2"
              opacity="0.85"
              onMouseEnter={(e) => setTooltip({ x: e.clientX, y: e.clientY, day: d.day, count: d.count })}
              onMouseLeave={() => setTooltip(null)}
              style={{ cursor: 'default', transition: 'opacity 0.15s' }}
            />
          );
        })}
        {/* X labels */}
        {xLabels.map(i => (
          <text key={i} x={padL + ((i + 0.5) / data.length) * chartW} y={svgH - 6} fontSize="9" fill="var(--ink-4)" textAnchor="middle">
            {fmtDay(data[i].day)}
          </text>
        ))}
        {/* Axis */}
        <line x1={padL} y1={padT} x2={padL} y2={padT + chartH} stroke="var(--line)" strokeWidth="1" />
        <line x1={padL} y1={padT + chartH} x2={svgW - padR} y2={padT + chartH} stroke="var(--line)" strokeWidth="1" />
      </svg>
      {tooltip && (
        <div style={{
          position: 'fixed', left: tooltip.x + 12, top: tooltip.y - 32, pointerEvents: 'none',
          background: 'var(--bg)', border: '1px solid var(--line)', borderRadius: 6,
          padding: '4px 10px', fontSize: 12, fontFamily: 'var(--mono)', zIndex: 9999,
          boxShadow: '0 4px 16px rgba(0,0,0,0.3)',
        }}>
          {tooltip.day} · <strong>{tooltip.count}</strong> ejecuciones
        </div>
      )}
    </div>
  );
};

// ---------------------------------------------------------------------------
// Analytics — dashboard completo con tendencias
// ---------------------------------------------------------------------------
const Analytics = ({ data, setRoute }) => {
  const [stats, setStats] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [days, setDays] = React.useState(28);
  const getToken = () => sessionStorage.getItem('af_token') || '';

  React.useEffect(() => {
    setLoading(true);
    fetch('/api/v1/analytics/summary?days=' + days, {
      headers: { Authorization: 'Bearer ' + getToken() },
    })
      .then(r => r.ok ? r.json() : null)
      .then(d => { setStats(d); setLoading(false); })
      .catch(() => setLoading(false));
  }, [days]);

  const trend = (curr, prev) => {
    if (!prev) return null;
    const pct = ((curr - prev) / prev * 100).toFixed(1);
    const up = curr >= prev;
    return { pct, up, label: (up ? '▲ +' : '▼ ') + Math.abs(pct) + ' %' };
  };

  const topMax = stats && stats.top_agents.length > 0 ? Math.max(1, stats.top_agents[0].count) : 1;
  const triggerColors = { playground: '#60a5fa', api: '#34d399', webhook: '#a78bfa', scheduled: '#fbbf24' };

  const exportCsv = () => {
    if (!stats) return;
    const rows = [
      ['Período', days + ' días'],
      ['Total ejecuciones', stats.total_runs],
      ['Tasa éxito', stats.success_rate + '%'],
      ['Latencia media ms', stats.avg_duration_ms],
      ['Tokens totales', stats.total_tokens],
      ['Coste total EUR', stats.total_cost_eur],
      [],
      ['Fecha', 'Ejecuciones'],
      ...stats.daily_counts.map(d => [d.day, d.count]),
    ];
    const csv = rows.map(r => r.join(',')).join('\n');
    const blob = new Blob([csv], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = 'agentforge-analytics.csv';
    document.body.appendChild(a); a.click();
    document.body.removeChild(a); URL.revokeObjectURL(url);
    window.afNotify && window.afNotify('Analytics exportado');
  };

  return (
    <div>
      <div className="page-head">
        <h1>Analítica</h1>
        <span className="crumbs mono">últimos {days} días</span>
        <div className="actions">
          <select className="select" value={days} onChange={e => setDays(Number(e.target.value))} style={{ width: 140 }}>
            <option value={7}>Últimos 7 días</option>
            <option value={28}>Últimos 28 días</option>
            <option value={90}>Últimos 90 días</option>
          </select>
          <button className="btn btn-ghost btn-sm" onClick={exportCsv}><Icon name="download" size={12} /> Exportar CSV</button>
        </div>
      </div>

      <div className="section">
        {loading && <div style={{ color: 'var(--ink-3)', fontSize: 13, padding: '40px 0', textAlign: 'center' }}>Cargando analítica…</div>}
        {!loading && !stats && <div style={{ color: 'var(--ink-3)', fontSize: 13, padding: '40px 0', textAlign: 'center' }}>No se pudo cargar la analítica.</div>}
        {!loading && stats && (() => {
          const runsTrend = trend(stats.total_runs, stats.prev_total_runs);
          const rateTrend = trend(stats.success_rate, stats.prev_success_rate);
          const costTrend = trend(stats.total_cost_eur, stats.prev_cost_eur);

          return (<>
            {/* KPIs */}
            <div className="kpi-row">
              {[
                {
                  label: 'Ejecuciones totales',
                  val: stats.total_runs.toLocaleString('es-ES'),
                  trend: runsTrend,
                  trendGood: runsTrend?.up,
                  color: 'var(--ink)',
                },
                {
                  label: 'Tasa de éxito',
                  val: stats.success_rate.toFixed(1) + ' %',
                  trend: rateTrend,
                  trendGood: rateTrend?.up,
                  color: stats.success_rate >= 95 ? 'var(--ok)' : stats.success_rate >= 80 ? 'var(--warn)' : 'var(--bad)',
                },
                {
                  label: 'Latencia media',
                  val: stats.avg_duration_ms > 0 ? (stats.avg_duration_ms / 1000).toFixed(2).replace('.', ',') + ' s' : '—',
                  color: 'var(--ink)',
                },
                {
                  label: 'Tokens consumidos',
                  val: (stats.total_tokens || 0).toLocaleString('es-ES'),
                  color: 'var(--ink)',
                },
                {
                  label: 'Gasto total',
                  val: stats.total_cost_eur.toFixed(4).replace('.', ',') + ' €',
                  trend: costTrend,
                  trendGood: !costTrend?.up,
                  color: 'var(--ink)',
                },
              ].map((k, i) => (
                <div key={i} className="kpi">
                  <div className="lbl">{k.label}</div>
                  <div className="val" style={{ color: k.color }}>{k.val}</div>
                  {k.trend && (
                    <div style={{ fontSize: 11, marginTop: 4, color: k.trendGood ? 'var(--ok)' : 'var(--bad)', fontFamily: 'var(--mono)' }}>
                      {k.trend.label} vs período ant.
                    </div>
                  )}
                </div>
              ))}
            </div>

            {/* Gráfico diario */}
            <div className="card card-pad" style={{ marginTop: 16 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 }}>
                <div>
                  <div style={{ fontWeight: 700, fontSize: 15 }}>Ejecuciones en el tiempo</div>
                  <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2 }}>
                    últimos {days} días · agregado diario · {stats.daily_counts.length} puntos
                  </div>
                </div>
              </div>
              <BarChart data={stats.daily_counts} height={220} />
            </div>

            {/* Fila inferior */}
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 16 }}>
              {/* Top agentes */}
              <div className="card card-pad">
                <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 14 }}>Top agentes</div>
                {stats.top_agents.length === 0 ? (
                  <div style={{ color: 'var(--ink-3)', fontSize: 13 }}>Sin datos todavía.</div>
                ) : stats.top_agents.map((a, i) => (
                  <div key={a.name} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 0' }}>
                    <div className="mono" style={{ fontSize: 10.5, color: 'var(--ink-4)', width: 16, flexShrink: 0 }}>{i + 1}</div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginBottom: 3 }} title={a.name}>{a.name}</div>
                      <div style={{ height: 4, background: 'var(--bg-3)', borderRadius: 4, overflow: 'hidden' }}>
                        <div style={{ width: Math.round(a.count / topMax * 100) + '%', height: '100%', background: 'var(--brand)', transition: 'width 0.4s' }} />
                      </div>
                    </div>
                    <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', flexShrink: 0 }}>{a.count.toLocaleString('es-ES')}</div>
                  </div>
                ))}
              </div>

              {/* Por disparador */}
              <div className="card card-pad">
                <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 14 }}>Por disparador</div>
                {(!stats.trigger_breakdown || stats.trigger_breakdown.length === 0) ? (
                  <div style={{ color: 'var(--ink-3)', fontSize: 13 }}>Sin datos todavía.</div>
                ) : (() => {
                  const total = stats.trigger_breakdown.reduce((s, t) => s + t.count, 0) || 1;
                  return stats.trigger_breakdown.map((t) => (
                    <div key={t.trigger} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 0' }}>
                      <div style={{ width: 8, height: 8, borderRadius: '50%', background: triggerColors[t.trigger] || 'var(--brand)', flexShrink: 0 }} />
                      <div style={{ flex: 1 }}>
                        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 3 }}>
                          <span className="mono" style={{ fontSize: 12 }}>{t.trigger}</span>
                          <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>{Math.round(t.count / total * 100)} %</span>
                        </div>
                        <div style={{ height: 4, background: 'var(--bg-3)', borderRadius: 4, overflow: 'hidden' }}>
                          <div style={{ width: Math.round(t.count / total * 100) + '%', height: '100%', background: triggerColors[t.trigger] || 'var(--brand)', transition: 'width 0.4s' }} />
                        </div>
                      </div>
                      <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', flexShrink: 0 }}>{t.count.toLocaleString('es-ES')}</div>
                    </div>
                  ));
                })()}
              </div>

              {/* Errores */}
              <div className="card card-pad">
                <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 14 }}>Errores frecuentes</div>
                {stats.error_breakdown.length === 0 ? (
                  <div style={{ color: 'var(--ok)', fontSize: 13 }}>Sin errores en el período ✓</div>
                ) : stats.error_breakdown.map((e, i) => (
                  <div key={i} style={{ display: 'flex', alignItems: 'flex-start', gap: 8, padding: '5px 0', borderBottom: i < stats.error_breakdown.length - 1 ? '1px solid var(--line)' : 'none' }}>
                    <div style={{ flex: 1, fontSize: 11.5, color: 'var(--ink-2)', wordBreak: 'break-word', fontFamily: 'var(--mono)' }}>{e.message}</div>
                    <span className="tag bad" style={{ flexShrink: 0, fontSize: 10.5 }}>{e.count}×</span>
                  </div>
                ))}
              </div>
            </div>
          </>);
        })()}
      </div>
    </div>
  );
};

window.Integrations = Integrations;
window.Billing = Billing;
window.SettingsInner = SettingsInner;
window.Settings = Settings;
window.Home = Home;
window.RunsLogs = RunsLogs;
window.Analytics = Analytics;
