/* PL-AI — landing: primitivos + nav + footer + modal + fab */ function Button({ variant = "primary", size, block, href, children, onClick, icon }) { const cls = ["btn", "btn--" + variant, size === "lg" && "btn--lg", block && "btn--block"].filter(Boolean).join(" "); const inner = [children, icon && React.createElement(Icon, { name: icon, key: "i" })]; if (href) return React.createElement("a", { className: cls, href, onClick }, inner); return React.createElement("button", { className: cls, onClick }, inner); } function Eyebrow({ children, invert }) { return

{children}

; } function SectionHead({ eyebrow, title, sub }) { return (
{eyebrow && {eyebrow}}

{title}

{sub &&

{sub}

}
); } function Brand({ onDark }) { return ( PL-AI ); } const NAV_LINKS = [ ["Funcionalidades", "#features"], ["Como funciona", "#how"], ["Para quem é", "#usecases"], ["Planos", "#pricing"], ["Dúvidas", "#faq"], ]; function Nav({ onSignup }) { const [scrolled, setScrolled] = React.useState(false); const [open, setOpen] = React.useState(false); React.useEffect(() => { const fn = () => setScrolled(window.scrollY > 12); fn(); window.addEventListener("scroll", fn, { passive: true }); return () => window.removeEventListener("scroll", fn); }, []); return ( ); } const FOOTER_COLS = [ ["Produto", ["Planos", "Funcionalidades", "API", "Roadmap", "Changelog"]], ["Empresa", ["Sobre", "Blog", "Vagas", "Contato"]], ["Recursos", ["Documentação", "Tutoriais", "Comunidade", "Suporte"]], ["Legal", ["Termos de uso", "Privacidade", "LGPD", "Cookies"]], ]; function Footer() { return ( ); } function WhatsAppFab({ onClick }) { return ( ); } function SignupModal({ onClose, plan }) { // steps: 1=email+plan+metodo, 2=pagamento, 3=perfil (nome/negócio/tel/senha), 4=indo pro painel const [step, setStep] = React.useState(1); const [loading, setLoading] = React.useState(false); const [err, setErr] = React.useState(""); const [email, setEmail] = React.useState(""); const [selectedPlan, setSelectedPlan] = React.useState(plan || "starter"); const [payMethod, setPayMethod] = React.useState("paypal"); // Stripe temporariamente escondido — só PayPal ativo const [signupData, setSignupData] = React.useState(null); const [profile, setProfile] = React.useState({ name: "", business_name: "", phone: "", password: "", vertical: "custom" }); const stripeRef = React.useRef(null); const elementsRef = React.useRef(null); const paymentElementMounted = React.useRef(false); const paypalButtonMounted = React.useRef(false); React.useEffect(() => { const fn = (e) => e.key === "Escape" && onClose(); window.addEventListener("keydown", fn); return () => window.removeEventListener("keydown", fn); }, []); const PLAN_INFO = { starter: { label: "Starter", price: "R$ 5", amountCents: 500, desc: "Assinatura base. Ideal pra começar." }, plus: { label: "Plus", price: "R$ 47", amountCents: 4700, desc: "Mais conversas e recursos pra crescer." }, pro: { label: "Pro", price: "R$ 149", amountCents: 14900, desc: "Mais volume e recursos." }, business: { label: "Business", price: "R$ 500", amountCents: 50000, desc: "Ilimitado + white-label." }, }; const cur = PLAN_INFO[selectedPlan] || PLAN_INFO.starter; // STEP 1 → 2: só email + plano + método async function submitStep1(e) { e.preventDefault(); setErr(""); if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { setErr("email inválido"); return; } setLoading(true); try { const endpoint = payMethod === "paypal" ? "/api/signup/paypal-create" : "/api/signup/subscribe"; const r = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email, plan: selectedPlan }), }); const d = await r.json(); if (!r.ok || (!d.client_secret && !d.signup_id)) throw new Error(d.error || "falha ao preparar pagamento"); setSignupData(d); setStep(2); } catch (e) { setErr(e.message || "erro"); } finally { setLoading(false); } } // Step 2: monta Stripe Elements (só se card) React.useEffect(() => { if (step !== 2 || !signupData || payMethod !== "card" || paymentElementMounted.current) return; (async () => { if (!window.Stripe) { await new Promise((res, rej) => { const s = document.createElement("script"); s.src = "https://js.stripe.com/v3/"; s.onload = res; s.onerror = () => rej(new Error("falha carregando Stripe.js")); document.head.appendChild(s); }); } try { const stripe = window.Stripe(signupData.publishable_key); const elements = stripe.elements({ clientSecret: signupData.client_secret, appearance: { theme: "night", variables: { colorPrimary: "#10b981", colorBackground: "#0f0f14", colorText: "#e8e8ee", colorDanger: "#e5484d", fontFamily: "Geist, system-ui, sans-serif", borderRadius: "10px" }, }, }); const pe = elements.create("payment", { layout: "tabs" }); pe.mount("#stripe-payment-element"); stripeRef.current = stripe; elementsRef.current = elements; paymentElementMounted.current = true; } catch (e) { setErr("erro carregando formulário de cartão: " + e.message); } })(); }, [step, signupData, payMethod]); // Step 2: monta PayPal Button (só o botão Cartão de Débito/Crédito) React.useEffect(() => { if (step !== 2 || !signupData || payMethod !== "paypal" || paypalButtonMounted.current) return; (async () => { const sdkSrc = `https://www.paypal.com/sdk/js?client-id=${encodeURIComponent(signupData.client_id)}¤cy=BRL&intent=capture&components=buttons`; if (!window.paypal || (window.__pp_client_id && window.__pp_client_id !== signupData.client_id) || !window.paypal.Buttons) { const existing = document.querySelectorAll("script[data-paypal-sdk]"); existing.forEach(s => s.remove()); window.paypal = undefined; await new Promise((res, rej) => { const s = document.createElement("script"); s.src = sdkSrc; s.setAttribute("data-paypal-sdk", "1"); s.onload = res; s.onerror = () => rej(new Error("falha carregando PayPal SDK")); document.head.appendChild(s); }); window.__pp_client_id = signupData.client_id; } try { const container = document.getElementById("paypal-button-container"); if (container) container.innerHTML = ""; window.paypal.Buttons({ fundingSource: window.paypal.FUNDING.CARD, style: { shape: "rect", color: "black", layout: "vertical", label: "pay" }, createOrder: async () => { const r = await fetch("/api/signup/paypal-order", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signup_id: signupData.signup_id }), }); const j = await r.json(); if (!r.ok || !j.order_id) throw new Error(j.error || "falha criando pedido"); return j.order_id; }, onApprove: async (data) => { setLoading(true); const fin = await fetch("/api/signup/paypal-finalize", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signup_id: signupData.signup_id, order_id: data.orderID }), }); const j = await fin.json(); setLoading(false); if (!fin.ok || !j.panel_url) { setErr(j.error || "falha finalizando"); return; } setSignupData(prev => ({ ...prev, panel_url: j.panel_url, tenant_slug: j.slug })); setStep(3); }, onError: (err) => { setErr("erro PayPal: " + (err?.message || String(err).slice(0,100))); }, onCancel: () => { setErr("pagamento cancelado"); }, }).render("#paypal-button-container"); paypalButtonMounted.current = true; } catch (e) { setErr("erro montando PayPal: " + e.message); } })(); }, [step, signupData, payMethod]); // Step 2 → 3 (Stripe): confirma SetupIntent + finalize async function submitStep2Stripe(e) { e.preventDefault(); setErr(""); if (!stripeRef.current || !elementsRef.current) { setErr("cartão ainda carregando..."); return; } setLoading(true); try { const { error: setupErr, setupIntent } = await stripeRef.current.confirmSetup({ elements: elementsRef.current, redirect: "if_required", }); if (setupErr) throw new Error(setupErr.message || "cartão recusado"); if (!setupIntent || setupIntent.status !== "succeeded") throw new Error("cartão não pôde ser validado"); const pmId = setupIntent.payment_method; if (!pmId) throw new Error("payment_method não retornado"); const finRes = await fetch("/api/signup/finalize", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signup_id: signupData.signup_id, payment_method_id: pmId }), }); const finD = await finRes.json(); if (!finRes.ok || !finD.panel_url) throw new Error(finD.error || "falha finalizando"); setSignupData(prev => ({ ...prev, panel_url: finD.panel_url, tenant_slug: finD.slug })); setStep(3); } catch (e) { setErr(e.message || "erro"); } finally { setLoading(false); } } // Step 3 → 4: envia perfil, redireciona async function submitStep3(e) { e.preventDefault(); setErr(""); if (profile.password.length < 6) { setErr("senha precisa ter no mínimo 6 caracteres"); return; } setLoading(true); try { const r = await fetch("/api/signup/complete-profile", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signup_id: signupData.signup_id, ...profile }), }); const d = await r.json(); if (!r.ok || !d.panel_url) throw new Error(d.error || "falha completando perfil"); setStep(4); setTimeout(() => { window.location.href = d.panel_url; }, 500); } catch (e) { setErr(e.message || "erro"); } finally { setLoading(false); } } const updProfile = (k) => (e) => setProfile(prev => ({ ...prev, [k]: e.target.value })); const chipStyle = (k, selected) => ({ flex: 1, padding: "12px 8px", border: "1px solid var(--border)", borderRadius: 10, background: selected ? "rgba(16,185,129,.10)" : "transparent", color: "var(--fg1)", cursor: "pointer", fontWeight: 600, borderColor: selected ? "var(--primary)" : "var(--border)", display: "flex", flexDirection: "column", gap: 2, alignItems: "center", fontSize: 13, }); return (
e.stopPropagation()}> {step === 1 && ( <>

Testar PL-AI

Só o email pra começar. Escolhe o plano, paga, e depois completa o resto. Cobrança mensal, cancela quando quiser.

{cur.label} — {cur.price}/mês. {cur.desc}

setEmail(e.target.value)} /> {/* Toggle Cartão/PayPal escondido temporariamente — só PayPal ativo */} {err &&

⚠ {err}

}

Pagamento seguro. "Cartão de débito ou crédito. Cobrança mensal."

)} {step === 2 && signupData && ( <>

Pagamento

{cur.label}{cur.price}. Pagamento único agora. Email: {email}

{payMethod === "card" ? (
{err &&

⚠ {err}

}

🔒 Cartão criptografado pela Stripe. Não guardamos nada.

) : (
{err &&

⚠ {err}

} {loading &&

Processando...

}

🔒 Pagamento seguro. Cartão de débito ou crédito.

)} )} {step === 3 && signupData && ( <>

Pagamento confirmado!

Agora só falta completar seus dados pra ter acesso ao painel.

{err &&

⚠ {err}

}

Sua senha vale só pra você. Podemos mudar depois no painel.

)} {step === 4 && (

Entrando no painel...

Só um instante.

)}
); } Object.assign(window, { Button, Eyebrow, SectionHead, Brand, Nav, Footer, WhatsAppFab, SignupModal });