const { useEffect, useState } = React; // Ícone por função do botão, desenhado em SVG inline: nada de arquivo externo e a // cor vem do CSS via currentColor, que é o oposto da cor do botão. const ICONES = { seta: , conversa: , selo: <>, }; const GRAFISMOS = { quote: , caminho: <>, livro: <>, pessoas: <>, selo: <>, }; function Grafismo({ tipo = 'caminho', className = '' }) { return ; } function DisabledButton({ children, className = '', small = false, icone = 'seta', href = '' }) { const desenho = ICONES[icone]; const conteudo = <>{children}{desenho && }; if (href) return {conteudo}; return ; } function Eyebrow({ children }) { return

{children}

; } // `layout` decide a diagramação do cabeçalho da dobra: // 'duplo' título à esquerda, introdução à direita, alinhados pela base. // 'centro' cabeçalho centrado, para dobra sem introdução. // padrão empilhado, para quando a coluna ao lado já tem conteúdo. // Existe porque 5 das 13 dobras usavam só 51% da calha na faixa do título. function SectionTitle({ eyebrow, title, intro, light = false, layout = '' }) { const variante = layout ? `section-title--${layout}` : ''; return
{eyebrow && {eyebrow}}

{title}

{intro &&

{intro}

}
; } function Countdown({ deadline }) { const getRemaining = () => Math.max(0, new Date(deadline).getTime() - Date.now()); const [remaining, setRemaining] = useState(getRemaining()); useEffect(() => { const id = window.setInterval(() => setRemaining(getRemaining()), 1000); return () => window.clearInterval(id); }, [deadline]); const total = Math.floor(remaining / 1000); const days = Math.floor(total / 86400); const hours = Math.floor((total % 86400) / 3600); const minutes = Math.floor((total % 3600) / 60); const seconds = total % 60; return
{[['dias', days], ['horas', hours], ['min', minutes], ['seg', seconds]].map(([label, value]) =>
{String(value).padStart(2, '0')}{label}
)}
; } function TestimonialCard({ item, compact = false }) { const name = Array.isArray(item) ? item[0] : item.name; const quote = Array.isArray(item) ? item[1] : item.quote; return
“{quote}”
{name}
; } function StatRibbon({ stats }) { const icones = ['livro', 'caminho', 'pessoas']; return
{stats.map((stat, index) => {stat})}
; } // Um acordeão só serve o FAQ e a grade de módulos. `renderBody` deixa o corpo ser // um parágrafo ou uma lista de tópicos sem duplicar estado, marcação e acesso. function Accordion({ items, className = '', getKey, renderHead, renderBody, initialOpen = 0 }) { const [open, setOpen] = useState(initialOpen); const key = getKey || ((item) => item[0]); const head = renderHead || ((item) => {item[0]}); const body = renderBody || ((item) =>

{item[1]}

); return
{items.map((item, index) => { const isOpen = open === index; return

; })}
; } // Anima só o número que abre a frase e devolve o resto do texto intacto. Se a // string não começar com número, renderiza verbatim e não faz nada. function CountUpText({ children }) { const texto = String(children); const match = texto.match(/^([\d.]+)(\s[\s\S]*)$/); const alvo = match ? Number(match[1].replace(/\./g, '')) : null; const [valor, setValor] = useState(alvo); const ref = React.useRef(null); useEffect(() => { if (alvo === null || !ref.current) return; if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; if (!('IntersectionObserver' in window)) return; let raf = 0; const observer = new IntersectionObserver((entries) => { if (!entries.some((e) => e.isIntersecting)) return; observer.disconnect(); const inicio = performance.now(); const passo = (agora) => { const t = Math.min(1, (agora - inicio) / 900); setValor(Math.round(alvo * (1 - Math.pow(1 - t, 3)))); if (t < 1) raf = requestAnimationFrame(passo); }; setValor(0); raf = requestAnimationFrame(passo); }, { threshold: 0.4 }); observer.observe(ref.current); return () => { observer.disconnect(); cancelAnimationFrame(raf); }; }, [alvo]); if (alvo === null) return {texto}; return {valor.toLocaleString('pt-BR')}{match[2]}; } // Vegetação em três planos de profundidade. A leitura de 3D vem de escala, // sobreposição, sangria pela borda e velocidade de parallax distinta por plano. // Nunca de opacidade reduzida nem de desfoque: planta apagada foi rejeitada. function Flora({ plano = 'fundo', especie = 'monstera', pos = 'dir-topo', espelho = false }) { return