// Velyx Companion — chat dock com contexto, citações, ações e Claude real.

const CHAT_STORAGE = 'velyx:chat:v1';
const DOCK_STATE_KEY = 'velyx:dock:open';

// ---------- System prompt ----------
const buildSystemPrompt = (ctx) => {
  const trilhasRes = VELYX.trilhas.map(t => `- ${t.num} ${t.name}: ${t.desc}`).join('\n');
  const modulosAtuais = ctx.trilha
    ? (VELYX.modulos[ctx.trilha.id] || []).map(m => `  ${String(m.n).padStart(2,'0')} ${m.title} (${m.dur})`).join('\n')
    : '';

  return `Você é a Companion da Velyx — portal de capacitação em atendimento com IA para o mercado brasileiro.

Sua função é ajudar a aluna (Marina, gerente de CX na Lumiar Comércio) a ter sucesso durante o curso.

ESCOPO ESTRITO:
- Só responda sobre: IA em atendimento ao cliente, os conteúdos das trilhas Velyx, playbooks, prompts, arquitetura de chatbot, governança, WhatsApp Business, métricas de CX.
- Se perguntarem algo fora disso (receitas, política, entretenimento), recuse educadamente em 1 linha e redirecione: "Isso foge do escopo do curso — posso te ajudar com algo sobre atendimento com IA?"

TOM:
- Português brasileiro direto, sem "Olá!" ou "Claro!" no começo.
- Frases curtas. Sem emoji. Sem formalidade exagerada.
- Não use bullet list pra tudo — use prosa quando for melhor.
- Seja específica e opinativa, não neutra/enciclopédica.

FORMATO DE SAÍDA — OBRIGATÓRIO:
Sempre responda em JSON válido nesse shape exato, sem texto antes/depois:
{
  "answer": "resposta em markdown leve, 2-5 parágrafos curtos",
  "citations": [{"trilha":"nome","modulo":"nome"}, ...],
  "actions": [{"label":"texto curto do botão","kind":"open-modulo|open-trilha|open-biblioteca|open-feed|new-thread","payload":"id opcional"}]
}

- "citations": máximo 2. Baseie em trilhas/módulos que existem (listados abaixo). Se não tem citação honesta, retorne [].
- "actions": 1 a 3 próximos passos concretos. Exemplo: {"label":"Ver aula sobre guardrails","kind":"open-modulo","payload":"a4"}. Se não couber, [].

MODO ATUAL: ${ctx.mode}
${ctx.mode === 'tutor' ? 'Explique conceitos, responda dúvidas sobre as aulas, dê exemplos.' : ''}
${ctx.mode === 'aplicar' ? 'A aluna quer adaptar o aprendizado pro contexto de negócio dela. Puxe detalhes do negócio dela se não tiver. Responda com recomendações específicas.' : ''}
${ctx.mode === 'revisar' ? 'A aluna vai colar um prompt ou um fluxo. Aponte riscos específicos com base no que as trilhas Velyx ensinam. Seja crítica, não educada demais.' : ''}
${ctx.mode === 'resumir' ? 'Transforme conteúdo em resumo de 3-5 bullets, flashcard, ou checklist.' : ''}

CONTEXTO DA TELA:
${ctx.screenSummary}

TRILHAS DISPONÍVEIS:
${trilhasRes}

${ctx.trilha ? `MÓDULOS DA TRILHA ATUAL (${ctx.trilha.name}):\n${modulosAtuais}` : ''}

Se o input for vago, faça UMA pergunta de volta. Não invente. Se não souber, diga que não está no material.`;
};

// ---------- Context detection ----------
const getScreenContext = (route, extra = {}) => {
  if (route.name === 'modulo') {
    const trilha = VELYX.trilhas.find(t => t.id === route.trilha);
    const mods = VELYX.modulos[route.trilha] || [];
    const modulo = mods.find(m => m.id === route.modulo);
    return {
      key: `modulo:${route.trilha}:${route.modulo}`,
      title: modulo?.title || 'Aula',
      subtitle: trilha?.name,
      placeholder: `Pergunte sobre "${modulo?.title || 'essa aula'}"…`,
      screenSummary: `A aluna está vendo a aula "${modulo?.title}" (${modulo?.dur}) dentro da trilha "${trilha?.name}". Aula ${modulo?.n} de ${mods.length}.`,
      trilha, modulo,
    };
  }
  if (route.name === 'trilha') {
    const trilha = VELYX.trilhas.find(t => t.id === route.id);
    return {
      key: `trilha:${route.id}`,
      title: trilha?.name,
      subtitle: 'Trilha',
      placeholder: `Pergunte sobre a trilha "${trilha?.name}"…`,
      screenSummary: `A aluna está olhando a trilha "${trilha?.name}": ${trilha?.desc} — ${trilha?.modules} módulos.`,
      trilha,
    };
  }
  if (route.name === 'post') {
    const post = VELYX.feed[route.idx||0];
    return {
      key: `post:${route.idx}`,
      title: post?.title?.slice(0, 60),
      subtitle: 'Post',
      placeholder: 'Pergunte sobre esse post…',
      screenSummary: `A aluna está lendo o post "${post?.title}". ${post?.summary}`,
    };
  }
  if (route.name === 'biblioteca') {
    return {
      key: 'biblioteca',
      title: 'Biblioteca',
      subtitle: 'Artefatos',
      placeholder: 'Pergunte sobre prompts, templates, frameworks…',
      screenSummary: 'A aluna está na Biblioteca olhando prompts, templates e checklists.',
    };
  }
  if (route.name === 'playbooks') {
    return {
      key: 'playbooks',
      title: 'Playbooks',
      subtitle: 'Aplicação',
      placeholder: 'Pergunte sobre playbooks por setor…',
      screenSummary: 'A aluna está escolhendo um playbook por setor.',
    };
  }
  if (route.name === 'feed') {
    return {
      key: 'feed',
      title: 'Conteúdo vivo',
      subtitle: 'Feed',
      placeholder: 'Pergunte sobre o que está rolando em IA de atendimento…',
      screenSummary: 'A aluna está no feed "O que está acontecendo".',
    };
  }
  // Default
  return {
    key: 'geral',
    title: 'Estudo geral',
    subtitle: 'Velyx Companion',
    placeholder: 'Pergunte qualquer coisa sobre o curso…',
    screenSummary: 'A aluna está no dashboard.',
  };
};

// ---------- Storage ----------
const loadThreads = () => {
  try { return JSON.parse(localStorage.getItem(CHAT_STORAGE)) || {}; } catch(e) { return {}; }
};
const saveThreads = (t) => {
  try { localStorage.setItem(CHAT_STORAGE, JSON.stringify(t)); } catch(e) {}
};

// ---------- Mock completion (fallback quando window.claude não existe) ----------
const mockComplete = async ({ system, messages }) => {
  await new Promise(r => setTimeout(r, 650 + Math.random() * 700));

  const userMsg = (messages[messages.length - 1]?.content || '').toLowerCase();
  const modeMatch = system.match(/MODO ATUAL:\s*(\w+)/);
  const mode = modeMatch ? modeMatch[1] : 'tutor';

  const match = (re) => re.test(userMsg);
  const isGreeting = match(/^(oi|ola|olá|hey|e a[íi])[!. ]*$/i);
  const offTopic   = match(/receita|bolo|pol[ií]tica|futebol|filme|m[úu]sica|jogo/);
  const handoff    = match(/handoff|escal|humano|transferir/);
  const guardrail  = match(/guardrail|seguran[cç]a|risc|aluci|proteger|leak/);
  const prompt     = match(/\bprompt\b|template|few.?shot|classific/);
  const whats      = match(/whatsapp|wpp|bot[aã]o|hsm/);
  const metrics    = match(/m[ée]trica|csat|nps|tpr|tempo|kpi/);
  const checklist  = match(/checklist|resum|bullets?|3 pontos|topicos|t[óo]picos/);
  const start      = match(/come[cç]|onde (come|eu|inicio)|por onde|primeira/);
  const negocio    = match(/meu neg[óo]cio|minha empresa|meu time|e.?commerce|saas|loja|varejo/);
  const cert       = match(/certif|linkedin|diploma|prova/);

  let answer, citations = [], actions = [];

  if (offTopic) {
    answer = 'Isso foge do escopo do curso — posso te ajudar com algo sobre atendimento com IA?';
  } else if (isGreeting) {
    answer = 'Oi. No que posso ajudar agora? Dúvida de aula, revisar um prompt seu, ou decidir o que aplicar primeiro no seu contexto — qualquer um serve.';
    actions = [
      { label: 'Por onde começo', kind: 'open-trilha', payload: 'fundamentos' },
      { label: 'Abrir Biblioteca', kind: 'open-biblioteca' },
    ];
  } else if (handoff) {
    answer = 'Handoff bem-feito tem três sinais explícitos:\n\n- Cliente pede humano duas vezes em turnos diferentes.\n- A conversa passa de quatro turnos sem resolver a intenção principal.\n- O tema entra numa lista de exceções (fraude, reclamação formal, LGPD).\n\nO erro mais comum não é escalar tarde — é escalar **sem passar contexto**. O humano recebe a conversa do zero, o cliente conta tudo de novo, e a frustração dobra.';
    citations = [{ trilha: 'Fundamentos de atendimento com IA', modulo: 'Handoff: quando chamar o humano' }];
    actions = [{ label: 'Abrir aula de Handoff', kind: 'open-modulo', payload: 'm6' }];
  } else if (guardrail) {
    answer = 'Guardrail sério tem três camadas:\n\n- **Entrada**: classificar intenção antes do LLM ver a mensagem. Bloqueia injeção, sai mais barato.\n- **Saída**: filtro de PII, tom e compliance depois da geração, antes do cliente.\n- **Fallback**: rota segura quando o modelo titubeia (confiança baixa, pergunta fora do escopo).\n\nO erro clássico é bloquear tudo por medo. Guardrail não é censura — é saber exatamente o que importa barrar.';
    citations = [{ trilha: 'Arquitetura de chatbot com LLM', modulo: 'Guardrails em produção' }];
    actions = [{ label: 'Ver Guardrails em produção', kind: 'open-modulo', payload: 'a4' }];
  } else if (prompt) {
    answer = 'Depende da tarefa. Pra **classificação** (intenção, sentimento), prompt curto com 3-5 exemplos few-shot funciona melhor que definição abstrata.\n\nPra **geração** (resposta ao cliente), o contrário: regras explícitas de tom, estrutura, e uma lista negativa do que nunca dizer.\n\nTem um banco por função na Biblioteca.';
    citations = [{ trilha: 'Fundamentos de atendimento com IA', modulo: 'Prompts que funcionam em atendimento' }];
    actions = [
      { label: 'Abrir Biblioteca', kind: 'open-biblioteca' },
      { label: 'Aula sobre prompts', kind: 'open-modulo', payload: 'm4' },
    ];
  } else if (whats) {
    answer = 'WhatsApp tem duas armadilhas: (1) janela de 24h pra resposta livre, fora dela só HSM aprovado; (2) botões e listas ajudam mais do que texto solto porque reduzem fricção de digitação no mobile.\n\nA trilha de WhatsApp Business cobre isso com exemplos de fluxo real.';
    citations = [{ trilha: 'WhatsApp Business com IA', modulo: '' }];
    actions = [{ label: 'Abrir trilha WhatsApp', kind: 'open-trilha', payload: 'whatsapp' }];
  } else if (metrics) {
    answer = 'CSAT mede satisfação no momento, não qualidade de atendimento. Em atendimento com IA, eu olharia três métricas juntas: **resolução no primeiro contato**, **tempo até a primeira resposta útil** (não qualquer resposta) e **taxa de handoff desnecessário**.\n\nSó CSAT vira refém de viés — cliente bravo não responde pesquisa.';
    citations = [{ trilha: 'Fundamentos de atendimento com IA', modulo: 'Medir qualidade sem virar refém de CSAT' }];
    actions = [{ label: 'Ver aula de métricas', kind: 'open-modulo', payload: 'm7' }];
  } else if (checklist || mode === 'resumir') {
    answer = '- Defina um objetivo concreto por módulo antes de começar.\n- Anote uma pergunta por aula enquanto assiste.\n- Aplique no seu contexto antes de passar pra próxima.\n- Revise o que salvou no fim da semana.\n- Faça a prova só quando conseguir explicar em voz alta.';
  } else if (cert) {
    answer = 'Certificação sai quando você conclui os 8 módulos de Fundamentos e passa na prova final (mínimo 70%). O código é verificável por link público — dá pra colar direto como credencial no LinkedIn, aparece com selo Velyx.';
    actions = [{ label: 'Abrir Certificações', kind: 'open-feed' }];
  } else if (start) {
    answer = 'Pra quem está começando, **Fundamentos de atendimento com IA** primeiro — dá o vocabulário que as outras trilhas assumem. Leva cerca de 3h no total.\n\nSó depois iria pra Arquitetura (mais técnica) ou Playbooks (aplicação por setor). Pular Fundamentos dá economia aparente e dívida real.';
    citations = [{ trilha: 'Fundamentos de atendimento com IA', modulo: '' }];
    actions = [{ label: 'Abrir Fundamentos', kind: 'open-trilha', payload: 'fundamentos' }];
  } else if (mode === 'aplicar' || negocio) {
    answer = 'Pra ser útil preciso de três coisas do seu contexto:\n\n- Volume mensal de atendimentos e canais principais (WhatsApp, chat do site, email).\n- O problema número um que você quer atacar: tempo de resposta, qualidade, ou custo.\n- O que você já tem rodando hoje (bot simples, só humano, nada).\n\nCom isso consigo sugerir por onde atacar de verdade.';
  } else if (mode === 'revisar') {
    answer = 'Cola o prompt ou o fluxo que você quer revisar. Vou apontar os riscos específicos com base no que as trilhas ensinam — onde pode alucinar, onde vaza PII, onde o usuário consegue quebrar a persona.';
  } else {
    answer = 'Posso responder melhor se você me disser se é sobre uma aula específica que está vendo, um problema do dia a dia do seu time, ou um conceito geral que quer entender.\n\nSe preferir, escolhe uma das sugestões abaixo pra eu puxar direto.';
  }

  return JSON.stringify({ answer, citations, actions });
};

// ---------- Claude call ----------
const askClaude = async (systemPrompt, history, userMsg) => {
  const messages = [
    ...history.filter(m => m.role === 'user' || m.role === 'assistant').map(m => ({
      role: m.role,
      content: m.role === 'assistant' ? (m.raw || m.text || '') : m.text,
    })),
    { role: 'user', content: userMsg },
  ];
  const payload = { system: systemPrompt, messages };
  const hasRealClaude = typeof window !== 'undefined'
    && window.claude
    && typeof window.claude.complete === 'function';
  if (hasRealClaude) {
    try {
      return await window.claude.complete(payload);
    } catch (e) {
      console.warn('Claude real falhou, caindo no mock:', e);
    }
  }
  return mockComplete(payload);
};

const parseResponse = (raw) => {
  // Try to extract JSON from the response
  const jsonMatch = raw.match(/\{[\s\S]*\}/);
  if (jsonMatch) {
    try {
      return JSON.parse(jsonMatch[0]);
    } catch (e) {}
  }
  return { answer: raw, citations: [], actions: [] };
};

// ---------- Mini markdown ----------
const renderInline = (s) => {
  // bold **x**, italic *x*, code `x`
  const parts = [];
  let i = 0, key = 0;
  const push = (el) => parts.push(<React.Fragment key={key++}>{el}</React.Fragment>);
  const re = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g;
  let lastIndex = 0, m;
  while ((m = re.exec(s))) {
    if (m.index > lastIndex) push(s.slice(lastIndex, m.index));
    const tok = m[0];
    if (tok.startsWith('**')) push(<strong className="text-ink font-semibold">{tok.slice(2,-2)}</strong>);
    else if (tok.startsWith('`')) push(<code className="font-mono text-[12px] px-1 py-0.5 rounded bg-black/40 text-accent">{tok.slice(1,-1)}</code>);
    else push(<em>{tok.slice(1,-1)}</em>);
    lastIndex = re.lastIndex;
  }
  if (lastIndex < s.length) push(s.slice(lastIndex));
  return parts;
};

const Markdown = ({ text }) => {
  const blocks = (text || '').split(/\n\n+/);
  return (
    <div className="space-y-2.5 text-[13.5px] leading-[1.65] text-ink/90">
      {blocks.map((b, i) => {
        if (b.trim().startsWith('- ') || b.trim().startsWith('* ')) {
          const items = b.split(/\n/).map(l => l.replace(/^[-*]\s/, ''));
          return (
            <ul key={i} className="space-y-1.5 pl-1">
              {items.map((it, j) => (
                <li key={j} className="flex gap-2">
                  <span className="text-mute2 pt-[3px]">•</span>
                  <span>{renderInline(it)}</span>
                </li>
              ))}
            </ul>
          );
        }
        return <p key={i}>{renderInline(b)}</p>;
      })}
    </div>
  );
};

// ---------- Main component ----------
const CompanionDock = ({ route, go, open, setOpen }) => {
  const ctx = React.useMemo(() => getScreenContext(route), [route.name, route.id, route.trilha, route.modulo, route.idx]);
  const [allThreads, setAllThreads] = React.useState(loadThreads);
  const [mode, setMode] = React.useState('tutor');
  const [input, setInput] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState(null);
  const scrollRef = React.useRef(null);

  const thread = allThreads[ctx.key] || [];

  const updateThread = (next) => {
    const updated = { ...allThreads, [ctx.key]: next };
    setAllThreads(updated);
    saveThreads(updated);
  };

  const clearThread = () => {
    updateThread([]);
  };

  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => {
      if (e.key === 'Escape') setOpen(false);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open]);

  React.useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [thread.length, loading]);

  const suggestions = React.useMemo(() => {
    if (ctx.modulo) return [
      `Me explica "${ctx.modulo.title}" de outro jeito`,
      'Aplicar isso ao meu negócio',
      'Gerar checklist dessa aula',
      'Quais os riscos mais comuns aqui?',
    ];
    if (ctx.trilha) return [
      `Por onde começar na trilha "${ctx.trilha.name}"?`,
      'Essa trilha é pra mim?',
      'Quanto tempo vou levar pra terminar?',
    ];
    if (route.name === 'biblioteca') return [
      'Qual prompt usar pra classificar intenção?',
      'Me ajuda a escolher um template de WhatsApp',
      'Qual a diferença entre framework e playbook?',
    ];
    if (route.name === 'playbooks') return [
      'Qual playbook faz sentido pra e-commerce D2C?',
      'E-commerce ou SaaS pro meu caso?',
      'Quais prompts são comuns entre setores?',
    ];
    if (route.name === 'feed' || route.name === 'post') return [
      'Resume isso em 3 bullets',
      'Como isso muda o que eu aprendi na trilha?',
      'Tem aula relacionada?',
    ];
    if (route.name === 'cert') return [
      'O que falta pra eu emitir a certificação?',
      'Como apresentar a cert no LinkedIn?',
    ];
    return [
      'Por onde começo?',
      'Me recomenda uma trilha pro meu negócio',
      'Qual a diferença entre IA e automação tradicional?',
      'Como monto um chatbot básico em 1 semana?',
    ];
  }, [ctx.key]);

  const submit = async (text) => {
    const msg = (text || input).trim();
    if (!msg || loading) return;
    setInput('');
    setError(null);
    const userMsg = { role: 'user', text: msg, t: Date.now() };
    const withUser = [...thread, userMsg];
    updateThread(withUser);

    setLoading(true);
    try {
      const systemPrompt = buildSystemPrompt({ ...ctx, mode });
      const raw = await askClaude(systemPrompt, thread, msg);
      const parsed = parseResponse(raw);
      const botMsg = {
        role: 'assistant',
        text: parsed.answer,
        citations: parsed.citations || [],
        actions: parsed.actions || [],
        raw,
        t: Date.now(),
      };
      updateThread([...withUser, botMsg]);
    } catch (e) {
      setError('Não consegui responder agora. Tenta de novo em instantes.');
      updateThread([...withUser, { role: 'assistant', text: 'Não consegui responder agora. Tenta de novo em instantes.', error: true, t: Date.now() }]);
    } finally {
      setLoading(false);
    }
  };

  const runAction = (a) => {
    if (a.kind === 'open-modulo' && a.payload) {
      // Try to find it
      for (const [tId, mods] of Object.entries(VELYX.modulos)) {
        const m = mods.find(x => x.id === a.payload);
        if (m) { go({ name: 'modulo', trilha: tId, modulo: m.id }); return; }
      }
    }
    if (a.kind === 'open-trilha' && a.payload) go({ name: 'trilha', id: a.payload });
    if (a.kind === 'open-biblioteca') go({ name: 'biblioteca' });
    if (a.kind === 'open-feed') go({ name: 'feed' });
    if (a.kind === 'new-thread') clearThread();
  };

  // Floating launcher button (always visible, bottom-right)
  const Launcher = (
    <button
      onClick={() => setOpen(true)}
      title="Abrir Companion (⌘J)"
      className="fixed z-40 bottom-6 right-6 h-11 pl-3 pr-4 rounded-full bg-accent text-black flex items-center gap-2 shadow-[0_10px_30px_-10px_rgba(216,255,60,0.6)] hover:scale-[1.03] t-fast">
      <I.spark size={15}/>
      <span className="text-[12.5px] font-semibold tracking-tight">Companion</span>
      <span className="font-mono text-[10px] text-black/55 border border-black/15 rounded px-1 py-[1px] ml-1">⌘J</span>
    </button>
  );

  if (!open) return Launcher;

  return (
    <>
    {/* Backdrop — subtle, doesn't dim content hard */}
    <div onClick={() => setOpen(false)}
         className="fixed inset-0 z-40 bg-black/30 backdrop-blur-[2px] fade-in"/>
    <aside className="fixed z-50 top-4 right-4 bottom-4 w-[400px] rounded-[14px] border border-line2 bg-panel flex flex-col shadow-[0_30px_80px_-20px_rgba(0,0,0,0.7)] slide-in">
      {/* Header */}
      <div className="px-4 pt-4 pb-3 border-b border-line">
        <div className="flex items-center gap-2 mb-2">
          <div className="w-6 h-6 rounded-[5px] bg-accent text-black flex items-center justify-center">
            <I.spark size={13}/>
          </div>
          <span className="text-ink text-[13px] font-semibold tracking-tightest">Companion</span>
          <button onClick={clearThread}
            title="Limpar conversa"
            className="ml-auto text-mute2 hover:text-ink t-fast text-[11px] font-mono uppercase tracking-wide">
            Limpar
          </button>
          <button onClick={() => setOpen(false)}
            title="Fechar (Esc)"
            className="w-6 h-6 rounded-[5px] hover:bg-white/[0.06] flex items-center justify-center text-mute2 hover:text-ink t-fast">
            <svg width="11" height="11" viewBox="0 0 10 10" fill="none"><path d="M1 1L9 9M9 1L1 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
          </button>
        </div>

        {/* Context pill */}
        <div className="flex items-center gap-2 mb-3 text-[11px] text-mute2 font-mono uppercase tracking-wide truncate">
          <span>Sobre:</span>
          <span className="text-ink/80 truncate">{ctx.title}</span>
        </div>

        {/* Mode selector */}
        <div className="flex gap-1 p-0.5 rounded-[6px] hairline bg-black/20">
          {[
            ['tutor','Tutor'],
            ['aplicar','Aplicar'],
            ['revisar','Revisar'],
            ['resumir','Resumir'],
          ].map(([v, l]) => (
            <button key={v} onClick={() => setMode(v)}
              className={`flex-1 h-7 rounded-[4px] text-[11px] t-fast
                ${mode === v ? 'bg-white/[0.08] text-ink hairline2' : 'text-mute hover:text-ink'}`}>
              {l}
            </button>
          ))}
        </div>
      </div>

      {/* Body */}
      <div ref={scrollRef} className="flex-1 overflow-y-auto px-4 py-4 space-y-5">
        {thread.length === 0 && (
          <EmptyState ctx={ctx} suggestions={suggestions} onPick={submit}/>
        )}
        {thread.map((m, i) => (
          <Message key={i} m={m} onAction={runAction}/>
        ))}
        {loading && <TypingDot/>}
        {error && <div className="text-[12px] text-red-400/80">{error}</div>}
      </div>

      {/* Footer input */}
      <div className="border-t border-line p-3">
        <div className="rounded-[8px] hairline bg-black/20 focus-within:shadow-[inset_0_0_0_1px_#D8FF3C] t-fast">
          <textarea
            value={input}
            onChange={e => setInput(e.target.value)}
            onKeyDown={e => {
              if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                submit();
              }
            }}
            placeholder={ctx.placeholder}
            rows={1}
            className="w-full bg-transparent text-ink text-[13px] placeholder:text-mute2 resize-none focus:outline-none px-3 py-2.5 max-h-[120px]"
            style={{ minHeight: 38 }}
          />
          <div className="flex items-center justify-between px-2 pb-2">
            <div className="text-mute2 text-[10.5px] font-mono">
              {loading ? 'pensando…' : 'Enter pra enviar · Shift+Enter nova linha'}
            </div>
            <button
              disabled={!input.trim() || loading}
              onClick={() => submit()}
              className={`h-7 px-3 rounded-[5px] text-[11.5px] font-medium t-fast
                ${input.trim() && !loading ? 'bg-accent text-black hover:bg-accent/90' : 'bg-white/[0.04] text-mute2 cursor-not-allowed'}`}>
              Enviar
            </button>
          </div>
        </div>
        <div className="flex items-center justify-between mt-2 px-1 text-[10px] text-mute2 font-mono">
          <span>Baseado no conteúdo Velyx</span>
          <span>Demo · respostas simuladas</span>
        </div>
      </div>
    </aside>
    </>
  );
};

// ---------- Subcomponents ----------
const EmptyState = ({ ctx, suggestions, onPick }) => (
  <div className="pt-2">
    <div className="mb-5">
      <h3 className="text-ink text-[15px] font-semibold tracking-tightest mb-1.5">
        Oi. No que eu te ajudo?
      </h3>
      <p className="text-mute text-[12.5px] leading-relaxed">
        Eu leio o que você está estudando. Pergunte sobre o material, peça pra aplicar ao seu caso, ou cole um prompt pra eu revisar.
      </p>
    </div>

    {/* Capability strip */}
    <div className="grid grid-cols-3 gap-1.5 mb-5">
      {[
        ['Tutor', 'explicar de novo', 'book'],
        ['Aplicar', 'ao seu caso', 'tag'],
        ['Revisar', 'seu prompt', 'checkCircle'],
      ].map(([label, sub, icon]) => {
        const Ico = I[icon];
        return (
          <div key={label} className="p-2.5 rounded-[6px] hairline bg-black/10">
            <Ico size={12} className="text-accent mb-1.5"/>
            <div className="text-ink text-[11.5px] font-medium leading-tight">{label}</div>
            <div className="text-mute2 text-[10.5px] leading-tight">{sub}</div>
          </div>
        );
      })}
    </div>

    <div className="text-mute2 text-[10px] font-mono uppercase tracking-[0.12em] mb-2">Sugestões</div>
    <div className="space-y-1.5">
      {suggestions.map((s, i) => (
        <button key={i} onClick={() => onPick(s)}
          className="w-full text-left p-2.5 rounded-[6px] hairline hover:bg-white/[0.03] hover:shadow-[inset_0_0_0_1px_#26262B] t-fast text-[12.5px] text-ink/80 hover:text-ink flex items-start gap-2 group">
          <I.arrowRight size={11} className="text-mute2 group-hover:text-accent mt-[3px] shrink-0 t-fast"/>
          <span>{s}</span>
        </button>
      ))}
    </div>
  </div>
);

const Message = ({ m, onAction }) => {
  if (m.role === 'user') {
    return (
      <div className="flex justify-end">
        <div className="max-w-[90%] rounded-[8px] bg-white/[0.05] hairline2 px-3 py-2 text-[13px] text-ink">
          {m.text}
        </div>
      </div>
    );
  }
  return (
    <div className="space-y-3">
      <Markdown text={m.text}/>
      {m.citations && m.citations.length > 0 && (
        <div className="flex flex-wrap gap-1.5 pt-1">
          {m.citations.map((c, i) => (
            <div key={i} className="inline-flex items-center gap-1.5 px-2 h-6 rounded-[4px] hairline text-[10.5px] text-mute">
              <I.doc size={10} className="text-accent"/>
              <span className="font-mono">{c.modulo || c.trilha}</span>
            </div>
          ))}
        </div>
      )}
      {m.actions && m.actions.length > 0 && (
        <div className="flex flex-wrap gap-1.5">
          {m.actions.map((a, i) => (
            <button key={i} onClick={() => onAction(a)}
              className="inline-flex items-center gap-1.5 px-2.5 h-7 rounded-[5px] hairline bg-white/[0.03] hover:bg-accent/10 hover:text-accent t-fast text-[11.5px] text-ink/90">
              {a.label} <I.arrowRight size={10}/>
            </button>
          ))}
        </div>
      )}
      {!m.error && (
        <div className="flex items-center gap-2 pt-0.5">
          <button className="text-mute2 hover:text-ink t-fast text-[11px]">👍</button>
          <button className="text-mute2 hover:text-ink t-fast text-[11px]">👎</button>
        </div>
      )}
    </div>
  );
};

const TypingDot = () => (
  <div className="flex items-center gap-1.5 text-mute2 text-[11px]">
    <span className="relative flex w-2 h-2">
      <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-accent opacity-60"/>
      <span className="relative inline-flex rounded-full w-2 h-2 bg-accent"/>
    </span>
    <span className="font-mono text-[10.5px] uppercase tracking-wider">pensando</span>
  </div>
);

Object.assign(window, { CompanionDock, DOCK_STATE_KEY });
