/* ================================================== STOCK, Institucional page interactive components Editorial/industrial language, distinct from Produto. Depends on React (global) + internal.jsx (Counter opt.) Exposes: useInstFx, EdNums, Timeline, DifRows, SegMarquee ================================================== */ const { useState: iUseState, useEffect: iUseEffect, useRef: iUseRef } = React; /* ---------- scroll fx: wipe/rise reveals + strip parallax + timeline ---------- */ function useInstFx() { iUseEffect(() => { /* reveals */ const els = document.querySelectorAll('.wipe, .rise'); const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('on'); io.unobserve(e.target); } }); }, { threshold: 0.18 }); els.forEach(el => io.observe(el)); /* strip parallax */ const strips = document.querySelectorAll('.ed-strip img'); let raf = null; const onScroll = () => { if (raf) return; raf = requestAnimationFrame(() => { raf = null; const vh = window.innerHeight; strips.forEach(img => { const r = img.parentElement.getBoundingClientRect(); const p = (r.top + r.height / 2 - vh / 2) / vh; // -0.5..0.5-ish img.style.transform = `translateY(${p * -40}px)`; }); /* timeline fill + dots */ const tl = document.querySelector('.tl'); if (tl) { const fill = tl.querySelector('.tl-fill'); const r = tl.getBoundingClientRect(); const passed = Math.min(Math.max((vh * 0.55 - r.top) / r.height, 0), 1); if (fill) fill.style.height = `${passed * 100}%`; tl.querySelectorAll('.tl-step').forEach(step => { const sr = step.getBoundingClientRect(); step.classList.toggle('on', sr.top + sr.height / 2 < vh * 0.62); }); } }); }; window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); return () => { window.removeEventListener('scroll', onScroll); io.disconnect(); }; }, []); } /* ---------- giant numbers with count-up ---------- */ function EdNum({ to, suffix, label, delay }) { const ref = iUseRef(null); const [v, setV] = iUseState(0); iUseEffect(() => { const el = ref.current; const io = new IntersectionObserver(([e]) => { if (!e.isIntersecting) return; io.disconnect(); const t0 = performance.now(); const dur = 1600; const tick = (t) => { const p = Math.min((t - t0) / dur, 1); setV(Math.round(to * (1 - Math.pow(1 - p, 3)))); if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); }, { threshold: 0.4 }); io.observe(el); return () => io.disconnect(); }, [to]); return (
Uma equipe só cuida de todo o processo, sem terceirizar etapas, sem repassar responsabilidade.
{s.d}
{d.d}