// Reusable primitives: CTAButton, SectionHeading, useReveal observer, Reveal wrapper. const { useEffect, useRef, useState, useCallback } = React; /* ---------- Reveal-on-scroll hook ---------- Scroll-driven (more reliable inside preview iframes than IntersectionObserver, which can stay silent when the host iframe isn't intersecting its parent doc). Uses CSS animations triggered by `.in` class; everything stays visible if JS fails or the safety timer expires. */ function useReveal() { useEffect(() => { const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (reduce) return; // Elements already in the initial viewport: mark as "already revealed" so // they do NOT animate (avoids a visible-flash → opacity:0 → animate flash). const vh = window.innerHeight || document.documentElement.clientHeight; const initialTrigger = vh + 100; document.querySelectorAll('[data-reveal]').forEach((el) => { const r = el.getBoundingClientRect(); if (r.top < initialTrigger) { el.dataset.revealSeen = '1'; // skip animating this one } }); let raf = 0; const reveal = () => { raf = 0; const vh2 = window.innerHeight || document.documentElement.clientHeight; const triggerY = vh2 - 60; document.querySelectorAll('[data-reveal]:not(.in)').forEach((el) => { if (el.dataset.revealSeen === '1') return; const r = el.getBoundingClientRect(); if (r.top < triggerY && r.bottom > 0) { const delay = el.dataset.revealDelay || '0'; el.style.animationDelay = delay + 'ms'; el.classList.add('in'); } }); }; const onScroll = () => { if (raf) return; raf = requestAnimationFrame(reveal); }; window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll, { passive: true }); return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); if (raf) cancelAnimationFrame(raf); }; }, []); } /* ---------- CTA Button ---------- */ function CTAButton({ children, variant = 'primary', size = 'md', icon = true, href = '#contato', onClick, disabled, className = '', ariaLabel, target, rel, }) { const base = "inline-flex items-center justify-center gap-2 rounded-full font-medium tracking-tight transition-all duration-300 lift focus:outline-none focus:ring-2 focus:ring-accent-500/40 focus:ring-offset-2 focus:ring-offset-ink-800"; const sizes = { sm: "text-sm px-5 py-2.5", md: "text-[15px] px-7 py-3.5", lg: "text-base px-8 py-4", xl: "text-base md:text-lg px-9 py-5", }; const variants = { primary: "bg-brand-mint hover:bg-brand-mint2 text-brand-navy shadow-[0_10px_30px_-12px_rgba(52,229,198,0.5)] hover:shadow-[0_18px_44px_-12px_rgba(52,229,198,0.6)]", secondary: "border border-white/20 hover:border-brand-mint/50 text-fog-100 hover:bg-white/[0.04]", ghost: "text-fog-100 hover:bg-white/[0.05]", }; const klass = [base, sizes[size], variants[variant], disabled ? 'btn-cta-disabled' : '', className].join(' '); const inner = ( {children} {icon && } ); if (onClick) { return ; } // External links open in a new tab by default const isExternal = typeof href === 'string' && /^https?:\/\//.test(href); const resolvedTarget = target || (isExternal ? '_blank' : undefined); const resolvedRel = rel || (isExternal ? 'noopener noreferrer' : undefined); return {inner}; } /* ---------- Section Heading ---------- */ function Eyebrow({ children }) { return (
{children}
); } function SectionTitle({ children, className = '', align = 'left' }) { return (

{children}

); } function SectionLead({ children, className = '', align = 'left' }) { return (

{children}

); } /* ---------- Subtle decorative divider ---------- */ function SectionDivider() { return
; } /* ---------- Brand mark (text logo) ---------- Mirrors the "IA Vertical for Business" wordmark in text form: Fraunces serif, italic mint "Vertical", with a small uppercase subtitle below. Size scales from the `size` prop. */ function BrandMark({ size = 'md', subtitle = 'Mentoria VIP para Empresários', className = '' }) { const sizes = { sm: { title: 'text-[15px]', sub: 'text-[9px] tracking-[0.18em]' }, md: { title: 'text-[17px] sm:text-[18px]', sub: 'text-[9.5px] sm:text-[10px] tracking-[0.18em]' }, lg: { title: 'text-[22px]', sub: 'text-[11px] tracking-[0.2em]' }, }; const s = sizes[size] || sizes.md; return (
IA{' '} Vertical {' '}for Business {subtitle && ( {subtitle} )}
); } Object.assign(window, { BrandMark }); Object.assign(window, { useReveal, CTAButton, Eyebrow, SectionTitle, SectionLead, SectionDivider, });