// sections.jsx — NewB LP sections // ── Hooks ─────────────────────────────────────── function useReveal() { React.useEffect(() => { const els = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' }); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }, []); } function useCountUp(target, trigger, opts = {}) { const { duration = 1800, decimals = 0 } = opts; const [val, setVal] = React.useState(0); React.useEffect(() => { if (!trigger) return; let raf, start; const step = (t) => { if (!start) start = t; const p = Math.min(1, (t - start) / duration); const eased = 1 - Math.pow(1 - p, 3); setVal(target * eased); if (p < 1) raf = requestAnimationFrame(step); }; raf = requestAnimationFrame(step); return () => cancelAnimationFrame(raf); }, [trigger, target, duration]); return decimals === 0 ? Math.round(val) : val.toFixed(decimals); } function useInView(ref) { const [seen, setSeen] = React.useState(false); React.useEffect(() => { if (!ref.current) return; const io = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) {setSeen(true);io.disconnect();} }, { threshold: 0.3 }); io.observe(ref.current); return () => io.disconnect(); }, [ref]); return seen; } // ── Forex ticker ──────────────────────────────── const TICKER_DATA = [ { pair: 'EUR/USD', px: '1.0842', d: '+0.12%', up: true }, { pair: 'GBP/USD', px: '1.2718', d: '+0.34%', up: true }, { pair: 'USD/JPY', px: '149.86', d: '−0.21%', up: false }, { pair: 'USD/BRL', px: '5.0214', d: '+0.48%', up: true }, { pair: 'AUD/USD', px: '0.6592', d: '−0.08%', up: false }, { pair: 'USD/CHF', px: '0.8814', d: '+0.06%', up: true }, { pair: 'NZD/USD', px: '0.6184', d: '−0.14%', up: false }, { pair: 'USD/CAD', px: '1.3622', d: '+0.18%', up: true }, { pair: 'EUR/GBP', px: '0.8527', d: '−0.05%', up: false }, { pair: 'USD/MXN', px: '17.214', d: '+0.22%', up: true }]; function Ticker() { const items = [...TICKER_DATA, ...TICKER_DATA]; return (
{items.map((t, i) => {t.pair} {t.px} {t.d} )}
); } // ── Header ────────────────────────────────────── function Header({ ctaLabel, onCTA }) { const [open, setOpen] = React.useState(false); // Close on Escape React.useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === 'Escape') setOpen(false); }; document.addEventListener('keydown', onKey); // Lock background scroll while menu open const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = prev; }; }, [open]); const closeMenu = () => setOpen(false); return (
NewB
{ctaLabel}
{/* Mobile dropdown menu */}
{ closeMenu(); onCTA && onCTA(); }}> {ctaLabel}
{open && }
); } // ── Hero ──────────────────────────────────────── const HEADLINES = { A: <> Seu dinheiro no exterior. Seu controle.
Nossa operação. , B: <> O investimento que a poupança nunca vai te oferecer. , C: <> Existe um mercado onde seu dinheiro trabalha
enquanto você vive. }; function FloatingPaths({ position }) { const paths = Array.from({ length: 36 }, (_, i) => ({ id: i, d: `M-${380 - i * 5 * position} -${189 + i * 6}C-${380 - i * 5 * position} -${189 + i * 6} -${312 - i * 5 * position} ${216 - i * 6} ${152 - i * 5 * position} ${343 - i * 6}C${616 - i * 5 * position} ${470 - i * 6} ${684 - i * 5 * position} ${875 - i * 6} ${684 - i * 5 * position} ${875 - i * 6}`, width: 0.5 + i * 0.03, dur: 20 + i * 0.7 % 10, delay: -(i * 0.4 + (position > 0 ? 0 : 1.2)) })); return ( ); } function SparklesCanvas({ height = 140, density = 0.6, // particles per 1000px² color = '#ffffff', speed = 1 }) { const ref = React.useRef(null); React.useEffect(() => { const cvs = ref.current; if (!cvs) return; const ctx = cvs.getContext('2d'); let raf,w = 0,h = 0,dpr = Math.min(window.devicePixelRatio || 1, 2); let particles = []; function resize() { const r = cvs.getBoundingClientRect(); w = r.width;h = r.height; cvs.width = Math.round(w * dpr); cvs.height = Math.round(h * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0); const count = Math.max(40, Math.round(w * h * density / 100)); particles = new Array(count).fill(0).map(() => spawn()); } function spawn() { return { x: Math.random() * w, y: Math.random() * h, r: 0.3 + Math.random() * 1.1, // opacity oscillation a: Math.random(), // angular speed of opacity wave ph: Math.random() * Math.PI * 2, sp: (0.4 + Math.random() * 1.2) * speed, // tiny drift vx: (Math.random() - 0.5) * 0.15, vy: (Math.random() - 0.5) * 0.15 }; } let t0 = performance.now(); function frame(t) { const dt = Math.min(0.05, (t - t0) / 1000);t0 = t; ctx.clearRect(0, 0, w, h); for (const p of particles) { p.ph += dt * p.sp; const op = 0.15 + 0.85 * (0.5 + 0.5 * Math.sin(p.ph)); p.x += p.vx;p.y += p.vy; if (p.x < -2) p.x = w + 2; if (p.x > w + 2) p.x = -2; if (p.y < -2) p.y = h + 2; if (p.y > h + 2) p.y = -2; ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); ctx.fillStyle = color; ctx.globalAlpha = op; ctx.fill(); } ctx.globalAlpha = 1; raf = requestAnimationFrame(frame); } resize(); const ro = new ResizeObserver(resize); ro.observe(cvs); raf = requestAnimationFrame(frame); return () => {cancelAnimationFrame(raf);ro.disconnect();}; }, [density, color, speed]); return (
); } function DottedSurface({ color = '#f5f1e8' }) { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el || !window.THREE) return; const THREE = window.THREE; const SEPARATION = 150; const AMOUNTX = 40; const AMOUNTY = 60; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, el.clientWidth / el.clientHeight, 1, 10000); camera.position.set(0, 355, 1220); const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(el.clientWidth, el.clientHeight); renderer.setClearColor(0x000000, 0); el.appendChild(renderer.domElement); const positions = []; const colors = []; const c = new THREE.Color(color); for (let ix = 0; ix < AMOUNTX; ix++) { for (let iy = 0; iy < AMOUNTY; iy++) { const x = ix * SEPARATION - AMOUNTX * SEPARATION / 2; const z = iy * SEPARATION - AMOUNTY * SEPARATION / 2; positions.push(x, 0, z); colors.push(c.r, c.g, c.b); } } const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); const material = new THREE.PointsMaterial({ size: 8, vertexColors: true, transparent: true, opacity: 0.55, sizeAttenuation: true }); const points = new THREE.Points(geometry, material); scene.add(points); let count = 0; let raf; const animate = () => { raf = requestAnimationFrame(animate); const pa = geometry.attributes.position; const arr = pa.array; let i = 0; for (let ix = 0; ix < AMOUNTX; ix++) { for (let iy = 0; iy < AMOUNTY; iy++) { const idx = i * 3; arr[idx + 1] = Math.sin((ix + count) * 0.3) * 50 + Math.sin((iy + count) * 0.5) * 50; i++; } } pa.needsUpdate = true; renderer.render(scene, camera); count += 0.1; }; const handleResize = () => { const w = el.clientWidth,h = el.clientHeight; camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); }; const ro = new ResizeObserver(handleResize); ro.observe(el); animate(); return () => { cancelAnimationFrame(raf); ro.disconnect(); geometry.dispose(); material.dispose(); renderer.dispose(); if (renderer.domElement.parentNode === el) el.removeChild(renderer.domElement); }; }, [color]); return