/* FORUM website — Parallax architectural hero. A Roman-forum colonnade receding in depth: layered columns parallax on scroll + pointer (the Stepout reference, rebuilt in the brand). Pure geometry — no generated imagery. Exports window.ForumHero. */ function makeColumns(count) { return Array.from({ length: count }); } function ColonnadeLayer({ depth, count, height, color, opacity, blur, bottom, parallax }) { // depth 0 = farthest, larger = nearer const cols = makeColumns(count); return (
{cols.map((_, i) => (
{/* capital */}
))}
); } function ForumHero({ logoSrc, onPrimary, onSecondary }) { const stageRef = React.useRef(null); const pointer = React.useRef({ x: 0, y: 0 }); const scroll = React.useRef(0); React.useEffect(() => { let raf = 0; const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const apply = () => { const layers = stageRef.current ? stageRef.current.querySelectorAll('[data-speed]') : []; layers.forEach((el) => { const sp = parseFloat(el.getAttribute('data-speed')); const ty = reduce ? 0 : scroll.current * sp; const tx = reduce ? 0 : pointer.current.x * sp * 26; el.style.transform = `translate(${tx}px, ${ty}px)`; }); raf = 0; }; const queue = () => { if (!raf) raf = requestAnimationFrame(apply); }; const onScroll = () => { const r = stageRef.current ? stageRef.current.getBoundingClientRect() : null; scroll.current = r ? Math.max(0, -r.top) : window.scrollY; queue(); }; const onMove = (e) => { const w = window.innerWidth, h = window.innerHeight; pointer.current = { x: (e.clientX / w - 0.5) * 2, y: (e.clientY / h - 0.5) * 2 }; queue(); }; // initial onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('pointermove', onMove, { passive: true }); return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('pointermove', onMove); if (raf) cancelAnimationFrame(raf); }; }, []); return (
{/* Background video */} {/* Brand-blue tint over the video */}
); } window.ForumHero = ForumHero;