/* SynapIA Studio — App root (conectado ao backend real) */

const { useState, useEffect, useRef, useCallback } = React;

/* Convert basic Markdown to simple HTML for clipboard (rich-text paste into Word etc.) */
function markdownToHtml(md) {
  if (!md) return '';
  let html = md
    // headings before bold so ## isn't eaten by the bold rule
    .replace(/^### (.+)$/gm, '<h3>$1</h3>')
    .replace(/^## (.+)$/gm, '<h2>$1</h2>')
    .replace(/^# (.+)$/gm, '<h1>$1</h1>')
    .replace(/^---$/gm, '<hr>')
    // blockquotes
    .replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')
    // bold & italic
    .replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>')
    .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
    .replace(/\*(.+?)\*/g, '<em>$1</em>')
    // inline code
    .replace(/`([^`]+)`/g, '<code>$1</code>');
  // paragraph breaks: double newline → </p><p>
  html = html.replace(/\n{2,}/g, '</p><p>');
  // single newlines → <br>
  html = html.replace(/\n/g, '<br>');
  return '<p>' + html + '</p>';
}

/* Copy message content as rich HTML + plain text to clipboard */
function copyMessageAsHtml(content) {
  const html = markdownToHtml(content);
  const plain = content || '';
  const blob = new Blob([html], { type: 'text/html' });
  const textBlob = new Blob([plain], { type: 'text/plain' });
  navigator.clipboard.write([
    new ClipboardItem({
      'text/html': blob,
      'text/plain': textBlob,
    }),
  ]);
}

const THINKING_PHASES = ['Pensando', 'Analisando', 'Elaborando', 'Verificando', 'Redigindo'];
function ThinkingIndicator() {
  const [phase, setPhase] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setPhase(p => (p + 1) % THINKING_PHASES.length), 2200);
    return () => clearInterval(id);
  }, []);
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 0 6px', userSelect: 'none' }}>
      <div style={{ display: 'flex', gap: 5 }}>
        {[0, 1, 2].map(i => (
          <span key={i} style={{
            display: 'block', width: 7, height: 7, borderRadius: '50%',
            background: 'var(--accent)',
            opacity: 0.75,
            animation: `bounce 1.2s infinite ease-in-out`,
            animationDelay: `${i * 0.18}s`
          }} />
        ))}
      </div>
      <span style={{
        fontSize: 13, color: 'var(--soft)', fontStyle: 'italic',
        transition: 'opacity 0.4s',
        fontWeight: 400, letterSpacing: 0.1
      }}>
        {THINKING_PHASES[phase]}…
      </span>
    </div>
  );
}

/* ── Painel Admin de Usuários ────────────────────────────────────────────── */
const AdminPanel = ({ onClose, showToast, currentUser }) => {
  const [users, setUsers] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [creating, setCreating] = React.useState(false);
  const [form, setForm] = React.useState({ email: '', password: '', name: '', role: 'user', planId: 'starter' });
  const [busy, setBusy] = React.useState(false);
  const [confirmDelete, setConfirmDelete] = React.useState(null);

  const load = async () => {
    setLoading(true);
    try {
      const r = await fetch('/api/admin/users', { headers: authHeaders() });
      const j = await r.json();
      if (j.ok) setUsers(j.users || []);
      else showToast(j.error || 'Erro ao carregar usuários', false);
    } catch { showToast('Erro de rede', false); }
    setLoading(false);
  };

  React.useEffect(() => { load(); }, []);

  const onChangeRole = async (id, role) => {
    setBusy(true);
    try {
      const r = await fetch(`/api/admin/users/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ role })
      });
      const j = await r.json();
      if (j.ok) { showToast('Papel atualizado.'); load(); }
      else showToast(j.error || 'Erro', false);
    } catch { showToast('Erro de rede', false); }
    setBusy(false);
  };

  const onDelete = async (id) => {
    setBusy(true);
    try {
      const r = await fetch(`/api/admin/users/${id}`, { method: 'DELETE', headers: authHeaders() });
      const j = await r.json();
      if (j.ok) { showToast('Usuário removido.'); setConfirmDelete(null); load(); }
      else showToast(j.error || 'Erro', false);
    } catch { showToast('Erro de rede', false); }
    setBusy(false);
  };

  const onSendEmail = async (id) => {
    setBusy(true);
    try {
      const r = await fetch(`/api/admin/users/${id}/send-email`, { method: 'POST', headers: authHeaders() });
      const j = await r.json();
      if (j.ok) showToast(j.simulated ? 'Simulado (configure RESEND_API_KEY).' : 'E-mail enviado!');
      else showToast(j.error || 'Erro ao enviar', false);
    } catch { showToast('Erro de rede', false); }
    setBusy(false);
  };

  const onCreate = async (e) => {
    e.preventDefault();
    if (!form.email || !form.password || form.password.length < 8) {
      showToast('Informe e-mail e senha (mín. 8 chars).', false); return;
    }
    setBusy(true);
    try {
      const r = await fetch('/api/admin/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify(form)
      });
      const j = await r.json();
      if (j.ok) { showToast('Usuário criado. E-mail de boas-vindas enviado!'); setCreating(false); setForm({ email: '', password: '', name: '', role: 'user', planId: 'starter' }); load(); }
      else showToast(j.error || 'Erro', false);
    } catch { showToast('Erro de rede', false); }
    setBusy(false);
  };

  const roleLabel = (r) => r === 'admin' ? '🔑 Admin' : '👤 Usuário';
  const fmtDate = (d) => d ? new Date(d).toLocaleDateString('pt-BR') : '—';

  return (
    <div className="ob-overlay" onClick={onClose}>
      <div className="ob-modal" style={{ maxWidth: 740, width: '95vw' }} onClick={e => e.stopPropagation()}>
        <div className="ob-header">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <span style={{ fontSize: 20 }}>👥</span>
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--accent)', textTransform: 'uppercase', letterSpacing: 1 }}>Painel Administrativo</div>
              <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--ink)' }}>Gerenciar Usuários</div>
            </div>
          </div>
          <button className="icon-btn" onClick={onClose}><I.x size={14}/></button>
        </div>

        <div className="ob-body" style={{ padding: '24px 28px', maxHeight: '75vh', overflowY: 'auto' }}>
          {/* Botão criar */}
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
            <div style={{ fontSize: 13, color: 'var(--soft)' }}>
              {users.length} usuário{users.length !== 1 ? 's' : ''} cadastrado{users.length !== 1 ? 's' : ''}
            </div>
            <button className="btn-primary" onClick={() => setCreating(c => !c)} style={{ fontSize: 13, padding: '7px 16px' }}>
              <I.plus size={12} sw={2.5}/> Novo usuário
            </button>
          </div>

          {/* Formulário criar */}
          {creating && (
            <form onSubmit={onCreate} style={{ background: 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: 12, padding: 20, marginBottom: 20, display: 'grid', gap: 12 }}>
              <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink)', marginBottom: 4 }}>Novo usuário</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--soft)', marginBottom: 4, fontWeight: 600 }}>Nome</div>
                  <input className="prof-input" value={form.name} onChange={e => setForm(f => ({ ...f, name: e.target.value }))} placeholder="Nome completo"/>
                </div>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--soft)', marginBottom: 4, fontWeight: 600 }}>E-mail *</div>
                  <input className="prof-input" type="email" required value={form.email} onChange={e => setForm(f => ({ ...f, email: e.target.value }))} placeholder="usuario@escritorio.com"/>
                </div>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--soft)', marginBottom: 4, fontWeight: 600 }}>Senha * (mín. 8 chars)</div>
                  <input className="prof-input" type="password" required minLength={8} value={form.password} onChange={e => setForm(f => ({ ...f, password: e.target.value }))} placeholder="••••••••"/>
                </div>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--soft)', marginBottom: 4, fontWeight: 600 }}>Papel</div>
                  <select className="prof-input" value={form.role} onChange={e => setForm(f => ({ ...f, role: e.target.value }))}>
                    <option value="user">👤 Usuário</option>
                    <option value="admin">🔑 Admin</option>
                  </select>
                </div>
                <div>
                  <div style={{ fontSize: 11, color: 'var(--soft)', marginBottom: 4, fontWeight: 600 }}>Plano</div>
                  <select className="prof-input" value={form.planId} onChange={e => setForm(f => ({ ...f, planId: e.target.value }))}>
                    <option value="starter">Essencial · 100 unidades</option>
                    <option value="pro">Profissional · 220 unidades</option>
                    <option value="office">Escritório · 650 unidades</option>
                    <option value="enterprise">Enterprise</option>
                  </select>
                </div>
              </div>
              <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
                <button type="button" className="btn-ghost" onClick={() => setCreating(false)} style={{ fontSize: 13 }}>Cancelar</button>
                <button type="submit" className="btn-primary" disabled={busy} style={{ fontSize: 13 }}>
                  {busy ? 'Criando…' : '✅ Criar e enviar e-mail'}
                </button>
              </div>
            </form>
          )}

          {/* Lista de usuários */}
          {loading ? (
            <div style={{ textAlign: 'center', padding: 40, color: 'var(--soft)' }}>Carregando…</div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {users.map(u => (
                <div key={u.id} style={{
                  display: 'grid', gridTemplateColumns: '1fr auto',
                  background: u.id === currentUser?.id ? 'rgba(14,165,233,0.08)' : 'var(--bg-elev)',
                  border: `1px solid ${u.id === currentUser?.id ? 'var(--accent)' : 'var(--border)'}`,
                  borderRadius: 10, padding: '12px 16px', gap: 12, alignItems: 'center'
                }}>
                  <div style={{ minWidth: 0 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 3 }}>
                      <span style={{ fontWeight: 700, fontSize: 14, color: 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                        {u.name || u.email}
                      </span>
                      {u.id === currentUser?.id && <span style={{ fontSize: 10, background: 'var(--accent)', color: '#fff', borderRadius: 6, padding: '1px 7px', fontWeight: 700 }}>VOCÊ</span>}
                    </div>
                    <div style={{ fontSize: 12, color: 'var(--soft)' }}>{u.email}</div>
                    <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 2 }}>
                      {u.provider} · {u.plan_id || 'starter'} · {u.monthly_interactions_used || 0}/{u.monthly_interactions_limit || '∞'} unidades · Cadastrado em {fmtDate(u.created_at)}
                    </div>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    {/* Selector de papel */}
                    {u.id !== currentUser?.id ? (
                      <select
                        value={u.role}
                        onChange={e => onChangeRole(u.id, e.target.value)}
                        disabled={busy}
                        style={{ fontSize: 12, padding: '4px 8px', borderRadius: 7, background: 'var(--surface)', color: 'var(--ink)', border: '1px solid var(--border)', cursor: 'pointer' }}
                      >
                        <option value="user">👤 Usuário</option>
                        <option value="admin">🔑 Admin</option>
                      </select>
                    ) : (
                      <span style={{ fontSize: 12, padding: '4px 10px', borderRadius: 7, background: 'var(--surface)', color: 'var(--accent)', border: '1px solid var(--accent)', fontWeight: 700 }}>
                        {roleLabel(u.role)}
                      </span>
                    )}
                    {/* Enviar e-mail */}
                    <button
                      className="btn-ghost"
                      onClick={() => onSendEmail(u.id)}
                      disabled={busy}
                      title="Reenviar e-mail de boas-vindas"
                      style={{ fontSize: 12, padding: '5px 10px' }}
                    >
                      ✉️
                    </button>
                    {/* Deletar */}
                    {u.id !== currentUser?.id && (
                      confirmDelete === u.id ? (
                        <div style={{ display: 'flex', gap: 4 }}>
                          <button className="btn-ghost" onClick={() => onDelete(u.id)} disabled={busy} style={{ fontSize: 11, color: '#f87171', borderColor: '#f87171', padding: '4px 8px' }}>Confirmar</button>
                          <button className="btn-ghost" onClick={() => setConfirmDelete(null)} style={{ fontSize: 11, padding: '4px 8px' }}>✕</button>
                        </div>
                      ) : (
                        <button
                          className="btn-ghost"
                          onClick={() => setConfirmDelete(u.id)}
                          disabled={busy}
                          title="Remover usuário"
                          style={{ fontSize: 12, padding: '5px 8px', color: '#94a3b8' }}
                        >
                          <I.trash size={12}/>
                        </button>
                      )
                    )}
                  </div>
                </div>
              ))}
            </div>
          )}

          {/* Nota sobre e-mail */}
          <div style={{ marginTop: 20, padding: '12px 16px', background: 'var(--bg-elev)', borderRadius: 10, border: '1px solid var(--border)', fontSize: 12, color: 'var(--soft)', lineHeight: 1.6 }}>
            💡 <strong>E-mails automáticos:</strong> Configure <code>RESEND_API_KEY</code> nas variáveis de ambiente do Vercel para ativar envio real.
            Sem a chave, os e-mails são simulados (log no servidor). Obtenha em <a href="https://resend.com" target="_blank" style={{ color: 'var(--accent)' }}>resend.com</a> — grátis até 3.000/mês.
          </div>
        </div>
      </div>
    </div>
  );
};

const PROVIDER_LABELS = { openrouter: 'OpenRouter', openai: 'OpenAI', groq: 'Groq', deepgram: 'Deepgram', gemini: 'Google Gemini' };

function UsageBillingModal({ onClose, showToast }) {
  const [usage, setUsage] = React.useState(null);
  const [keys, setKeys] = React.useState([]);
  const [provider, setProvider] = React.useState('openrouter');
  const [apiKey, setApiKey] = React.useState('');
  const [busy, setBusy] = React.useState(false);

  const load = React.useCallback(async () => {
    const [u, k] = await Promise.all([
      fetch('/api/usage/me', { headers: authHeaders() }).then(r => r.json()).catch(() => null),
      fetch('/api/provider-keys', { headers: authHeaders() }).then(r => r.json()).catch(() => null),
    ]);
    if (u?.ok) setUsage(u);
    if (k?.ok) setKeys(k.keys || []);
  }, []);

  React.useEffect(() => { load(); }, [load]);

  const saveKey = async (e) => {
    e.preventDefault();
    if (!apiKey.trim()) return;
    setBusy(true);
    try {
      const r = await fetch('/api/provider-keys', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ provider, apiKey, label: PROVIDER_LABELS[provider] }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok || !j.ok) throw new Error(j.error || 'Falha ao salvar chave');
      setApiKey('');
      setKeys(j.keys || []);
      showToast('Chave individual conectada.');
    } catch (err) { showToast(String(err.message || err), false); }
    setBusy(false);
  };

  const removeKey = async (p) => {
    setBusy(true);
    try {
      const r = await fetch('/api/provider-keys/' + encodeURIComponent(p), { method: 'DELETE', headers: authHeaders() });
      const j = await r.json().catch(() => ({}));
      if (j.ok) { setKeys(j.keys || []); showToast('Chave removida.'); }
      else throw new Error(j.error || 'Falha ao remover');
    } catch (err) { showToast(String(err.message || err), false); }
    setBusy(false);
  };

  const startOpenRouterOAuth = async () => {
    const raw = crypto.getRandomValues(new Uint8Array(32));
    const verifier = btoa(String.fromCharCode(...raw)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
    const challenge = btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    sessionStorage.setItem('synapia.or.verifier', verifier);
    window.location.href = `https://openrouter.ai/auth?callback_url=${encodeURIComponent(window.location.origin + window.location.pathname)}&code_challenge=${encodeURIComponent(challenge)}&code_challenge_method=S256`;
  };

  const plan = usage?.plan || {};
  const month = usage?.month || {};
  const pct = plan.creditsUsd ? Math.min(100, Math.round((Number(month.estimatedCostUsd || 0) / Number(plan.creditsUsd || 1)) * 100)) : 0;
  const usedUnits = Number(month.totalUnits || usage?.user?.monthlyInteractionsUsed || 0);
  const unitPct = plan.interactions ? Math.min(100, Math.round((usedUnits / Number(plan.interactions || 1)) * 100)) : 0;

  return (
    <div className="ob-overlay" onClick={onClose}>
      <div className="ob-modal" style={{ maxWidth: 900, width: '94vw' }} onClick={e => e.stopPropagation()}>
        <div className="ob-header">
          <div><div className="ob-eyebrow">PLANO & USO</div><strong>Custos, quotas e consumo individual</strong></div>
          <button className="icon-btn" onClick={onClose}><I.x size={14}/></button>
        </div>
        <div className="ob-body" style={{ display: 'grid', gap: 18, maxHeight: '78vh', overflowY: 'auto' }}>
          <section style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12 }}>
            <div className="agent-combo-card"><span className="combo-k">Plano atual</span><strong>{plan.name || 'Starter'}</strong><small>{plan.priceBrl ? `R$ ${plan.priceBrl}/mês · ${plan.interactions} unidades` : 'Contrato dedicado'}</small></div>
            <div className="agent-combo-card"><span className="combo-k">Crédito técnico</span><strong>US$ {Number(month.estimatedCostUsd || 0).toFixed(3)} / {plan.creditsUsd || 'sob proposta'}</strong><small>{pct}% do orçamento mensal estimado</small></div>
            <div className="agent-combo-card"><span className="combo-k">Unidades</span><strong>{usedUnits} / {plan.interactions || 'ilimitado'}</strong><small>{unitPct}% da franquia de interações</small></div>
          </section>

          <section style={{ border: '1px solid var(--border)', borderRadius: 8, background: 'var(--bg-elev)', padding: 16 }}>
            <div className="eyebrow">Consumo por funcionalidade</div>
            <div style={{ display: 'grid', gap: 8, marginTop: 10 }}>
              {(usage?.usage || []).length ? usage.usage.map(row => (
                <div key={row.feature} style={{ display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 10, fontSize: 12.5, color: 'var(--fg-2)' }}>
                  <strong style={{ color: 'var(--ink)' }}>{usage.costs?.[row.feature]?.label || row.feature}</strong>
                  <span>{Number(row.units || 0)} unidades</span>
                  <span>US$ {Number(row.estimatedCostUsd || 0).toFixed(3)}</span>
                </div>
              )) : <p style={{ margin: 0, color: 'var(--muted)', fontSize: 13 }}>Ainda não há consumo registrado neste mês.</p>}
            </div>
          </section>

          <section style={{ border: '1px solid var(--border)', borderRadius: 8, background: 'var(--bg-elev)', padding: 16 }}>
            <div className="eyebrow">APIs automáticas</div>
            <p style={{ margin: '6px 0 12px', color: 'var(--muted)', fontSize: 13, lineHeight: 1.55 }}>
              O SynapIA gerencia modelos, OCR, áudio e pesquisa no backend. Cada conta recebe consumo individual, franquia mensal e custo estimado por funcionalidade, sem que o usuário precise colar ou contratar chaves de API.
            </p>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 8 }}>
              <div className="agent-combo-card"><span className="combo-k">Provedores</span><strong>Automáticos</strong><small>Roteamento feito pela plataforma</small></div>
              <div className="agent-combo-card"><span className="combo-k">Uso individual</span><strong>Ativo</strong><small>Consumo separado por usuário</small></div>
              <div className="agent-combo-card"><span className="combo-k">Governança</span><strong>{keys.length ? 'Dedicada' : 'Compartilhada'}</strong><small>Limites e custos por plano</small></div>
            </div>
          </section>
        </div>
      </div>
    </div>
  );
}

function App() {
  const [authUser, setAuthUser] = useState(() => window.readStoredUser ? window.readStoredUser() : null);
  const [authLoading, setAuthLoading] = useState(true);
  const [sbCollapsed, setSb] = useState(false);
  const [conversations, setConversations] = useState([]);
  const [activeId, setActiveId] = useState(null);
  const [conversationId, setConversationId] = useState(null);
  const [messages, setMessages] = useState([]);
  const [draft, setDraft] = useState('');
  const [mode, setMode] = useState('chat');
  const [busy, setBusy] = useState(false);
  const [bridge, setBridge] = useState(null);
  const [processSession, setProcessSession] = useState(null);
  const [showOnboarding, setShowOnboarding] = useState(() => !localStorage.getItem('synapia.onb-seen'));
  const [profile, setProfile] = useState(() => loadProfile());
  const [showProfile, setShowProfile] = useState(false);
  const [showStyle, setShowStyle] = useState(false);
  const [showUsage, setShowUsage] = useState(false);
  const [showAdmin, setShowAdmin] = useState(false);
  const [showFormatting, setShowFormatting] = useState(false);
  const [theme, setTheme] = useState(() => localStorage.getItem('synapia.theme') || 'dark');
  const [streaming, setStreaming] = useState(false);
  const stopRef = useRef(false);
  const streamCtlRef = useRef(null);
  
  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    localStorage.setItem('synapia.theme', theme);
  }, [theme]);
  
  const onToggleTheme = () => setTheme(t => t === 'dark' ? 'light' : 'dark');
  const [attachments, setAttachments] = useState([]); // [{ name, attachmentId }]
  const [healthInfo, setHealthInfo] = useState(null);
  const [toast, setToast] = useState(null);
  const [showScrollBtn, setShowScrollBtn] = useState(false);
  const [agents, setAgents] = useState([]);
  const [activeAgent, setActiveAgent] = useState(null); // agent object or null
  const [availableSkills, setAvailableSkills] = useState([]);
  const [selectedSkills, setSelectedSkills] = useState(() => {
    try { return JSON.parse(localStorage.getItem('synapia.selectedSkills') || '[]'); }
    catch { return []; }
  });
  const toastTimerRef = useRef(null);

  const showToast = useCallback((msg, ok = true) => {
    if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
    setToast({ msg, ok });
    toastTimerRef.current = setTimeout(() => setToast(null), 2600);
  }, []);

  useEffect(() => {
    if (!authUser) return;
    const params = new URLSearchParams(window.location.search || '');
    const code = params.get('code');
    const verifier = sessionStorage.getItem('synapia.or.verifier');
    if (!code || !verifier) return;
    sessionStorage.removeItem('synapia.or.verifier');
    fetch('/api/provider-keys/openrouter/exchange', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', ...authHeaders() },
      body: JSON.stringify({ code, codeVerifier: verifier, codeChallengeMethod: 'S256' }),
    }).then(r => r.json()).then(j => {
      if (j.ok) showToast('OpenRouter conectada ao seu usuário.');
      else showToast(j.error || 'Falha ao conectar OpenRouter.', false);
      window.history.replaceState({}, '', window.location.pathname);
      setShowUsage(true);
    }).catch(() => showToast('Falha ao conectar OpenRouter.', false));
  }, [authUser, showToast]);

  const messagesRef = useRef(null);
  const fileInputRef = useRef(null);
  const templateInputRef = useRef(null);
  const [templateInfo, setTemplateInfo] = useState(null);
  const [styleInfo, setStyleInfo] = useState(null);
  const [showCanvas, setShowCanvas] = useState(false);
  const [canvasContent, setCanvasContent] = useState('');
  const [canvasTitle, setCanvasTitle] = useState('');
  const [canvasVersions, setCanvasVersions] = useState([]);

  const refreshTemplateInfo = useCallback(async () => {
    try {
      const b = await fetch('/api/branding/info', { headers: authHeaders() }).then(r => r.json());
      if (b?.hasTemplate) {
        setTemplateInfo({ nome: b.meta?.originalName || 'modelo.docx', tamanho: b.meta?.logoCount || 0, hasTimbre: true });
        return;
      }
    } catch {}
    try {
      const j = await fetch('/api/template', { headers: authHeaders() }).then(r => r.json());
      setTemplateInfo(j.template || null);
    } catch {}
  }, []);

  const refreshStyleInfo = useCallback(async () => {
    try {
      const j = await fetch('/api/style/list', { headers: authHeaders() }).then(r => r.json());
      const backup = (!j.total && window.styleListFromBackup) ? window.styleListFromBackup() : null;
      const src = backup || j;
      setStyleInfo({ total: src.total || 0, profile_disponivel: !!src.profile_disponivel, updated: src.updated || null });
    } catch {}
  }, []);

  useEffect(() => {
    let mounted = true;
    const finish = () => mounted && setAuthLoading(false);
    const bootAuth = async () => {
      try {
        const hash = new URLSearchParams((window.location.hash || '').replace(/^#/, ''));
        const oauthToken = hash.get('access_token');
        if (oauthToken) {
          const r = await fetch('/api/auth/oauth-session', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ accessToken: oauthToken }),
          });
          const j = await r.json().catch(() => ({}));
          if (r.ok && j.ok) {
            saveAuthSession(j);
            if (mounted) setAuthUser(j.user);
            window.history.replaceState({}, '', window.location.pathname);
            return;
          }
        }

        const token = localStorage.getItem(AUTH_TOKEN_KEY);
        if (!token) {
          if (mounted) setAuthUser(null);
          return;
        }
        const r = await fetch('/api/auth/me', { headers: authHeaders() });
        const j = await r.json().catch(() => ({}));
        if (r.ok && j.ok) {
          if (mounted) setAuthUser(j.user);
        } else {
          clearAuthSession();
          if (mounted) setAuthUser(null);
        }
      } catch {
        clearAuthSession();
        if (mounted) setAuthUser(null);
      } finally {
        finish();
      }
    };
    bootAuth();
    return () => { mounted = false; };
  }, []);

  // ── Boot: health, conversations, bridge by URL
  useEffect(() => {
    api.health().then(setHealthInfo).catch(() => {});
    refreshConversations();
    // Load agents
    fetch('/api/agents').then(r => r.json()).then(j => { if (j.agents) setAgents(j.agents); }).catch(() => {});
    fetch('/api/skills').then(r => r.json()).then(j => { if (j.skills) setAvailableSkills(j.skills); }).catch(() => {});
    // Load template/style indicators
    refreshTemplateInfo();
    refreshStyleInfo();
    const sid = getSessionFromURL();
    if (sid) {
      setProcessSession(sid);
      api.getBridge(sid).then(b => { if (b) setBridge(b); });
    }
    // Keepalive: ping a cada 4 min para manter função serverless quente e reduzir cold start
    const keepAlive = setInterval(() => fetch('/api/health').catch(() => {}), 4 * 60 * 1000);
    return () => clearInterval(keepAlive);
  }, [refreshTemplateInfo, refreshStyleInfo]);

  // Auto-scroll
  useEffect(() => {
    if (!messagesRef.current) return;
    if (messages.length === 0) { messagesRef.current.scrollTop = 0; setShowScrollBtn(false); }
    else messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
  }, [messages, busy]);

  const onMessagesScroll = useCallback((e) => {
    const el = e.currentTarget;
    const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
    setShowScrollBtn(!atBottom && messages.length > 0);
  }, [messages.length]);

  // Atalho ⌘N / Ctrl+N
  useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'n') {
        e.preventDefault();
        onNew();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  const refreshAgents = useCallback(() => {
    fetch('/api/agents').then(r => r.json()).then(j => { if (j.agents) setAgents(j.agents); }).catch(() => {});
  }, []);

  const refreshSkills = useCallback(() => {
    fetch('/api/skills').then(r => r.json()).then(j => { if (j.skills) setAvailableSkills(j.skills); }).catch(() => {});
  }, []);

  useEffect(() => {
    try { localStorage.setItem('synapia.selectedSkills', JSON.stringify(selectedSkills)); } catch {}
  }, [selectedSkills]);

  const onToggleSkill = useCallback((name) => {
    setSelectedSkills(prev => prev.includes(name) ? prev.filter(x => x !== name) : [...prev, name]);
  }, []);

  const refreshConversations = useCallback(async () => {
    try {
      const j = await api.listConversations();
      const items = (j.items || []).map(c => ({
        id: c.id,
        title: c.title || 'Conversa sem título',
        when: relativeTime(c.updatedAt || c.createdAt),
        pinned: false,
        _raw: c,
      }));
      setConversations(items);
    } catch {}
  }, []);

  const onSelect = useCallback(async (id) => {
    setActiveId(id);
    setConversationId(id);
    setBusy(true);
    setShowCanvas(false);
    setCanvasContent('');
    setCanvasTitle('');
    setCanvasVersions([]);
    try {
      const conv = await api.getConversation(id);
      if (conv && Array.isArray(conv.messages)) {
        const mapped = conv.messages.map(m => ({
          role: m.role,
          mode: conv.mode,
          content: m.content,
          thinking: m.thinking,
          sources: m.sources || [],
          tools: normalizeToolTrace(m.toolTrace),
        }));
        setMessages(mapped);
        if (conv.mode) setMode(conv.mode);
      }
      // Load canvas if exists
      try {
        const cr = await fetch(`/api/canvas/${id}`, { headers: authHeaders() });
        const cj = await cr.json();
        if (cj?.canvas?.content) {
          const cv = cj.canvas;
          setCanvasContent(cv.content);
          setCanvasTitle(cv.title || 'Peça em andamento');
          setCanvasVersions(cv.versions?.map(v => typeof v === 'string' ? v : v.content) || [cv.content]);
          setShowCanvas(true);
        }
      } catch {}
    } finally { setBusy(false); }
  }, []);

  const onNew = useCallback(() => {
    setActiveId(null);
    setConversationId(null);
    setMessages([]);
    setDraft('');
    setAttachments([]);
    setShowCanvas(false);
    setCanvasContent('');
    setCanvasTitle('');
    setCanvasVersions([]);
  }, []);

  const onLogout = useCallback(async () => {
    await fetch('/api/auth/logout', { method: 'POST', headers: authHeaders() }).catch(() => {});
    clearAuthSession();
    setAuthUser(null);
    setMessages([]);
    setConversationId(null);
    setActiveId(null);
  }, []);

  const onPickPrompt = (text) => setDraft(text);
  const onPickMode = (m) => setMode(m);

  const onPickAgent = (agent) => {
    setActiveAgent(agent);
    if (agent.modoDefault) setMode(agent.modoDefault);
    showToast(`Agente "${agent.nome}" ativado.`);
  };

  const onShare = useCallback(() => {
    const url = conversationId
      ? `${window.location.origin}${window.location.pathname}?conv=${conversationId}`
      : window.location.href;
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(url).then(() => showToast('Link copiado!')).catch(() => fallbackCopy(url));
    } else {
      fallbackCopy(url);
    }
    function fallbackCopy(text) {
      try {
        const ta = document.createElement('textarea');
        ta.value = text;
        ta.style.position = 'fixed';
        document.body.appendChild(ta);
        ta.focus();
        ta.select();
        document.execCommand('copy');
        document.body.removeChild(ta);
        showToast('Link copiado!');
      } catch (e) {
        showToast('Não foi possível copiar.', false);
      }
    }
  }, [conversationId, showToast]);

  const onStop = useCallback(() => {
    stopRef.current = true;
    setStreaming(false);
    try { streamCtlRef.current?.abort(); } catch {}
    showToast('Geração interrompida.');
  }, [showToast]);

  const onSend = useCallback(async () => {
    const text = draft.trim();
    if (!text || busy) return;

    const attachmentIds = [...new Set(attachments.map(a => a.attachmentId).filter(Boolean))];
    const attachmentId = attachmentIds[0] || null;
    const attachLabel = attachments.length ? `\n\n(${attachments.length} anexo(s): ${attachments.map(a => a.name).join(', ')})` : '';

    const userMsg = { role: 'user', content: text + attachLabel };
    setMessages(m => [...m, userMsg]);
    setDraft('');
    setBusy(true);

    // Placeholder do assistant — preenchido em tempo real via SSE
    const replyData = {
      role: 'assistant',
      mode,
      content: '',
      thinking: '',
      status: mode === 'pecas' ? 'Preparando leitura dos anexos' : 'Preparando resposta',
      sources: [],
      tools: selectedSkills.map(name => ({ name: `skill:${name}`, status: 'running', args: { origem: 'SKILL.md selecionada' } })),
      latencyMs: null,
      ts: new Date().toISOString(),
      streaming: true,
    };
    setMessages(m => [...m, replyData]);
    setBusy(false);
    setStreaming(true);
    stopRef.current = false;

    let aborted = false;
    const ctl = api.chatStream({
      conversationId,
      message: text,
      mode,
      processSession,
      profile,
      attachmentId,
      attachmentIds,
      selectedSkills,
      agentSupplement: activeAgent?.systemSupplement || null,
    }, {
      onMeta: (m) => {
        if (m?.conversationId && !conversationId) {
          setConversationId(m.conversationId);
          setActiveId(m.conversationId);
        }
      },
      onDelta: (chunk) => {
        if (stopRef.current) { aborted = true; ctl.abort(); return; }
        setMessages(prev => {
          const next = [...prev];
          const last = next[next.length - 1];
          if (last && last.role === 'assistant') last.content = (last.content || '') + chunk;
          return next;
        });
      },
      onThinking: (chunk) => {
        setMessages(prev => {
          const next = [...prev];
          const last = next[next.length - 1];
          if (last && last.role === 'assistant') last.thinking = (last.thinking || '') + chunk;
          return next;
        });
      },
      onStatus: (payload) => {
        setMessages(prev => {
          const next = [...prev];
          const last = next[next.length - 1];
          if (last && last.role === 'assistant') last.status = payload?.text || payload?.phase || 'Gerando';
          return next;
        });
      },
      onDone: (j) => {
        // Reconcilia com versão final sanitizada do servidor
        setMessages(prev => {
          const next = [...prev];
          const last = next[next.length - 1];
          if (last && last.role === 'assistant') {
            if (j.reply) last.content = j.reply;
            if (j.thinking) last.thinking = j.thinking;
            last.status = '';
            last.tools = normalizeToolTrace(j.toolTrace);
            last.latencyMs = j.latencyMs;
            last.mode = j.mode || mode;
            last.model = j.model;
            last.streaming = false;
          }
          return next;
        });
        setStreaming(false);
        if (j.conversationId && !conversationId) {
          setConversationId(j.conversationId);
          setActiveId(j.conversationId);
        }
        if (j.canvas && j.canvas.content) {
          setCanvasContent(j.canvas.content);
          setCanvasTitle(j.canvas.title || 'Peça em andamento');
          setCanvasVersions(j.canvas.versions || [j.canvas.content]);
          setShowCanvas(true);
        }
        setAttachments([]);
        refreshConversations();
      },
      onError: (msg) => {
        if (aborted) return;
        setMessages(prev => {
          const next = [...prev];
          const last = next[next.length - 1];
          if (last && last.role === 'assistant') {
            last.content = (last.content && last.content.length > 0)
              ? last.content + `\n\n[stream interrompido: ${msg}]`
              : `Não foi possível obter resposta agora. ${msg}`;
            last.streaming = false;
          }
          return next;
        });
        setStreaming(false);
      }
    });

    // Liga onStop ao abort
    streamCtlRef.current = ctl;
  }, [draft, busy, attachments, selectedSkills, conversationId, mode, processSession, profile, refreshConversations, activeAgent]);

  const onCloseOnboarding = () => {
    setShowOnboarding(false);
    localStorage.setItem('synapia.onb-seen', '1');
  };

  // Composer tools
  const onAttachClick = () => fileInputRef.current?.click();
  const onFilesPicked = async (e) => {
    const files = e.target.files;
    if (!files || !files.length) return;
    setBusy(true);
    try {
      const j = await api.upload(files);
      if (j.ok) {
        const items = (j.arquivos || []).map(a => ({ name: a.nome, attachmentId: a.attachmentId || j.attachmentId }));
        setAttachments(prev => [...prev, ...items]);
      }
    } catch (err) {
      showToast('Falha no upload: ' + err.message, false);
    } finally {
      setBusy(false);
      e.target.value = '';
    }
  };

  const onMicClick = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { showToast('Reconhecimento de voz não disponível neste navegador.', false); return; }
    const rec = new SR();
    rec.lang = 'pt-BR';
    rec.continuous = false;
    rec.interimResults = false;
    rec.onresult = (ev) => {
      const txt = ev.results[0]?.[0]?.transcript || '';
      if (txt) { setDraft(d => (d ? d + ' ' : '') + txt); showToast('Ditado: "' + txt.slice(0, 40) + '"'); }
    };
    rec.onerror = (ev) => console.warn('voice', ev.error);
    rec.start();
  };

  const onTemplatePicked = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    setBusy(true);
    try {
      const fd = new FormData();
      fd.append('file', file);
      const r = await fetch('/api/template/upload', { method: 'POST', headers: authHeaders(), body: fd });
      const j = await r.json();
      if (j.ok) {
        setTemplateInfo({ nome: j.nome, tamanho: j.tamanho });
        showToast('Template salvo: ' + j.nome);
      } else {
        showToast(j.error || 'Falha ao salvar template', false);
      }
    } catch (err) {
      showToast('Falha no upload do template: ' + err.message, false);
    } finally {
      setBusy(false);
      e.target.value = '';
    }
  };

  const onRefineClick = async () => {
    if (!draft.trim()) { showToast('Escreva um rascunho antes de refinar.', false); return; }
    setBusy(true);
    try {
      const j = await api.refine(draft, processSession ? { processSession } : null);
      if (j.ok && j.refined) {
        setDraft(j.refined);
        showToast('Prompt refinado.');
      } else {
        showToast('Refinamento indisponível agora.', false);
      }
    } catch (e) { showToast('Falha ao refinar.', false); }
    finally { setBusy(false); }
  };

  const onOpenExtension = () => setShowOnboarding(true);
  const onOpenProfile = () => setShowProfile(true);
  const onOpenStyle = () => setShowStyle(true);
  const onOpenAdmin = () => setShowAdmin(true);

  // ── Canvas handlers ──────────────────────────────────────────────────
  const canvasSaveTimer = useRef(null);
  const onCanvasChange = useCallback((val) => {
    setCanvasContent(val);
    if (!conversationId) return;
    if (canvasSaveTimer.current) clearTimeout(canvasSaveTimer.current);
    canvasSaveTimer.current = setTimeout(() => {
      fetch(`/api/canvas/${conversationId}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ content: val, title: canvasTitle }),
      }).catch(() => {});
    }, 1200);
  }, [conversationId, canvasTitle]);

  const onCanvasClose = useCallback(() => setShowCanvas(false), []);

  const onCanvasUndo = useCallback(async () => {
    if (!conversationId) return;
    try {
      const r = await fetch(`/api/canvas/${conversationId}/undo`, { method: 'POST', headers: authHeaders() });
      const j = await r.json();
      if (j.content !== undefined) {
        setCanvasContent(j.content || '');
        setCanvasVersions(j.versions || []);
      }
    } catch {}
  }, [conversationId]);

  const onCanvasEditCommand = useCallback((command) => {
    if (!command) return;
    setDraft(command);
    setMode('pecas');
    showToast('Comando de edição preparado. Envie para aplicar no Canvas.');
  }, [showToast]);

  const onOpenCanvas = useCallback((msg) => {
    const content = msg.content || '';
    // Extract title from first line or heading
    const firstLine = content.split('\n')[0] || '';
    const title = firstLine.replace(/^#+\s*/, '').replace(/\*\*/g, '').slice(0, 80) || 'Peça em andamento';
    setCanvasContent(content);
    setCanvasTitle(title);
    setCanvasVersions([content]);
    setShowCanvas(true);
    // Save to backend if conversation exists
    if (conversationId) {
      fetch(`/api/canvas/${conversationId}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ content, title }),
      }).catch(() => {});
    }
  }, [conversationId]);

  // ── PDF Tools ──────────────────────────────────────────────────────────
  const [showPdfTools, setShowPdfTools] = React.useState(false);
  const onInsertOcrText = (text) => {
    setDraft(d => d ? d + '\n\n' + text : text);
    showToast('Texto extraído inserido no rascunho.');
  };

  // ── Bridge manual (extensão) ───────────────────────────────────────────
  const [showBridgeInput, setShowBridgeInput] = React.useState(false);
  const [bridgeInputVal, setBridgeInputVal] = React.useState('');
  const onImportBridgeManual = async () => {
    const sid = bridgeInputVal.trim();
    if (!sid) return;
    try {
      const b = await api.getBridge(sid);
      if (b && b.numero) {
        setBridge(b);
        setProcessSession(sid);
        setShowBridgeInput(false);
        setBridgeInputVal('');
        showToast(`Processo ${b.numero} importado.`);
      } else {
        showToast('Sessão não encontrada ou inválida.', false);
      }
    } catch { showToast('Erro ao conectar à extensão.', false); }
  };

  const onDelete = useCallback(async (id) => {
    try {
      await api.deleteConversation(id);
      setConversations(prev => prev.filter(c => c.id !== id));
      if (id === activeId) { setActiveId(null); setConversationId(null); setMessages([]); }
      showToast('Conversa excluída.');
    } catch { showToast('Não foi possível excluir.', false); }
  }, [activeId, showToast]);

  const activeConv = conversations.find(c => c.id === activeId);
  const title = activeConv ? activeConv.title : (messages.length ? 'Diálogo em andamento' : 'Novo diálogo');
  const isWelcome = messages.length === 0;

  if (authLoading) {
    return <div className="auth-loading"><div className="gs-orbit"><span></span><span></span><span></span></div><strong>Preparando SynapIA Studio</strong></div>;
  }

  if (!authUser) {
    return <AuthScreen onSuccess={(user) => setAuthUser(user)} />;
  }

  return (
    <div className={`app ${sbCollapsed ? 'sb-collapsed' : ''}`}>
      <Sidebar
        collapsed={sbCollapsed}
        conversations={conversations}
        activeId={activeId}
        onSelect={onSelect}
        onNew={onNew}
        onGoHome={onNew}
        bridge={bridge}
        onOpenExtension={onOpenExtension}
        onOpenProfile={onOpenProfile}
        onOpenStyle={onOpenStyle}
        onOpenUsage={() => setShowUsage(true)}
        onOpenAdmin={onOpenAdmin}
        onDelete={onDelete}
        user={authUser}
        onLogout={onLogout}
      />

      <div className="main">
        <Topbar
          title={title}
          sidebarCollapsed={sbCollapsed}
          onToggleSidebar={() => setSb(s => !s)}
          mode={mode}
          bridge={bridge}
          onShare={onShare}
          conversationId={conversationId}
          onOpenProfile={onOpenProfile}
          theme={theme}
          onToggleTheme={onToggleTheme}
          user={authUser}
          onLogout={onLogout}
        />

        <div className={`workspace ${showCanvas ? 'has-canvas' : ''}`}>
          <div className="messages" ref={messagesRef} onScroll={onMessagesScroll}>
            {isWelcome && (
              <Welcome
                profile={profile}
                bridge={bridge}
                onPickPrompt={onPickPrompt}
                onPickMode={onPickMode}
                onPickAgent={onPickAgent}
                agents={agents}
                onAgentsChange={refreshAgents}
                availableSkills={availableSkills}
                selectedSkills={selectedSkills}
                onToggleSkill={onToggleSkill}
                onSkillsChange={refreshSkills}
                onOpenExtension={onOpenExtension}
                onOpenUsage={() => setShowUsage(true)}
                onOnboardingStart={() => setShowOnboarding(true)}
                mode={mode}
              />
            )}
            {!isWelcome && (
              <div className="messages-inner">
                {messages.map((m, i) => (
                  <Message key={i} m={m}
                    onCopy={() => { copyMessageAsHtml(m.content); showToast('Copiado para a área de transferência.'); }}
                    onRegen={() => {}}
                    onOpenCanvas={onOpenCanvas}
                  />
                ))}
                {busy && <ThinkingIndicator/>}
              </div>
            )}
          </div>
          {showCanvas && (
            <Canvas
              content={canvasContent}
              title={canvasTitle}
              onClose={onCanvasClose}
              onUndo={onCanvasUndo}
              onChange={onCanvasChange}
              onEditCommand={onCanvasEditCommand}
              hasVersions={canvasVersions.length > 1}
            />
          )}
        </div>

        {<Composer
          value={draft}
          onChange={setDraft}
          onSend={onSend}
          mode={mode}
          onMode={setMode}
          busy={busy}
          streaming={streaming}
          onStop={onStop}
          attachments={attachments}
          onRemoveAttach={(i) => setAttachments(a => a.filter((_, k) => k !== i))}
          onAttachClick={onAttachClick}
          onMicClick={onMicClick}
          onRefineClick={onRefineClick}
          onTemplateClick={() => setShowStyle(true)}
          templateInfo={templateInfo}
          styleInfo={styleInfo}
          onStyleClick={() => setShowStyle(true)}
          onPdfToolsClick={() => setShowPdfTools(true)}
          onFormattingClick={() => setShowFormatting(true)}
          onImportBridgeClick={() => setShowBridgeInput(true)}
          bridge={bridge}
          centered={false}
        />}

        <input
          ref={fileInputRef}
          type="file"
          multiple
          accept=".pdf,.docx,.txt,.md,.csv,.json,.html,.xml,image/*"
          style={{ display: 'none' }}
          onChange={onFilesPicked}
        />
        <input
          ref={templateInputRef}
          type="file"
          accept=".docx"
          style={{ display: 'none' }}
          onChange={onTemplatePicked}
        />
      </div>

      <Onboarding open={showOnboarding} onClose={onCloseOnboarding} onJump={() => {}}/>
      {showProfile && <ProfileModal profile={profile} onClose={() => setShowProfile(false)} onSave={(p) => { setProfile(p); saveProfile(p); setShowProfile(false); showToast('Perfil salvo.'); }}/>}
      {showStyle && <StyleLearningModal onClose={() => setShowStyle(false)} showToast={showToast} onTemplateChanged={refreshTemplateInfo} onStyleChanged={refreshStyleInfo}/>}
      {showUsage && <UsageBillingModal onClose={() => setShowUsage(false)} showToast={showToast}/>} 
      {showAdmin && <AdminPanel onClose={() => setShowAdmin(false)} showToast={showToast} currentUser={authUser}/>}
      {showFormatting && <FormattingModal onClose={() => setShowFormatting(false)} showToast={showToast}/>}
      {showPdfTools && <PdfToolsPanel onClose={() => setShowPdfTools(false)} onInsertText={onInsertOcrText}/>}
      {showBridgeInput && (
        <div className="ob-overlay" onClick={() => setShowBridgeInput(false)}>
          <div className="bridge-input-modal" onClick={e => e.stopPropagation()}>
            <div className="bridge-input-header">
              <I.link size={14}/> Importar processo da extensão
              <button className="icon-btn" onClick={() => setShowBridgeInput(false)}><I.x size={13}/></button>
            </div>
            <p className="bridge-input-hint">Cole aqui o ID de sessão exibido na extensão SynapIA para o Chrome/Edge:</p>
            <input
              className="bridge-input-field"
              type="text"
              placeholder="Ex: sess_abc123xyz..."
              value={bridgeInputVal}
              onChange={e => setBridgeInputVal(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && onImportBridgeManual()}
              autoFocus
            />
            <div className="bridge-input-actions">
              <button className="btn-ghost" onClick={() => setShowBridgeInput(false)}>Cancelar</button>
              <button className="btn-primary" onClick={onImportBridgeManual} disabled={!bridgeInputVal.trim()}>
                <I.link size={12}/> Conectar
              </button>
            </div>
          </div>
        </div>
      )}
      {toast && <div className={`toast ${toast.ok ? '' : 'toast-err'}`}>{toast.ok ? <I.check size={12} sw={2.2}/> : <I.x size={12} sw={2}/>} {toast.msg}</div>}
      {showScrollBtn && (
        <button className="scroll-btn" onClick={() => { messagesRef.current.scrollTop = messagesRef.current.scrollHeight; setShowScrollBtn(false); }}>
          <I.arrowDown size={14} sw={2}/>
        </button>
      )}
    </div>
  );
}

/* Modal simples de perfil (Dr. Nome / instituição / estilo) */
const ProfileModal = ({ profile, onClose, onSave }) => {
  const [p, setP] = useState(profile || {});
  return (
    <div className="ob-overlay" onClick={onClose}>
      <div className="ob-card" onClick={e => e.stopPropagation()} style={{ maxWidth: 520 }}>
        <button className="ob-close" onClick={onClose}><I.x size={14} sw={2}/></button>
        <div className="ob-body" style={{ textAlign: 'left', padding: '28px 28px 20px' }}>
          <div className="ob-eyebrow">PERFIL & ESTILO</div>
          <h2 className="ob-title" style={{ textAlign: 'left' }}>Como devo me dirigir a você?</h2>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginTop: 16 }}>
            <Field label="Nome de tratamento" value={p.name || ''} onChange={v => setP(s => ({ ...s, name: v }))} placeholder="Ex.: Dr. Silva, Dra. Lima, Des. Mendes"/>
            <Field label="Cargo / função" value={p.role || ''} onChange={v => setP(s => ({ ...s, role: v }))} placeholder="Advogado · OAB/MG"/>
            <Field label="Instituição" value={p.institution || ''} onChange={v => setP(s => ({ ...s, institution: v }))} placeholder="Escritório · Vara · Tribunal"/>
            <Field label="Sobre o seu trabalho" value={p.about || ''} onChange={v => setP(s => ({ ...s, about: v }))} placeholder="Áreas de atuação, especialidades, contexto." multi/>
            <Field label="Estilo preferido de redação" value={p.style || ''} onChange={v => setP(s => ({ ...s, style: v }))} placeholder="Doutrinário; objetivo; com base legal explícita." multi/>
          </div>
        </div>
        <div className="ob-foot">
          <span className="eyebrow">Salvo localmente · sem nuvem</span>
          <div className="ob-actions">
            <button className="btn-ghost" onClick={onClose}>Cancelar</button>
            <button className="btn-primary" onClick={() => onSave(p)}>Salvar <I.check size={12} sw={2.2}/></button>
          </div>
        </div>
      </div>
    </div>
  );
};
const Field = ({ label, value, onChange, placeholder, multi }) => (
  <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <span className="eyebrow">{label}</span>
    {multi
      ? <textarea value={value} onChange={e => onChange(e.target.value)} placeholder={placeholder}
                  rows={3}
                  style={{ width: '100%', padding: '8px 10px', border: '1px solid var(--border-2)', borderRadius: 'var(--r-2)', fontFamily: 'inherit', fontSize: 13.5, resize: 'vertical', background: 'var(--bg-elev)', color: 'var(--ink)' }}/>
      : <input value={value} onChange={e => onChange(e.target.value)} placeholder={placeholder}
                style={{ width: '100%', padding: '8px 10px', border: '1px solid var(--border-2)', borderRadius: 'var(--r-2)', fontFamily: 'inherit', fontSize: 13.5, background: 'var(--bg-elev)', color: 'var(--ink)' }}/>}
  </label>
);

/* Modal: Aprender estilo de escrita do usuário a partir de peças anexadas */

/* Sub-seção: identidade visual / timbrado / logo do escritório.
   Funde-se ao StyleLearningModal acima. Faz upload de um .docx modelo,
   extrai cabeçalho/rodapé/logo/fontes/margens e armazena como template-shell
   no servidor. Endpoints: /api/branding/{upload,info,template,generate}. */
const BrandingTemplateSection = ({ showToast, onChanged }) => {
  const [info, setInfo] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const fileRef = React.useRef(null);

  const refresh = async () => {
    try {
      const j = await fetch('/api/branding/info', { headers: authHeaders() }).then(r => r.json());
      setInfo(j);
      return j;
    } catch { setInfo({ hasTemplate: false }); }
    return { hasTemplate: false };
  };
  React.useEffect(() => { refresh(); }, []);

  const onPick = () => fileRef.current?.click();

  const onFile = async (e) => {
    const f = e.target.files?.[0];
    e.target.value = '';
    if (!f) return;
    if (!f.name.toLowerCase().endsWith('.docx')) {
      showToast('Envie um arquivo .docx (Word moderno).', false);
      return;
    }
    setBusy(true);
    try {
      const fd = new FormData();
      fd.append('file', f);
      const r = await fetch('/api/branding/upload', { method: 'POST', headers: authHeaders(), body: fd });
      const j = await r.json();
      if (!j.ok) {
        showToast(j.error || 'Falha ao processar o modelo.', false);
      } else {
        showToast('Identidade visual extraída. As próximas peças sairão com seu timbrado.');
        await refresh();
        if (onChanged) onChanged();
      }
    } catch { showToast('Erro de rede no upload.', false); }
    setBusy(false);
  };

  const onClear = async () => {
    if (!confirm('Remover o template visual cadastrado?')) return;
    await fetch('/api/branding/template', { method: 'DELETE', headers: authHeaders() });
    showToast('Template removido.');
    await refresh();
    if (onChanged) onChanged();
  };

  const onTestDownload = async () => {
    setBusy(true);
    try {
      const demo = [
        '# I — DEMONSTRAÇÃO DO TIMBRADO',
        '',
        'Este é um documento gerado para validar a aplicação da identidade visual do escritório. O cabeçalho com **logo**, o rodapé, a fonte padrão, as margens e o tema de cores foram herdados do modelo enviado.',
        '',
        '# II — TIPOGRAFIA E LAYOUT',
        '',
        'O parágrafo padrão está justificado, com recuo de primeira linha de 1,25cm e espaçamento entre linhas de 1,5. Combinações como *itálico* e **negrito** são preservadas.',
        '',
        '- Item de lista 1',
        '- Item de lista 2',
        '',
        'Caso este teste apareça com a sua marca corretamente, o template está pronto para uso em peças reais.'
      ].join('\n');
      const r = await fetch('/api/branding/generate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ content: demo, filename: 'teste-timbrado' })
      });
      if (!r.ok) {
        const j = await r.json().catch(() => ({}));
        showToast(j.error || 'Falha ao gerar.', false);
      } else {
        const blob = await r.blob();
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url; a.download = 'teste-timbrado.docx';
        document.body.appendChild(a); a.click(); a.remove();
        setTimeout(() => URL.revokeObjectURL(url), 2000);
      }
    } catch { showToast('Erro de rede.', false); }
    setBusy(false);
  };

  const meta = info?.meta;
  const m = meta?.pageMarginsCm;

  return (
    <div>
      <div style={{ display: 'flex', gap: 10, marginBottom: 12, flexWrap: 'wrap' }}>
        <input ref={fileRef} type="file" accept=".docx" onChange={onFile} style={{ display: 'none' }} />
        <button className="btn-primary" onClick={onPick} disabled={busy}>
          <I.attach size={12} sw={2.2}/> {busy ? 'Processando…' : (info?.hasTemplate ? 'Substituir modelo' : 'Enviar modelo .docx')}
        </button>
        {info?.hasTemplate && (
          <>
            <button className="btn-ghost" onClick={onTestDownload} disabled={busy}>
              <I.download size={12} sw={2.2}/> Baixar Word de teste
            </button>
            <button className="btn-ghost" onClick={onClear} disabled={busy}>
              <I.trash size={12} sw={2.2}/> Remover
            </button>
          </>
        )}
      </div>

      {info?.hasTemplate && meta && (
        <div style={{
          marginTop: 14,
          padding: 16,
          background: 'linear-gradient(135deg, var(--surface) 0%, var(--bg-elev) 100%)',
          borderRadius: 12,
          border: '1px solid var(--border)',
          boxShadow: '0 2px 8px rgba(0,0,0,0.06)'
        }}>
          {/* Grid: logo + info */}
          <div style={{ display: 'grid', gridTemplateColumns: meta.logoPreview ? '140px 1fr' : '1fr', gap: 20, marginBottom: 16 }}>
            {meta.logoPreview && (
              <div style={{
                background: '#fff',
                padding: 12,
                borderRadius: 10,
                border: '2px solid var(--border)',
                display: 'flex',
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center'
              }}>
                <img src={meta.logoPreview} alt="logo" style={{ width: '100%', maxHeight: 120, height: 'auto', display: 'block', marginBottom: 8 }} />
                <div style={{ fontSize: 10, textAlign: 'center', color: 'var(--soft)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5 }}>{meta.logoFromHeader ? 'Logo do cabeçalho' : 'Imagem extraída'}</div>
                {meta.logoFile && <div style={{ fontSize: 9, textAlign: 'center', color: 'var(--soft)', marginTop: 2, maxWidth: 112, overflow: 'hidden', textOverflow: 'ellipsis' }}>{meta.logoFile.replace('word/media/', '')}</div>}
              </div>
            )}

            {/* Info em columns */}
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, fontSize: 12.5, color: 'var(--ink)', lineHeight: 1.8 }}>
              <div>
                <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8, marginBottom: 6 }}>Arquivo</div>
                <div>{meta.originalName}</div>

                <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8, marginBottom: 6, marginTop: 12 }}>Tipografia</div>
                <div>{meta.fonts?.default || '(Calibri padrão)'}</div>
                {meta.fontSizePt && <div style={{ fontSize: 11.5, color: 'var(--soft)' }}>Tamanho: {meta.fontSizePt}pt</div>}
              </div>

              <div>
                <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8, marginBottom: 6 }}>Página</div>
                <div>{meta.pageSizeCm?.width}×{meta.pageSizeCm?.height} cm ({meta.orientation})</div>

                <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8, marginBottom: 6, marginTop: 12 }}>Margens</div>
                <div style={{ fontSize: 11.5, fontFamily: 'monospace' }}>
                  {m?.top}cm ↑ &nbsp; {m?.left}cm ←
                </div>
              </div>
            </div>
          </div>

          {/* Seção de detalhes: cabeçalho, rodapé, cores */}
          <div style={{ borderTop: '1px solid var(--border-2)', paddingTop: 12 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
              {/* Cabeçalho */}
              <div>
                <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8, marginBottom: 6 }}>Cabeçalho</div>
                {meta.headerCount > 0 ? (
                  <>
                    <div style={{ fontSize: 12, color: 'var(--ink)', marginBottom: 6 }}>
                      {meta.headerCount} cabeçalho(s) {meta.headerHasImage ? '(com logo)' : ''}
                    </div>
                    {meta.headerPreview && (
                      <div style={{ fontSize: 11, color: 'var(--muted)', fontStyle: 'italic', background: 'var(--bg-elev)', padding: 8, borderRadius: 6, borderLeft: '3px solid var(--ink)' }}>
                        "{meta.headerPreview.slice(0, 100)}{meta.headerPreview.length > 100 ? '…' : ''}"
                      </div>
                    )}
                  </>
                ) : (
                  <div style={{ fontSize: 11, color: 'var(--soft)' }}>Nenhum cabeçalho detectado</div>
                )}
              </div>

              {/* Rodapé */}
              <div>
                <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8, marginBottom: 6 }}>Rodapé</div>
                {meta.footerCount > 0 ? (
                  <>
                    <div style={{ fontSize: 12, color: 'var(--ink)', marginBottom: 6 }}>
                      {meta.footerCount} rodapé(s)
                    </div>
                    {meta.footerPreview && (
                      <div style={{ fontSize: 11, color: 'var(--muted)', fontStyle: 'italic', background: 'var(--bg-elev)', padding: 8, borderRadius: 6, borderLeft: '3px solid var(--ink)' }}>
                        "{meta.footerPreview.slice(0, 100)}{meta.footerPreview.length > 100 ? '…' : ''}"
                      </div>
                    )}
                  </>
                ) : (
                  <div style={{ fontSize: 11, color: 'var(--soft)' }}>Nenhum rodapé detectado</div>
                )}
              </div>
            </div>

            {/* Paleta de cores */}
            {(meta.accent1 || meta.accent2) && (
              <div style={{ marginTop: 12, paddingTop: 12, borderTop: '1px solid var(--border-2)' }}>
                <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8, marginBottom: 8 }}>Paleta de Cores</div>
                <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
                  {meta.accent1 && (
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <span style={{ display: 'inline-block', width: 28, height: 28, background: meta.accent1, borderRadius: 6, border: '2px solid var(--border)', boxShadow: '0 2px 4px rgba(0,0,0,0.1)' }} title={meta.accent1} />
                      <span style={{ fontSize: 11, fontFamily: 'monospace', color: 'var(--soft)' }}>{meta.accent1}</span>
                    </div>
                  )}
                  {meta.accent2 && (
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <span style={{ display: 'inline-block', width: 28, height: 28, background: meta.accent2, borderRadius: 6, border: '2px solid var(--border)', boxShadow: '0 2px 4px rgba(0,0,0,0.1)' }} title={meta.accent2} />
                      <span style={{ fontSize: 11, fontFamily: 'monospace', color: 'var(--soft)' }}>{meta.accent2}</span>
                    </div>
                  )}
                </div>
              </div>
            )}
          </div>
        </div>
      )}

      {!info?.hasTemplate && (
        <div style={{
          marginTop: 12,
          padding: 14,
          background: 'linear-gradient(135deg, var(--surface) 0%, var(--bg-elev) 100%)',
          borderRadius: 10,
          border: '1px solid var(--border-2)',
          fontSize: 12,
          lineHeight: 1.7,
          color: 'var(--muted)'
        }}>
          <strong style={{ color: 'var(--ink)' }}>Nenhum modelo cadastrado.</strong> Quando enviar um .docx do seu escritório, o sistema preserva integralmente <strong>cabeçalho, rodapé, logo, fonte, paleta de cores, margens e numeração</strong> — e usa esse esqueleto para entregar Word pronto em cada peça.
        </div>
      )}
    </div>
  );
};

const StyleLearningModal = ({ onClose, showToast, onTemplateChanged, onStyleChanged }) => {
  const [list, setList] = useState({ samples: [], total: 0, profile_disponivel: false });
  const [titulo, setTitulo] = useState('');
  const [tipo, setTipo] = useState('peça');
  const [texto, setTexto] = useState('');
  const [profile, setProfile] = useState(null);
  const [busy, setBusy] = useState(false);
  /* tab: 'paste' | 'upload' */
  const [tab, setTab] = useState('paste');
  const [file, setFile] = useState(null);
  const [dragOver, setDragOver] = useState(false);
  const fileRef = React.useRef(null);
  /* Teste de replicação */
  const [testText, setTestText] = useState('');
  const [testResult, setTestResult] = useState(null);
  const [testBusy, setTestBusy] = useState(false);
  const [previewModel, setPreviewModel] = useState('deepseek/deepseek-v4-flash');
  const [previewTema, setPreviewTema] = useState('');
  const [showAdvanced, setShowAdvanced] = useState(false);

  const MODEL_OPTIONS = [
    { id: 'deepseek/deepseek-v4-flash', label: 'Rápido (padrão)' },
    { id: 'deepseek/deepseek-chat', label: 'Avançado' },
    { id: 'openai/gpt-4o-mini', label: 'Econômico' },
    { id: 'openai/gpt-4o', label: 'Potente' },
    { id: 'anthropic/claude-3.5-sonnet', label: 'Criativo' },
    { id: 'anthropic/claude-3.5-haiku', label: 'Leve' },
    { id: 'google/gemini-2.5-flash', label: 'Versátil' },
    { id: 'google/gemini-2.5-pro', label: 'Premium' },
    { id: 'meta-llama/llama-3.3-70b-instruct', label: 'Aberto' },
    { id: 'mistralai/mistral-large', label: 'Multilíngue' }
  ];

  const refresh = async () => {
    try {
      const r = await fetch('/api/style/list', { headers: authHeaders() }).then(r => r.json());
      const backup = (!r.total && window.styleListFromBackup) ? window.styleListFromBackup() : null;
      setList(backup || r);
      if (onStyleChanged) onStyleChanged();
      const p = await fetch('/api/style/profile', { headers: authHeaders() }).then(r => r.json());
      const local = window.loadStyleBackup ? window.loadStyleBackup() : null;
      setProfile((!p.profile && local?.store?.profile) ? { profile: local.store.profile, profilesByTipo: local.store.profilesByTipo || {}, updated: local.store.updated || local.savedAt, total: local.store.samples?.length || 0, brief: local.brief } : p);
    } catch {}
  };
  useEffect(() => { refresh(); }, []);

  const onAdd = async () => {
    if (!texto.trim() || texto.trim().length < 200) {
      showToast('Cole pelo menos 200 caracteres da sua peça.', false);
      return;
    }
    setBusy(true);
    try {
      const r = await fetch('/api/style/sample', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ titulo, tipo, texto })
      }).then(r => r.json());
      if (r.ok) {
        if (r.client_store && window.saveStyleBackup) window.saveStyleBackup(r);
        showToast(r.volatile ? 'Estilo salvo no navegador deste login.' : 'Amostra registrada. Estilo atualizado.');
        setTitulo(''); setTexto('');
        refresh();
      } else {
        showToast(r.erro || 'Falha ao registrar.', false);
      }
    } catch (e) { showToast('Erro de rede.', false); }
    setBusy(false);
  };

  const onUpload = async () => {
    if (!file) { showToast('Selecione um arquivo DOCX ou PDF.', false); return; }
    setBusy(true);
    try {
      const fd = new FormData();
      fd.append('file', file);
      fd.append('titulo', titulo || file.name.replace(/\.[^.]+$/, ''));
      fd.append('tipo', tipo);
      const r = await fetch('/api/style/upload', { method: 'POST', headers: authHeaders(), body: fd }).then(r => r.json());
      if (r.ok) {
        if (r.client_store && window.saveStyleBackup) window.saveStyleBackup(r);
        showToast(`Arquivo processado — ${r.chars_extraidos?.toLocaleString()} caracteres aprendidos.`);
        setFile(null); setTitulo('');
        refresh();
      } else {
        showToast(r.erro || 'Falha ao processar arquivo.', false);
      }
    } catch (e) { showToast('Erro de rede.', false); }
    setBusy(false);
  };

  const onFileDrop = (e) => {
    e.preventDefault(); setDragOver(false);
    const f = e.dataTransfer?.files?.[0];
    if (f) setFile(f);
  };

  const onDel = async (id) => {
    if (!confirm('Remover esta amostra?')) return;
    await fetch(`/api/style/sample/${id}`, { method: 'DELETE', headers: authHeaders() });
    refresh();
  };

  const onClear = async () => {
    if (!confirm('Apagar TODAS as amostras de estilo?')) return;
    await fetch('/api/style/all', { method: 'DELETE', headers: authHeaders() });
    showToast('Estilo zerado.');
    refresh();
  };

  /* Teste de replicação: avalia um texto colado vs perfil aprendido */
  const onTestText = async () => {
    if (!testText || testText.trim().length < 100) {
      showToast('Cole pelo menos 100 caracteres do texto a testar.', false);
      return;
    }
    setTestBusy(true); setTestResult(null);
    try {
      const r = await fetch('/api/style/test', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ texto: testText })
      }).then(r => r.json());
      if (!r.ok) { showToast(r.erro || 'Falha ao testar.', false); }
      else setTestResult({ kind: 'test', ...r });
    } catch { showToast('Erro de rede.', false); }
    setTestBusy(false);
  };

  /* Preview com IA: gera parágrafo no estilo aprendido e auto-avalia */
  const onPreviewWithAI = async () => {
    setTestBusy(true); setTestResult(null);
    try {
      const r = await fetch('/api/style/preview', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify({ model: previewModel, tema: previewTema, tipo })
      }).then(r => r.json());
      if (!r.ok) { showToast(r.erro || 'Falha ao gerar amostra.', false); }
      else setTestResult({ kind: 'preview', ...r });
    } catch { showToast('Erro de rede ou modelo indisponível.', false); }
    setTestBusy(false);
  };

  /* styles helpers */
  const tabBtn = (active) => ({
    flex: 1, padding: '8px 0', fontSize: 12, fontWeight: 600,
    borderRadius: 7, border: 'none', cursor: 'pointer',
    background: active ? 'var(--accent)' : 'transparent',
    color: active ? '#fff' : 'var(--soft)',
    transition: 'background .15s, color .15s'
  });

  return (
    <div className="ob-overlay" onClick={onClose}>
      <div className="ob-card" onClick={e => e.stopPropagation()} style={{ maxWidth: 820, maxHeight: '92vh', overflowY: 'auto' }}>
        <button className="ob-close" onClick={onClose}><I.x size={14} sw={2}/></button>
        <div className="ob-body" style={{ textAlign: 'left', padding: '28px 32px 20px' }}>
          
          {/* ========== SEÇÃO 1: TIMBRE E IDENTIDADE VISUAL ========== */}
          <section style={{ marginBottom: 32 }}>
            <div className="ob-eyebrow" style={{ marginBottom: 4 }}>🎨 TIMBRE & IDENTIDADE VISUAL</div>
            <h2 className="ob-title" style={{ textAlign: 'left', marginBottom: 10 }}>Envie seu .docx timbrado</h2>
            <p style={{ color: 'var(--muted)', fontSize: 13, marginBottom: 16, lineHeight: 1.6 }}>
              Faça upload da peça-modelo do seu escritório. O sistema extrai cabeçalho, rodapé, logo, 
              fontes, margens e tema de cores — pronto para aplicar em todas as peças geradas.
            </p>
            <BrandingTemplateSection showToast={showToast} onChanged={onTemplateChanged} />
          </section>

          {/* Separador visual */}
          <div style={{ height: 1, background: 'linear-gradient(90deg, transparent, var(--border), transparent)', margin: '28px 0' }} />

          {/* ========== SEÇÃO 2: APRENDIZADO DE ESTILO ========== */}
          <section>
            <div className="ob-eyebrow" style={{ marginBottom: 4 }}>✍️ APRENDIZADO DE ESTILO</div>
            <h2 className="ob-title" style={{ textAlign: 'left', marginBottom: 6 }}>IA aprende seu tom de escrita</h2>
            <p style={{ color: 'var(--muted)', fontSize: 13, marginBottom: 16, lineHeight: 1.6 }}>
              Adicione suas peças (texto ou arquivo). O sistema extrai padrões 
              (frase, latinismos, conectivos, estrutura) e replica seu tom nas peças geradas.
            </p>

            {/* Tab toggle */}
            <div style={{ display: 'flex', gap: 4, padding: 4, background: 'var(--surface)', borderRadius: 10, marginBottom: 16, border: '1px solid var(--border-2)' }}>
              <button style={tabBtn(tab === 'paste')} onClick={() => setTab('paste')}>
                ✏️ Colar texto
              </button>
              <button style={tabBtn(tab === 'upload')} onClick={() => setTab('upload')}>
                📎 Enviar arquivo (DOCX / PDF)
              </button>
            </div>

            {/* Campos comuns: Título + Tipo */}
            <div style={{ display: 'flex', gap: 10, marginBottom: 12 }}>
              <input
                value={titulo}
                onChange={e => setTitulo(e.target.value)}
                placeholder={tab === 'upload' ? 'Título (auto-preenchido pelo nome do arquivo)' : 'Título (ex.: Contestação Banco X)'}
                style={{ flex: 1, padding: '10px 12px', border: '1px solid var(--border-2)', borderRadius: 8, background: 'var(--bg-elev)', color: 'var(--ink)', fontSize: 13, fontWeight: 500 }}
              />
              <select value={tipo} onChange={e => setTipo(e.target.value)}
                style={{ width: 160, padding: '10px 12px', border: '1px solid var(--border-2)', borderRadius: 8, background: 'var(--bg-elev)', color: 'var(--ink)', fontSize: 13, fontWeight: 500 }}>
                <option value="peça">Peça (geral)</option>
                <option value="contestação">Contestação</option>
                <option value="apelação">Apelação</option>
                <option value="recurso">Recurso</option>
                <option value="parecer">Parecer</option>
                <option value="petição">Petição inicial</option>
                <option value="embargos">Embargos</option>
              </select>
            </div>

            {/* ---- TAB: COLAR TEXTO ---- */}
            {tab === 'paste' && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                <textarea
                  value={texto}
                  onChange={e => setTexto(e.target.value)}
                  placeholder="Cole aqui o texto integral de uma peça sua (mínimo 200 caracteres). Não envie nada confidencial sem anonimizar."
                  rows={8}
                  style={{ padding: 12, border: '1px solid var(--border-2)', borderRadius: 8, background: 'var(--bg-elev)', color: 'var(--ink)', fontFamily: 'inherit', fontSize: 13, resize: 'vertical' }}
                />
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                  <span style={{ fontSize: 11, color: 'var(--soft)', fontWeight: 500 }}>{texto.length} caracteres</span>
                  <button className="btn-primary" onClick={onAdd} disabled={busy || texto.trim().length < 200}>
                    <I.check size={12} sw={2.2}/> {busy ? 'Processando…' : 'Aprender este estilo'}
                  </button>
                </div>
              </div>
            )}

            {/* ---- TAB: ENVIAR ARQUIVO ---- */}
            {tab === 'upload' && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {/* Drop zone */}
                <div
                  onClick={() => fileRef.current?.click()}
                  onDragOver={e => { e.preventDefault(); setDragOver(true); }}
                  onDragLeave={() => setDragOver(false)}
                  onDrop={onFileDrop}
                  style={{
                    border: `2px dashed ${dragOver ? 'var(--accent)' : file ? 'var(--success)' : 'var(--border-2)'}`,
                    borderRadius: 12,
                    padding: '28px 20px',
                    textAlign: 'center',
                    cursor: 'pointer',
                    background: dragOver ? 'color-mix(in srgb, var(--accent) 6%, transparent)' : file ? 'color-mix(in srgb, var(--success) 5%, transparent)' : 'var(--surface)',
                    transition: 'all .15s'
                  }}>
                  <input ref={fileRef} type="file" accept=".docx,.doc,.pdf,.txt" style={{ display: 'none' }}
                    onChange={e => { const f = e.target.files?.[0]; if (f) setFile(f); }} />
                  {file ? (
                    <div>
                      <div style={{ fontSize: 28, marginBottom: 6 }}>📄</div>
                      <div style={{ fontWeight: 700, color: 'var(--ink)', fontSize: 13 }}>{file.name}</div>
                      <div style={{ fontSize: 11, color: 'var(--soft)', marginTop: 4 }}>{(file.size / 1024).toFixed(0)} KB — clique para trocar</div>
                    </div>
                  ) : (
                    <div>
                      <div style={{ fontSize: 32, marginBottom: 8 }}>📎</div>
                      <div style={{ fontWeight: 600, color: 'var(--ink)', fontSize: 13 }}>Arraste ou clique para selecionar</div>
                      <div style={{ fontSize: 11, color: 'var(--soft)', marginTop: 4 }}>DOCX, PDF ou TXT — máx. 20 MB</div>
                    </div>
                  )}
                </div>
                <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
                  <button className="btn-primary" onClick={onUpload} disabled={busy || !file}>
                    <I.check size={12} sw={2.2}/> {busy ? 'Extraindo texto…' : 'Aprender com este arquivo'}
                  </button>
                </div>
              </div>
            )}

            {/* Lista de amostras */}
            {list.samples?.length > 0 && (
              <div style={{ marginTop: 16, paddingTop: 14, borderTop: '1px solid var(--border-2)' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
                  <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8 }}>
                    Amostras registradas ({list.total})
                  </div>
                  <button className="btn-ghost" onClick={onClear} style={{ fontSize: 11 }}>Apagar todas</button>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 200, overflowY: 'auto' }}>
                  {list.samples.map(s => (
                    <div key={s.id} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', background: 'linear-gradient(135deg, var(--surface) 0%, var(--bg-elev) 100%)', borderRadius: 8, border: '1px solid var(--border-2)', fontSize: 12 }}>
                      <span style={{ flex: 1 }}>
                        <div style={{ fontWeight: 600, color: 'var(--ink)' }}>{s.titulo}</div>
                        <div style={{ color: 'var(--soft)', fontSize: 11, marginTop: 2 }}>{s.tipo} · {s.chars} chars</div>
                      </span>
                      <button className="icon-btn" onClick={() => onDel(s.id)} title="Remover" style={{ flexShrink: 0 }}>
                        <I.trash size={12}/>
                      </button>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {profile?.profile && (() => {
              const p = profile.profile;
              const sumValues = (o) => Object.values(o || {}).reduce((a,b)=>a+b,0);
              const topEntries = (o, n=6) => Object.entries(o || {}).sort((a,b)=>b[1]-a[1]).slice(0, n);
              const tipoEntries = Object.entries(profile.profilesByTipo || {});
              const fleschBand = p.flesch >= 70 ? 'Muito fácil' : p.flesch >= 60 ? 'Fácil' : p.flesch >= 50 ? 'Médio' : p.flesch >= 30 ? 'Difícil' : 'Muito difícil';
              const ttrBand = p.ttr >= 0.55 ? 'Alta' : p.ttr >= 0.40 ? 'Média' : 'Baixa';
              const isLegacy = p.ttr === 0 && p.flesch === 0 && p.samples_n > 0;

              const Stat = ({ label, value, sub }) => (
                <div style={{ padding: '10px 12px', background: 'var(--bg-elev)', borderRadius: 8, border: '1px solid var(--border-2)' }}>
                  <div style={{ fontSize: 9.5, color: 'var(--soft)', textTransform: 'uppercase', letterSpacing: 0.6, fontWeight: 700 }}>{label}</div>
                  <div style={{ fontWeight: 700, fontSize: 14, color: 'var(--ink)', marginTop: 2 }}>{value}</div>
                  {sub && <div style={{ fontSize: 10, color: 'var(--soft)', marginTop: 2 }}>{sub}</div>}
                </div>
              );
              const Chip = ({ label, count }) => (
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, padding: '3px 8px', background: 'color-mix(in srgb, var(--accent) 8%, transparent)', border: '1px solid color-mix(in srgb, var(--accent) 25%, transparent)', borderRadius: 100, fontSize: 11, fontWeight: 500, color: 'var(--ink)' }}>
                  {label}
                  <span style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 600 }}>×{count}</span>
                </span>
              );

              return (
                <div style={{ marginTop: 16, padding: 16, background: 'linear-gradient(135deg, var(--surface) 0%, var(--bg-elev) 100%)', borderRadius: 12, border: '1px solid var(--border-2)' }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
                    <div style={{ fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', fontSize: 10, letterSpacing: 0.8 }}>📊 Fingerprint detalhado · n={p.samples_n}</div>
                    <button className="btn-ghost" onClick={() => setShowAdvanced(s => !s)} style={{ fontSize: 11 }}>
                      {showAdvanced ? 'Ocultar avançado' : 'Mostrar avançado'}
                    </button>
                  </div>

                  {isLegacy && (
                    <div style={{ marginBottom: 12, padding: '8px 10px', background: 'color-mix(in srgb, #e0a800 10%, transparent)', border: '1px solid color-mix(in srgb, #e0a800 30%, transparent)', borderRadius: 7, fontSize: 11.5, color: 'var(--ink)', lineHeight: 1.5 }}>
                      ⚠️ Amostras antigas detectadas. <strong>Reenvie</strong> para gerar métricas completas (TTR, Flesch, bigramas, perfis por tipo).
                    </div>
                  )}

                  {/* Métricas principais */}
                  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8, marginBottom: 12 }}>
                    <Stat label="Frase média" value={`${p.avgSentenceLen} palavras`} />
                    <Stat label="Parágrafo" value={`${p.avgParagraphLen} chars`} />
                    <Stat label="Palavra média" value={`${p.avgWordLen} letras`} />
                    <Stat label="TTR (riqueza)" value={p.ttr} sub={ttrBand} />
                    <Stat label="Flesch PT-BR" value={p.flesch} sub={fleschBand} />
                    <Stat label="Latinismos" value={sumValues(p.latinismos)} />
                    <Stat label="Súmulas" value={p.sumulas} />
                    <Stat label="Artigos" value={p.artigos} />
                  </div>

                  {/* Latinismos / Conectivos / Deferências (chips) */}
                  {topEntries(p.latinismos).length > 0 && (
                    <div style={{ marginBottom: 10 }}>
                      <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Latinismos recorrentes</div>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
                        {topEntries(p.latinismos, 8).map(([k, v]) => <Chip key={k} label={k} count={v} />)}
                      </div>
                    </div>
                  )}
                  {topEntries(p.conectivos).length > 0 && (
                    <div style={{ marginBottom: 10 }}>
                      <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Conectivos prediletos</div>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
                        {topEntries(p.conectivos, 10).map(([k, v]) => <Chip key={k} label={k} count={v} />)}
                      </div>
                    </div>
                  )}
                  {topEntries(p.deferencias).length > 0 && (
                    <div style={{ marginBottom: 10 }}>
                      <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Fórmulas de deferência</div>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
                        {topEntries(p.deferencias, 6).map(([k, v]) => <Chip key={k} label={k} count={v} />)}
                      </div>
                    </div>
                  )}

                  {showAdvanced && (
                    <div style={{ marginTop: 8, paddingTop: 12, borderTop: '1px dashed var(--border-2)' }}>
                      {(p.topBigramsArr || []).length > 0 && (
                        <div style={{ marginBottom: 10 }}>
                          <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Colocações típicas (bigramas)</div>
                          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
                            {(p.topBigramsArr || []).slice(0, 12).map(b => <Chip key={b.w} label={b.w} count={b.c} />)}
                          </div>
                        </div>
                      )}
                      {(p.topWordsArr || []).length > 0 && (
                        <div style={{ marginBottom: 10 }}>
                          <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Vocabulário recorrente</div>
                          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
                            {(p.topWordsArr || []).slice(0, 18).map(w => <Chip key={w.w} label={w.w} count={w.c} />)}
                          </div>
                        </div>
                      )}
                      {Object.keys(p.tribunais || {}).length > 0 && (
                        <div style={{ marginBottom: 10 }}>
                          <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Tribunais citados</div>
                          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
                            {topEntries(p.tribunais, 8).map(([k, v]) => <Chip key={k} label={k} count={v} />)}
                          </div>
                        </div>
                      )}
                      <div style={{ marginBottom: 10 }}>
                        <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Pontuação (totais)</div>
                        <div style={{ fontSize: 11, color: 'var(--ink)' }}>
                          ; {p.punct?.semicolons || 0} · — {p.punct?.dashes || 0} · ( {p.punct?.parens || 0} · : {p.punct?.colons || 0}
                        </div>
                      </div>
                      {tipoEntries.length > 1 && (
                        <div>
                          <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>Perfis por tipo de peça</div>
                          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                            {tipoEntries.map(([k, tp]) => (
                              <div key={k} style={{ fontSize: 11, padding: '4px 8px', background: 'var(--bg-elev)', borderRadius: 6, border: '1px solid var(--border-2)' }}>
                                <strong style={{ color: 'var(--ink)' }}>{k}</strong>
                                <span style={{ color: 'var(--soft)', marginLeft: 8 }}>
                                  n={tp.samples_n} · frase {tp.avgSentenceLen}p · TTR {tp.ttr} · Flesch {tp.flesch}
                                </span>
                              </div>
                            ))}
                          </div>
                        </div>
                      )}
                    </div>
                  )}
                </div>
              );
            })()}

            {/* ---- TESTE DE REPLICAÇÃO ---- */}
            {profile?.profile && (
              <div style={{ marginTop: 18, padding: 16, background: 'var(--bg-elev)', borderRadius: 12, border: '1px solid var(--border-2)' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                  <span style={{ fontSize: 16 }}>🧪</span>
                  <div style={{ fontWeight: 700, color: 'var(--ink)', fontSize: 14 }}>Testar replicação do estilo</div>
                </div>
                <p style={{ fontSize: 12, color: 'var(--muted)', lineHeight: 1.6, marginBottom: 12 }}>
                  Cole um texto gerado e veja o quão próximo ele está do seu estilo (0-100), ou peça à IA para gerar uma amostra automaticamente.
                </p>

                {/* Seleção de modelo */}
                <div style={{ display: 'flex', gap: 8, marginBottom: 10, flexWrap: 'wrap' }}>
                  <select value={previewModel} onChange={e => setPreviewModel(e.target.value)}
                    style={{ flex: '1 1 220px', padding: '8px 10px', border: '1px solid var(--border-2)', borderRadius: 7, background: 'var(--surface)', color: 'var(--ink)', fontSize: 12 }}>
                    {MODEL_OPTIONS.map(m => <option key={m.id} value={m.id}>{m.label}</option>)}
                  </select>
                  <input value={previewTema} onChange={e => setPreviewTema(e.target.value)}
                    placeholder="Tema do teste (opcional)"
                    style={{ flex: '2 1 280px', padding: '8px 10px', border: '1px solid var(--border-2)', borderRadius: 7, background: 'var(--surface)', color: 'var(--ink)', fontSize: 12 }} />
                </div>

                <textarea value={testText} onChange={e => setTestText(e.target.value)}
                  placeholder="Cole aqui um texto gerado para avaliar similaridade com seu estilo (mín. 100 chars). Ou clique em 'Gerar amostra com IA' abaixo."
                  rows={5}
                  style={{ width: '100%', padding: 10, border: '1px solid var(--border-2)', borderRadius: 7, background: 'var(--surface)', color: 'var(--ink)', fontSize: 12, fontFamily: 'inherit', resize: 'vertical', boxSizing: 'border-box' }} />

                <div style={{ display: 'flex', gap: 8, marginTop: 10, justifyContent: 'flex-end' }}>
                  <button className="btn-ghost" onClick={onTestText} disabled={testBusy || testText.trim().length < 100} style={{ fontSize: 12 }}>
                    {testBusy ? 'Avaliando…' : 'Avaliar texto colado'}
                  </button>
                  <button className="btn-primary" onClick={onPreviewWithAI} disabled={testBusy} style={{ fontSize: 12 }}>
                    {testBusy ? 'Gerando…' : '✨ Gerar amostra com IA'}
                  </button>
                </div>

                {/* Resultado */}
                {testResult && (() => {
                  const ev = testResult.evaluation || testResult;
                  if (ev.error || ev.erro) return <div style={{ marginTop: 12, padding: 10, color: 'var(--danger)', fontSize: 12 }}>{ev.error || ev.erro}</div>;
                  const score = ev.score || 0;
                  const scoreColor = score >= 75 ? 'var(--success)' : score >= 55 ? '#e0a800' : 'var(--danger)';
                  const scoreLabel = score >= 75 ? 'Excelente replicação' : score >= 55 ? 'Replicação parcial' : 'Replicação fraca';
                  return (
                    <div style={{ marginTop: 14, padding: 14, background: 'linear-gradient(135deg, var(--surface) 0%, var(--bg-elev) 100%)', borderRadius: 10, border: '1px solid var(--border-2)' }}>
                      {/* Texto gerado pelo preview */}
                      {testResult.kind === 'preview' && testResult.generated && (
                        <div style={{ marginBottom: 14, padding: 10, background: 'var(--surface)', borderLeft: '3px solid var(--accent)', borderRadius: 6, fontSize: 12, lineHeight: 1.7, color: 'var(--ink)', maxHeight: 220, overflowY: 'auto' }}>
                          <div style={{ fontSize: 9.5, color: 'var(--soft)', textTransform: 'uppercase', fontWeight: 700, letterSpacing: 0.6, marginBottom: 6 }}>Texto gerado pela IA</div>
                          {testResult.generated}
                        </div>
                      )}

                      {/* Score grande */}
                      <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 14 }}>
                        <div style={{ width: 84, height: 84, borderRadius: '50%', background: `conic-gradient(${scoreColor} ${score * 3.6}deg, var(--border-2) 0)`, display: 'grid', placeItems: 'center', flexShrink: 0 }}>
                          <div style={{ width: 68, height: 68, borderRadius: '50%', background: 'var(--bg-elev)', display: 'grid', placeItems: 'center' }}>
                            <div style={{ fontSize: 22, fontWeight: 800, color: scoreColor }}>{Math.round(score)}</div>
                          </div>
                        </div>
                        <div style={{ flex: 1 }}>
                          <div style={{ fontWeight: 700, color: 'var(--ink)', fontSize: 14 }}>{scoreLabel}</div>
                          <div style={{ fontSize: 11.5, color: 'var(--muted)', marginTop: 4, lineHeight: 1.6 }}>
                            Cosseno léxico: <strong>{ev.cosine}</strong> ·
                            Frase gerada: <strong>{ev.metricas_geradas?.avgSentenceLen}p</strong> (alvo {profile.profile.avgSentenceLen}p) ·
                            TTR: <strong>{ev.metricas_geradas?.ttr}</strong>
                          </div>
                        </div>
                      </div>

                      {/* Breakdown por eixo */}
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
                        {Object.entries(ev.eixos || {}).map(([eixo, val]) => {
                          const pct = Math.max(0, Math.min(100, val));
                          const barColor = pct >= 75 ? 'var(--success)' : pct >= 55 ? '#e0a800' : 'var(--danger)';
                          return (
                            <div key={eixo} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 11.5 }}>
                              <div style={{ flex: '0 0 170px', color: 'var(--muted)' }}>{eixo}</div>
                              <div style={{ flex: 1, height: 6, background: 'var(--border-2)', borderRadius: 100, overflow: 'hidden' }}>
                                <div style={{ width: `${pct}%`, height: '100%', background: barColor, transition: 'width .3s' }} />
                              </div>
                              <div style={{ flex: '0 0 36px', textAlign: 'right', fontWeight: 600, color: 'var(--ink)' }}>{Math.round(pct)}</div>
                            </div>
                          );
                        })}
                      </div>

                      {/* Sugestões */}
                      {(ev.suggestions || []).length > 0 && (
                        <div style={{ marginTop: 12, padding: 10, background: 'color-mix(in srgb, var(--accent) 5%, transparent)', borderRadius: 6, border: '1px solid color-mix(in srgb, var(--accent) 20%, transparent)' }}>
                          <div style={{ fontSize: 10, color: 'var(--soft)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 6 }}>💡 Sugestões para melhorar replicação</div>
                          <ul style={{ margin: 0, paddingLeft: 18, fontSize: 11.5, lineHeight: 1.7, color: 'var(--ink)' }}>
                            {ev.suggestions.map((s, i) => <li key={i}>{s}</li>)}
                          </ul>
                        </div>
                      )}
                    </div>
                  );
                })()}
              </div>
            )}

            {!list.samples?.length && !profile?.profile && (
              <div style={{ marginTop: 12, padding: 12, background: 'linear-gradient(135deg, var(--surface) 0%, var(--bg-elev) 100%)', borderRadius: 10, border: '1px solid var(--border-2)', fontSize: 12, lineHeight: 1.6, color: 'var(--muted)' }}>
                Nenhuma amostra enviada ainda. Cole texto ou envie um arquivo DOCX/PDF de uma peça sua. O sistema aprenderá seu estilo pessoal e o replicará.
              </div>
            )}
          </section>
        </div>
      </div>
    </div>
  );
};

/* Modal de preferências de formatação (fonte, espaçamento, margens) */
const FormattingModal = ({ onClose, showToast }) => {
  const [prefs, setPrefs] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  React.useEffect(() => {
    fetch('/api/formatting', { headers: authHeaders() }).then(r => r.json()).then(j => setPrefs(j.preferences || {})).catch(() => setPrefs({}));
  }, []);

  const set = (key, val) => setPrefs(p => ({ ...p, [key]: val }));
  const setMargin = (side, val) => setPrefs(p => ({ ...p, margins: { ...p.margins, [side]: parseFloat(val) || 0 } }));

  const handleSave = async () => {
    setBusy(true);
    try {
      const r = await fetch('/api/formatting', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', ...authHeaders() },
        body: JSON.stringify(prefs),
      });
      const j = await r.json();
      if (j.ok) {
        showToast('Formatação salva. As próximas peças usarão estas configurações.');
        onClose();
      }
    } catch { showToast('Erro ao salvar.', false); }
    setBusy(false);
  };

  if (!prefs) return null;

  const inputStyle = { width: '100%', padding: '8px 10px', border: '1px solid var(--border-2)', borderRadius: 'var(--r-2)', background: 'var(--bg-elev)', color: 'var(--ink)', fontSize: 13, fontFamily: 'inherit' };
  const labelStyle = { display: 'flex', flexDirection: 'column', gap: 4 };

  return (
    <div className="ob-overlay" onClick={onClose}>
      <div className="ob-card" onClick={e => e.stopPropagation()} style={{ maxWidth: 640 }}>
        <button className="ob-close" onClick={onClose}><I.x size={14} sw={2}/></button>
        <div className="ob-body" style={{ textAlign: 'left', padding: '24px 28px 12px' }}>
          <div className="ob-eyebrow">FORMATO E TIPOGRAFIA</div>
          <h2 className="ob-title" style={{ textAlign: 'left' }}>Configurar formatação das peças</h2>
          <p style={{ color: 'var(--muted)', fontSize: 13, marginTop: 6, lineHeight: 1.55 }}>
            Defina a fonte, espaçamento, margens e outros detalhes tipográficos. As peças geradas e os downloads (Word/PDF) usarão estas configurações.
          </p>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginTop: 16 }}>
            <label style={labelStyle}>
              <span className="eyebrow">Fonte</span>
              <select value={prefs.fontFamily || 'Times New Roman'} onChange={e => set('fontFamily', e.target.value)} style={inputStyle}>
                {['Times New Roman', 'Arial', 'Calibri', 'Garamond', 'Courier New', 'Verdana', 'Georgia', 'Trebuchet MS'].map(f => <option key={f} value={f}>{f}</option>)}
              </select>
            </label>
            <label style={labelStyle}>
              <span className="eyebrow">Tamanho (pt)</span>
              <select value={prefs.fontSize || 12} onChange={e => set('fontSize', parseInt(e.target.value))} style={inputStyle}>
                {[10,11,12,13,14].map(s => <option key={s} value={s}>{s}pt</option>)}
              </select>
            </label>
            <label style={labelStyle}>
              <span className="eyebrow">Espaçamento entre linhas</span>
              <select value={prefs.lineHeight || 1.5} onChange={e => set('lineHeight', parseFloat(e.target.value))} style={inputStyle}>
                {[1.0, 1.15, 1.3, 1.5, 1.8, 2.0].map(s => <option key={s} value={s}>{s}</option>)}
              </select>
            </label>
            <label style={labelStyle}>
              <span className="eyebrow">Espaçamento após parágrafo (pt)</span>
              <select value={prefs.paragraphSpacing || 12} onChange={e => set('paragraphSpacing', parseInt(e.target.value))} style={inputStyle}>
                {[0, 6, 8, 10, 12, 14, 18, 24].map(s => <option key={s} value={s}>{s}pt</option>)}
              </select>
            </label>
            <label style={labelStyle}>
              <span className="eyebrow">Alinhamento</span>
              <select value={prefs.alignment || 'justify'} onChange={e => set('alignment', e.target.value)} style={inputStyle}>
                <option value="justify">Justificado</option>
                <option value="left">À esquerda</option>
              </select>
            </label>
            <label style={labelStyle}>
              <span className="eyebrow">Recuo de 1ª linha (cm)</span>
              <input type="number" step="0.25" min="0" max="5" value={prefs.firstLineIndent || 0} onChange={e => set('firstLineIndent', parseFloat(e.target.value) || 0)} style={inputStyle}/>
            </label>
            <label style={labelStyle}>
              <span className="eyebrow">Papel</span>
              <select value={prefs.paperSize || 'A4'} onChange={e => set('paperSize', e.target.value)} style={inputStyle}>
                <option value="A4">A4</option>
                <option value="Letter">Letter</option>
              </select>
            </label>
            <label style={labelStyle}>
              <span className="eyebrow">Endereçamento</span>
              <select value={prefs.enderecamentoStyle || 'upper'} onChange={e => set('enderecamentoStyle', e.target.value)} style={inputStyle}>
                <option value="upper">MAIÚSCULAS</option>
                <option value="title">Title Case</option>
              </select>
            </label>
          </div>
          <div style={{ marginTop: 16 }}>
            <span className="eyebrow" style={{ marginBottom: 8, display: 'block' }}>MARGENS (cm)</span>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 8 }}>
              {['top','bottom','left','right'].map(side => (
                <label key={side} style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                  <span style={{ fontSize: 11, color: 'var(--soft)' }}>{{top:'Superior',bottom:'Inferior',left:'Esquerda',right:'Direita'}[side]}</span>
                  <input type="number" step="0.5" min="0" max="5" value={prefs.margins?.[side] || 2} onChange={e => setMargin(side, e.target.value)} style={inputStyle}/>
                </label>
              ))}
            </div>
          </div>
        </div>
        <div className="ob-foot">
          <span className="eyebrow">Salvo localmente</span>
          <div className="ob-actions">
            <button className="btn-ghost" onClick={onClose}>Cancelar</button>
            <button className="btn-primary" onClick={handleSave} disabled={busy}>Salvar <I.check size={12} sw={2.2}/></button>
          </div>
        </div>
      </div>
    </div>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
