import React, { useState, useMemo } from "react"; // ───────────────────────────────────────────────────────────── // Diagnóstico de e-commerce — v2 (ramificado + qualificação) // Mudanças pedidas pelo Bruno: // • Caminho "não vende ainda" corrigido (perguntas próprias). // • 6 perfis, não 4 buckets. // • Camada de CAPACIDADE DE INVESTIR — filtra "acha que é barato" // e adapta o fechamento (protege as vagas / o tempo do Bruno). // • Resultado por COMBINAÇÃO (vazamento primário + secundário). // • Regra do Bruno: BASE PRIMEIRO quando há mais de um problema. // ───────────────────────────────────────────────────────────── const NAVY = "#1A2B4A", NAVY_PANEL = "#1f3255", NAVY_DEEP = "#101d33"; const EMERALD = "#27AE60", EMERALD_DK = "#1f8f4e", LABEL = "#8A93A6"; const AMBER = "#E0A93B"; // usado só no aviso honesto de expectativa const BORDER = "rgba(229,231,235,0.16)", TEXT = "#EAF0F6"; const FRENTE_MAX = { CONVERSAO: 6, BASE: 4, RECOMPRA: 3, OPERACAO: 3 }; const FRENTE_NOME = { CONVERSAO: "Conversão / Checkout", BASE: "Base / CRM", RECOMPRA: "Recompra", OPERACAO: "Operação (estoque/marketplace/ERP)", }; // Perguntas com predicado `when(a)` para ramificar de verdade. const QS = [ { id: "vende", texto: "Como está a venda da sua loja hoje?", op: [ { t: "Ainda não vendo — quero começar", v: "nao" }, { t: "Vendo pouco, ainda começando", v: "pouco" }, { t: "Vendo bem, e quero vender mais", v: "bem" }, ], }, // ── ramo INICIANTE ── { id: "ini", when: (a) => a.vende === "nao", texto: "Onde você está agora?", op: [ { t: "Já tenho loja/estrutura montada, falta vender", v: "estrut" }, { t: "Estou do zero — ideia, produto ou fornecedor", v: "zero" }, ], }, // ── ramo QUEM JÁ VENDE ── { id: "base", when: (a) => a.vende === "pouco" || a.vende === "bem", texto: "Você tem uma base de clientes ou contatos (quem já comprou ou demonstrou interesse)?", op: [ { t: "Sim, uma base considerável", v: "grande" }, { t: "Sim, mas pequena", v: "peq" }, { t: "Praticamente não tenho", v: "nao" }, ], }, { id: "trafego", when: (a) => a.vende === "pouco" || a.vende === "bem", texto: "Você investe em tráfego pago (anúncios)?", op: [ { t: "Sim — traz visita, mas a venda não acompanha", f: "CONVERSAO", p: 3 }, { t: "Sim — e converte de forma saudável", p: 0 }, { t: "Ainda não invisto", p: 0 }, { t: "Invisto, mas não sei medir se converte", f: "CONVERSAO", p: 2 }, ], }, { id: "conversao", when: (a) => a.vende === "pouco" || a.vende === "bem", texto: "Você sabe quantos visitantes viram compra — e quantos abandonam o carrinho?", op: [ { t: "Sim, acompanho de perto", p: 0 }, { t: "Sei que está baixo", f: "CONVERSAO", p: 3 }, { t: "Não faço ideia", f: "CONVERSAO", p: 2 }, ], }, { id: "base_trab", when: (a) => (a.vende === "pouco" || a.vende === "bem") && (a.base === "grande" || a.base === "peq"), texto: "Você vende ativamente pra quem já está na sua base — e-mail, CRM, ofertas pra quem já comprou?", op: [ { t: "Sim, de forma constante", p: 0 }, { t: "Tenho a base, mas quase não uso", f: "BASE", p: 3 }, { t: "Não trabalho — só busco cliente novo", f: "BASE", p: 4 }, ], }, { id: "recompra", when: (a) => a.vende === "pouco" || a.vende === "bem", texto: "Seus clientes voltam a comprar?", op: [ { t: "Voltam bastante", p: 0 }, { t: "Raramente voltam", f: "RECOMPRA", p: 3 }, { t: "Não sei / não acompanho", f: "RECOMPRA", p: 2 }, ], }, { id: "operacao", when: (a) => a.vende === "pouco" || a.vende === "bem", texto: "Sua operação te dá dor de cabeça — estoque furado, marketplace bagunçado, ERP que não conversa?", op: [ { t: "Sim, vive travando", f: "OPERACAO", p: 3 }, { t: "Um pouco", f: "OPERACAO", p: 1 }, { t: "Está sob controle", p: 0 }, ], }, // ── CAPACIDADE (todos) — o corte que qualifica ── { id: "capacidade", texto: "Pra crescer no digital, além do seu tempo você investe em três frentes: quem opera, mídia paga e ferramentas. Como isso está pra você hoje?", nota: "Não muda seu diagnóstico — ajusta o próximo passo certo pra você.", op: [ { t: "Tenho orçamento pra investir com consistência", v: "ok" }, { t: "Orçamento limitado, mas dá pra começar", v: "limitado" }, { t: "Esperava gastar pouco — achava que dava pra fazer barato", v: "barato" }, { t: "Não tenho orçamento pra investir agora", v: "sem" }, ], }, ]; export default function App() { const [tela, setTela] = useState("intro"); const [pos, setPos] = useState(0); const [a, setA] = useState({}); const [email, setEmail] = useState(""); const [enviado, setEnviado] = useState(false); const visiveis = useMemo(() => QS.filter((q) => !q.when || q.when(a)), [a]); const atual = visiveis[pos]; function responder(op) { const na = { ...a }; if (op.f) na[atual.id] = { f: op.f, p: op.p }; else if (op.v !== undefined) na[atual.id] = op.v; else na[atual.id] = { p: op.p || 0 }; // resposta "saudável" sem frente setA(na); const novasVis = QS.filter((q) => !q.when || q.when(na)); if (pos + 1 >= novasVis.length) setTela("resultado"); else setPos(pos + 1); } function voltar() { if (pos === 0) setTela("intro"); else setPos(pos - 1); } function reiniciar() { setA({}); setPos(0); setEmail(""); setEnviado(false); setTela("intro"); } return (
Bruno Cortes diagnóstico de e-commerce
{tela === "intro" && { setPos(0); setTela("perguntas"); }} />} {tela === "perguntas" && atual && ( )} {tela === "resultado" && ( email.includes("@") && setEnviado(true)} onReiniciar={reiniciar} /> )}
); } function Intro({ onStart }) { return (
DIAGNÓSTICO GRATUITO · 2 MIN

Sua loja recebe visita e não vende?

Na maioria das lojas o problema não é falta de tráfego — é o que acontece depois do clique e a base de clientes que fica esquecida enquanto se caça gente nova.

Responda algumas perguntas e veja onde provavelmente está o seu maior vazamento — e por onde começar. Sem PDF genérico.

Feito por quem operou e-commerce por dentro por 10 anos — não por quem só fala de marketing.

); } function Pergunta({ q, index, total, onResp, onVoltar }) { const pct = Math.round((index / total) * 100); return (
{index + 1} / {total}

{q.texto}

{q.nota &&

{q.nota}

}
{q.op.map((op, k) => ( ))}
); } // ── motor de diagnóstico ── function diagnosticar(a) { const scores = { CONVERSAO: 0, BASE: 0, RECOMPRA: 0, OPERACAO: 0 }; Object.values(a).forEach((r) => { if (r && typeof r === "object" && r.f) scores[r.f] += r.p; }); const temBase = a.base === "grande" || a.base === "peq"; const cap = a.capacidade; // ok | limitado | barato | sem // severidade normalizada const sev = {}; Object.keys(FRENTE_MAX).forEach((k) => { sev[k] = Math.round((scores[k] / FRENTE_MAX[k]) * 100); }); // frentes aplicáveis (quem não vende ainda não tem vazamento medido) const iniciante = a.vende === "nao"; const querMais = a.vende === "bem"; let frentes = Object.keys(FRENTE_MAX) .filter((k) => (k === "BASE" ? temBase : true)) .map((k) => ({ chave: k, nome: FRENTE_NOME[k], sev: sev[k] })); // ordena por severidade frentes.sort((x, y) => y.sev - x.sev); // REGRA DO BRUNO: base primeiro. Se há base e ela está parada (sev alta), // ela vira o primeiro passo, mesmo que outra frente pontue mais. let ordemPrioridade = [...frentes]; const baseObj = frentes.find((f) => f.chave === "BASE"); if (baseObj && baseObj.sev >= 50) { ordemPrioridade = [baseObj, ...frentes.filter((f) => f.chave !== "BASE")]; } const primario = ordemPrioridade.find((f) => f.sev > 0) || null; const secundario = ordemPrioridade.filter((f) => f.sev > 0 && f !== primario)[0] || null; return { scores, sev, frentes, primario, secundario, temBase, cap, iniciante, querMais, baseFirst: !!(baseObj && baseObj.sev >= 50 && primario && primario.chave === "BASE") }; } function Resultado({ a, email, setEmail, enviado, onEnviar, onReiniciar }) { const d = useMemo(() => diagnosticar(a), [a]); const c = montarCopy(d, a); return (
SEU DIAGNÓSTICO
{c.perfil}

{c.titulo}

{/* mapa de vazamento (some no iniciante, que não tem loja pra medir) */} {!d.iniciante && d.frentes.some((f) => f.sev > 0) && (
{d.frentes.map((f) => { const isTop = d.primario && f.chave === d.primario.chave; return (
{f.nome} {rotulo(f.sev)}
); })}
)}
{c.paragrafos.map((t, k) =>

)}

{c.aviso && (
Um aviso honesto

)} {/* fechamento adaptado à capacidade */}
{c.ctaTit}

{c.mostrarCTA && } {c.mostrarEmail && (!enviado ? (

setEmail(e.target.value)} />
) :

✓ Diagnóstico completo a caminho. (protótipo — envio simulado)

)}
); } // ── copy por combinação perfil × vazamento × capacidade ── function montarCopy(d, a) { const nomeVaz = { CONVERSAO: "conversão depois do clique", BASE: "a base parada", RECOMPRA: "a falta de recompra", OPERACAO: "a operação travada", }; const detalheVaz = { CONVERSAO: "Você paga pra trazer gente pra uma loja que não fecha. Mais tráfego só vaza mais rápido — o gargalo é a loja, a oferta e o checkout, não a falta de visita.", BASE: "Você tem gente que já comprou ou se interessou, e essa base está esquecida enquanto você caça lead novo. Vender de novo pra quem já te conhece é a venda mais barata que existe.", RECOMPRA: "Cada venda é um cliente novo e ninguém volta. Você corre na esteira — sem recompra, o tráfego nunca alcança.", OPERACAO: "Sua venda existe, mas escapa na operação: estoque furado, marketplace desorganizado, sistemas que não conversam. Você perde venda por dentro, não por falta de demanda.", }; // ---- perfil INICIANTE ---- if (d.iniciante) { const doZero = a.ini === "zero"; const c = { perfil: "PERFIL: COMEÇANDO", titulo: doZero ? "Você está do zero — a construir, não a consertar." : "Você tem estrutura, falta fazer a máquina vender.", paragrafos: [ doZero ? "Não há vazamento pra apontar porque a operação ainda não existe. A boa notícia: dá pra construir do jeito certo desde o começo, sem herdar os erros que a maioria carrega por anos." : "Você já tem loja montada — o que falta é transformá-la numa máquina de vender: tráfego que converte, base sendo construída desde a primeira venda, checkout que não derruba ninguém.", ], }; aplicarCapacidade(c, d, true); return c; } // ---- perfil QUER MAIS (vende bem) ---- if (d.querMais && !d.primario) { const c = { perfil: "PERFIL: ESCALAR", titulo: "Você já vende bem. Daqui, o ganho é de operação inteira.", paragrafos: [ "Não achei um vazamento gritante — o que é ótimo e raro. Sendo honesto: a partir daqui o ganho é incremental e exige mais operação, não menos. Não é um ajuste pontual, é escalar a máquina como um todo — mais canais, mais integração, mais estrutura.", ], }; aplicarCapacidade(c, d, false); return c; } // ---- perfis com vazamento (vende pouco/bem) ---- const p = d.primario; const c = { perfil: d.querMais ? "PERFIL: VENDE BEM, MAS VAZA" : "PERFIL: VENDENDO, COM VAZAMENTO", titulo: tituloPorFrente(p.chave), paragrafos: [detalheVaz[p.chave]], }; // base primeiro (regra do Bruno) — explica a ordem if (d.baseFirst && d.secundario) { c.paragrafos.push(`Você tem mais de um ponto aberto — mas eu começaria pela base. É o movimento mais rápido e barato: dá caixa e confiança vendendo pra quem já te conhece, antes de atacar ${nomeVaz[d.secundario.chave]}.`); } else if (d.secundario) { c.paragrafos.push(`E tem um segundo ponto puxando junto: ${nomeVaz[d.secundario.chave]}. Mas o primeiro a atacar é o de cima — resolver ele já muda seu resultado.`); } aplicarCapacidade(c, d, false); return c; } function tituloPorFrente(k) { return { CONVERSAO: "Seu maior vazamento está depois do clique.", BASE: "Você tem dinheiro parado na sua própria base.", RECOMPRA: "Você vende uma vez e perde o cliente.", OPERACAO: "Você está perdendo venda por dentro da operação.", }[k]; } // aplica o fechamento conforme capacidade de investir function aplicarCapacidade(c, d, iniciante) { const cap = d.cap; const temBase = d.temBase; if (cap === "ok" || cap === "limitado") { c.mostrarCTA = true; c.mostrarEmail = true; c.ctaSoft = false; c.ctaTit = "O próximo passo"; c.ctaTexto = "Este é um retrato de 2 minutos. Numa conversa de 20 min, com seus números na mão, eu confirmo se é isso mesmo e te digo o que atacar primeiro. Sem compromisso."; return; } if (cap === "barato") { c.aviso = "Digital que vende não é barato nem automático. Anúncio simplificado e ferramenta grátis não substituem operação — quem promete resultado gastando pouco está te vendendo ilusão. Não sou eu."; c.mostrarCTA = false; c.mostrarEmail = true; c.ctaSoft = true; c.ctaTit = "Por onde começar sem gastar muito"; c.ctaTexto = temBase ? "O movimento mais barato que existe é trabalhar a base que você já tem — vender de novo pra quem já te conhece, sem depender de mídia. Comece por aí. Quando estiver pronto pra investir com consistência, a conversa de 20 min é o caminho." : "Antes de investir em tráfego, vale entender o custo real de operar isso direito — pra não jogar dinheiro fora. Deixa seu e-mail que eu te mando o diagnóstico completo com esse retrato honesto."; return; } if (cap === "sem") { c.aviso = "Sem orçamento pra quem opera, mídia e ferramentas, um contrato de gestão não vai funcionar — e eu não vou te vender o que não entrega. Prefiro ser honesto agora."; c.mostrarCTA = false; c.mostrarEmail = true; c.ctaSoft = true; c.ctaTit = "O que dá pra fazer agora, sozinho"; c.ctaTexto = temBase ? "Trabalhe sua base atual (é de graça e é o que mais converte) e acompanhe meu conteúdo. Quando houver caixa pra investir na operação, me procura — aí faz sentido conversar." : "Construa o básico com seu próprio esforço e acompanhe meu conteúdo — eu mostro o caminho. Quando houver caixa pra investir, a porta está aberta."; return; } // fallback (capacidade não respondida) c.mostrarCTA = true; c.mostrarEmail = true; c.ctaSoft = false; c.ctaTit = "O próximo passo"; c.ctaTexto = "Numa conversa de 20 min eu confirmo o diagnóstico com seus números e te digo o que atacar primeiro."; } function rotulo(sev) { if (sev >= 66) return "vazamento alto"; if (sev >= 33) return "atenção"; if (sev > 0) return "leve"; return "ok"; } // ── estilos ── const S = { root: { minHeight: "100%", background: `radial-gradient(120% 90% at 50% -10%, ${NAVY_PANEL} 0%, ${NAVY} 42%, ${NAVY_DEEP} 100%)`, color: TEXT, fontFamily: "'Open Sans', system-ui, sans-serif", padding: "28px 18px 40px", boxSizing: "border-box" }, shell: { maxWidth: 640, margin: "0 auto" }, brandRow: { display: "flex", alignItems: "baseline", gap: 10, marginBottom: 18, paddingLeft: 2 }, brandName: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700, letterSpacing: 0.3, fontSize: 15, color: "#fff" }, brandTag: { fontSize: 11, letterSpacing: 1.5, textTransform: "uppercase", color: LABEL }, card: { background: "rgba(255,255,255,0.02)", border: `1px solid ${BORDER}`, borderRadius: 14, padding: "30px 28px 26px" }, eyebrow: { fontSize: 11, letterSpacing: 2, color: EMERALD, fontWeight: 600, marginBottom: 14, fontFamily: "'Montserrat', sans-serif" }, h1: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700, fontSize: 30, lineHeight: 1.14, margin: "0 0 16px", color: "#fff", letterSpacing: -0.4 }, h2: { fontFamily: "'Montserrat', sans-serif", fontWeight: 600, fontSize: 20, lineHeight: 1.3, margin: "6px 0 10px", color: "#fff" }, qNota: { fontSize: 12.5, color: LABEL, margin: "0 0 20px", fontStyle: "italic" }, h2res: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700, fontSize: 24, lineHeight: 1.22, margin: "10px 0 22px", color: "#fff", letterSpacing: -0.2 }, perfilBadge: { display: "inline-block", fontFamily: "'Montserrat', sans-serif", fontSize: 10.5, fontWeight: 600, letterSpacing: 1.5, color: LABEL, border: `1px solid ${BORDER}`, borderRadius: 20, padding: "4px 11px" }, lead: { fontSize: 15, lineHeight: 1.6, color: "#C9D2DE", margin: "0 0 14px" }, microHonest: { fontSize: 12.5, color: LABEL, marginTop: 18, lineHeight: 1.5, fontStyle: "italic" }, progWrap: { display: "flex", alignItems: "center", gap: 12, marginBottom: 26 }, progTrack: { flex: 1, height: 5, background: "rgba(255,255,255,0.08)", borderRadius: 4, overflow: "hidden" }, progFill: { height: "100%", background: EMERALD, borderRadius: 4, transition: "width .35s ease" }, progLabel: { fontFamily: "'Montserrat', sans-serif", fontSize: 12, color: LABEL, fontWeight: 600, minWidth: 40, textAlign: "right" }, opcoes: { display: "flex", flexDirection: "column", gap: 10 }, opt: { display: "flex", alignItems: "center", gap: 13, textAlign: "left", padding: "15px 16px", background: "rgba(255,255,255,0.015)", border: `1px solid ${BORDER}`, borderRadius: 10, color: TEXT, fontSize: 14.5, fontFamily: "'Open Sans', sans-serif", cursor: "pointer", lineHeight: 1.4, transition: "border-color .15s, background .15s" }, optDot: { width: 15, height: 15, borderRadius: "50%", border: `2px solid ${LABEL}`, flexShrink: 0, transition: "border-color .15s, background .15s" }, linkBack: { marginTop: 22, background: "none", border: "none", color: LABEL, fontSize: 13, cursor: "pointer", padding: 0, fontFamily: "'Open Sans', sans-serif" }, btnPrim: { marginTop: 8, width: "100%", padding: "15px 20px", background: EMERALD, color: "#06240f", border: "none", borderRadius: 10, fontFamily: "'Montserrat', sans-serif", fontWeight: 700, fontSize: 15, cursor: "pointer", letterSpacing: 0.2, transition: "background .15s, transform .05s" }, mapa: { display: "flex", flexDirection: "column", gap: 15, marginBottom: 24 }, mapaTop: { display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 7 }, mapaNome: { fontSize: 13.5, fontWeight: 600, fontFamily: "'Open Sans', sans-serif" }, mapaPct: { fontSize: 11, letterSpacing: 1, textTransform: "uppercase", fontFamily: "'Montserrat', sans-serif", fontWeight: 600 }, barTrack: { height: 8, background: "rgba(255,255,255,0.07)", borderRadius: 5, overflow: "hidden" }, barFill: { height: "100%", borderRadius: 5, transition: "width .5s ease" }, diagBox: { borderLeft: `3px solid ${EMERALD}`, paddingLeft: 16, margin: "4px 0 20px" }, diagP: { fontSize: 14.5, lineHeight: 1.62, color: "#D3DBE6", margin: "0 0 12px" }, avisoBox: { background: "rgba(224,169,59,0.07)", border: `1px solid rgba(224,169,59,0.3)`, borderRadius: 12, padding: "16px 18px", margin: "0 0 18px" }, avisoTit: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700, fontSize: 12, letterSpacing: 1.2, textTransform: "uppercase", color: AMBER, marginBottom: 7 }, avisoP: { fontSize: 14, lineHeight: 1.55, color: "#E4D9C2", margin: 0 }, ctaBox: { background: "rgba(39,174,96,0.06)", border: `1px solid rgba(39,174,96,0.25)`, borderRadius: 12, padding: "20px 20px 22px" }, ctaBoxSoft: { background: "rgba(255,255,255,0.02)", border: `1px solid ${BORDER}` }, ctaTitulo: { fontFamily: "'Montserrat', sans-serif", fontWeight: 700, fontSize: 13, letterSpacing: 1.5, textTransform: "uppercase", marginBottom: 8 }, ctaTexto: { fontSize: 14, lineHeight: 1.55, color: "#D3DBE6", margin: "0 0 16px" }, emailRow: { display: "flex", gap: 8, marginTop: 4 }, input: { flex: 1, padding: "12px 14px", background: "rgba(255,255,255,0.04)", border: `1px solid ${BORDER}`, borderRadius: 9, color: TEXT, fontSize: 13.5, fontFamily: "'Open Sans', sans-serif", outline: "none" }, btnSec: { padding: "12px 18px", background: "transparent", color: EMERALD, border: `1px solid ${EMERALD}`, borderRadius: 9, fontFamily: "'Montserrat', sans-serif", fontWeight: 600, fontSize: 13.5, cursor: "pointer", whiteSpace: "nowrap" }, emailOk: { fontSize: 13.5, color: EMERALD, marginTop: 4, lineHeight: 1.5 }, }; const CSS = ` @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700&family=Open+Sans:wght@400;600&display=swap'); * { -webkit-font-smoothing: antialiased; } .btnPrim:hover { background: ${EMERALD_DK} !important; } .btnPrim:active { transform: translateY(1px); } .btnSec:hover { background: rgba(39,174,96,0.12) !important; } .opt:hover { border-color: ${EMERALD} !important; background: rgba(39,174,96,0.05) !important; } .opt:hover .optDot { border-color: ${EMERALD} !important; background: ${EMERALD} !important; } .linkBack:hover { color: ${TEXT} !important; } input::placeholder { color: rgba(138,147,166,0.7); } button:focus-visible, input:focus-visible { outline: 2px solid ${EMERALD}; outline-offset: 2px; } `;