// tour.jsx — o tour de 90 segundos, guiado por cima da própria landing. // // Por que por cima da página e não um vídeo: o produto já está todo desenhado // aqui embaixo, vivo e animando. Um vídeo seria uma segunda versão do site pra // manter desatualizada. O tour apaga o resto da página, acende uma seção por // vez, explica em uma frase o que o visitante está vendo e anda sozinho — 9 // paradas de 10 segundos, 90 no total, com o botão de sair sempre à mão. const { useState: tSt, useEffect: tFx, useRef: tRef } = React; const PARADAS = [ { id: 'hero', r: 'Parada 1', t: 'O painel do vendedor', d: 'Tudo o que você vendeu num lugar só, com a comissão já calculada. É essa tela que abre quando você entra.', pontos: ['Vendas do mês', 'Comissão acumulada', 'Meta em tempo real'], }, { id: 'agitacao', r: 'Parada 2', t: 'Por que a conta nunca fecha', d: 'Não é falta de venda. É que ninguém rastreia a venda — todo mundo lança à mão, no fim do mês, de memória.', pontos: ['Vender mais não resolve', 'Planilha que ninguém assina', 'R$ 2.880/ano no escuro'], }, { id: 'diferenca', r: 'Parada 3', t: 'A planilha contra o rastro', d: 'A mesma venda dos dois lados: digitada com 2% na planilha, calculada com 3% no Comissa. R$ 1.189 de diferença numa venda só.', pontos: ['Linha 15 errada', 'Rastro com carimbo', 'R$ 1.189 de diferença'], }, { id: 'jornada', r: 'Parada 4', t: 'Como o dado anda', d: 'Clique no seu link, pagamento aprovado na plataforma, comissão resolvida. Três passos que rodam sem você tocar em nada.', pontos: ['track-click', 'webhook → sale_events', 'resolve_commission'], }, { id: 'interfaces', r: 'Parada 5', t: 'As duas pontas', d: 'Você registra de um lado, a empresa recebe do outro no segundo seguinte. Um número só, sem ninguém digitar duas vezes.', pontos: ['Registra em 3 cliques', 'Chega na empresa na hora', 'Mesmo valor dos dois lados'], }, { id: 'demo', r: 'Parada 6', t: 'Um dia inteiro no Comissa', d: 'Nova venda, comissão na hora, follow-up agendado e o time vendendo ao vivo — este é o painel de quem paga a comissão.', pontos: ['Cria produto e link', 'Ranking do time ao vivo', 'Total a pagar fechado'], }, { id: 'pos-venda', r: 'Parada 7', t: 'Depois do fechamento', d: 'Seu contrato preenchido com os dados do cliente e a comissão liberada a cada boleto que ele paga.', pontos: ['Contrato no seu modelo', '6x = 6 comissões', 'Atraso fica à vista'], }, { id: 'pricing', r: 'Parada 8', t: 'Quanto custa', d: 'Começa grátis por 14 dias, sem cartão. Cresce quando você faturar, sem fidelidade.', pontos: ['14 dias grátis', 'Sem cartão', 'Cancela em um clique'], }, { id: 'cta', r: 'Parada 9', t: 'É isso', d: 'Noventa segundos. Agora é criar a conta e lançar a primeira venda — leva menos tempo do que esse tour.', pontos: ['Configura em 2 minutos', 'Importa a planilha atual', 'Primeira venda hoje'], fim: true, }, ]; const DURACAO = 10000; function TourGuiado() { const [ativo, setAtivo] = tSt(false); const [i, setI] = tSt(0); const [pausado, setPausado] = tSt(false); const [caixa, setCaixa] = tSt(null); const [prog, setProg] = tSt(0); const inicio = tRef(0); const decorrido = tRef(0); // Registra o gatilho global uma vez. Os dois CTAs de "ver tour" e o item de // menu chamam window.iniciarTour(). tFx(() => { window.iniciarTour = () => { setI(0); setProg(0); setPausado(false); decorrido.current = 0; inicio.current = performance.now(); setAtivo(true); }; return () => { delete window.iniciarTour; }; }, []); // Leva a seção da parada pro alto da tela: o holofote começa logo abaixo da // navegação e a legenda mora embaixo, então é esse enquadramento que deixa // as duas coisas visíveis ao mesmo tempo. tFx(() => { if (!ativo) return; const el = document.getElementById(PARADAS[i].id); if (!el) return; const r = el.getBoundingClientRect(); const folga = Math.min(90, Math.max(24, (window.innerHeight * 0.62 - Math.min(r.height, window.innerHeight * 0.62)) / 2 + 24)); window.scrollTo({ top: Math.max(0, window.scrollY + r.top - folga), behavior: 'smooth' }); }, [ativo, i]); // Mede o buraco do holofote continuamente: a página segue rolando (suave) e // as seções animam por conta própria, então uma medida só ficaria pra trás. tFx(() => { if (!ativo) return; let raf = 0; const medir = () => { raf = requestAnimationFrame(medir); const el = document.getElementById(PARADAS[i].id); if (!el) return; const r = el.getBoundingClientRect(); // Seção mais alta que a tela abre um buraco do tamanho da tela inteira, e // aí o véu não escurece nada — o "holofote" some. O buraco é limitado a // 62% da altura visível: sempre sobra escuro em volta pra o olho saber // onde olhar. const teto = Math.min(window.innerHeight * 0.62, window.innerHeight - 172); const topo = Math.max(12, Math.min(r.top - 10, window.innerHeight * 0.22)); const altura = Math.max(140, Math.min(r.height + 16, teto, window.innerHeight - 172 - topo)); setCaixa({ top: topo, left: Math.max(12, r.left - 8), width: Math.min(window.innerWidth - 24, r.width + 16), height: altura, }); }; raf = requestAnimationFrame(medir); return () => cancelAnimationFrame(raf); }, [ativo, i]); // Relógio da parada. Pausa de verdade: guarda o decorrido em vez de reiniciar. tFx(() => { if (!ativo || pausado) return; inicio.current = performance.now() - decorrido.current; let raf = 0; const tick = () => { raf = requestAnimationFrame(tick); decorrido.current = performance.now() - inicio.current; const p = Math.min(1, decorrido.current / DURACAO); setProg(p); if (p >= 1) { decorrido.current = 0; if (i < PARADAS.length - 1) setI((v) => v + 1); else setAtivo(false); } }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [ativo, pausado, i]); tFx(() => { if (!ativo) return; const tecla = (e) => { if (e.key === 'Escape') setAtivo(false); if (e.key === 'ArrowRight') { decorrido.current = 0; setI((v) => Math.min(PARADAS.length - 1, v + 1)); } if (e.key === 'ArrowLeft') { decorrido.current = 0; setI((v) => Math.max(0, v - 1)); } }; window.addEventListener('keydown', tecla); return () => window.removeEventListener('keydown', tecla); }, [ativo]); if (!ativo) return null; const p = PARADAS[i]; const restante = Math.max(0, Math.round(((PARADAS.length - i) * DURACAO - decorrido.current) / 1000)); const irPara = (n) => { decorrido.current = 0; setProg(0); setI(n); }; return ( // `background: transparent` é obrigatório: o `className="cd"` está aqui só // pra herdar a tipografia do site no cartão do tour, mas a regra `.cd` // também traz `background: var(--bg)` — numa camada fixa cobrindo a // viewport inteira isso pinta a página de branco, e o furo do holofote // passa a mostrar o próprio véu em vez da seção.
{/* O holofote: uma caixa transparente com uma sombra gigante em volta. É o jeito de furar um véu escuro sem precisar de máscara SVG nem de recortar o fundo em quatro retângulos. */}
{/* A legenda. Fixa embaixo, no mesmo lugar em todas as paradas: o olho não precisa procurar onde o texto foi parar a cada troca. */}
{p.r} de {PARADAS.length} · faltam {restante}s
{p.t}

{p.d}

{p.pontos && (
{p.pontos.map((x) => ( {x} ))}
)} {PARADAS[i + 1] && (
A seguir: {PARADAS[i + 1].t}
)}
{p.fim && ( )}
{PARADAS.map((x, n) => (
); } window.TourGuiado = TourGuiado;