/* global React */ const { useState, useEffect, useRef } = React; /* ---------- hooks ---------- */ function useReveal(){ useEffect(()=>{ const els=[...document.querySelectorAll('.reveal')]; if(!('IntersectionObserver' in window)){els.forEach(e=>e.classList.add('in'));return;} const io=new IntersectionObserver((ents)=>{ ents.forEach(e=>{ if(e.isIntersecting){ e.target.classList.add('in'); io.unobserve(e.target);} }); },{threshold:.16,rootMargin:'0px 0px -8% 0px'}); els.forEach(e=>io.observe(e)); // Fallback: if IO callbacks never fire (some embedded/iframe contexts), // force everything visible so content is never stuck at opacity:0. const t=setTimeout(()=>els.forEach(e=>e.classList.add('in')),650); return ()=>{io.disconnect();clearTimeout(t);}; }); } function useCountUp(target, run, opts={}){ const {dur=1400, decimals=0}=opts; const [val,setVal]=useState(0); useEffect(()=>{ if(!run) return; let raf, start; const step=(t)=>{ if(start==null) start=t; const p=Math.min(1,(t-start)/dur); const e=1-Math.pow(1-p,3); setVal(target*e); if(p<1) raf=requestAnimationFrame(step); else setVal(target); }; raf=requestAnimationFrame(step); return ()=>cancelAnimationFrame(raf); },[run,target,dur]); return decimals?val.toFixed(decimals):Math.round(val); } /* ---------- icons (simple geometric line glyphs) ---------- */ function Icon({name, size=24}){ const p={width:size,height:size,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor", strokeWidth:1.7,strokeLinecap:"round",strokeLinejoin:"round"}; const paths={ funnel:<>, lock:<>, timer:<>, shield:<>, chart:<>, network:<>, coins:<>, bell:<>, users:<>, route:<>, layers:<>, check:<>, arrow:<>, spark:<>, doc:<>, }; return ; } function Logo({white=false, h=30}){ return Tovix; } Object.assign(window,{ useReveal, useCountUp, Icon, Logo });