// Blast Events — shared chrome (Nav, Footer, hooks, helpers) // Loaded BEFORE the page-specific app.jsx. Exposes everything on window. const { useEffect, useState, useRef } = React; // ============ Cross-page link helper ============ function currentPage() { const f = (window.location.pathname.split('/').pop() || 'index.html').toLowerCase(); if (!f || f === '' || f === 'index.html') return 'home'; return f.replace('.html', ''); } function homeLink(anchor) { return currentPage() === 'home' ? '#' + anchor : 'index.html#' + anchor; } // Page link helpers — return '#top' if already on that page, else the .html. // 'home' is special: the file is index.html, not home.html. function pageLink(page) { if (currentPage() === page) return '#top'; return page === 'home' ? 'index.html' : page + '.html'; } // ============ TR — mask-slide title reveal ============ function TR({ children, className }) { return {children}; } // ============ Animated counter ============ function Counter({ to, duration = 1800, decimals = 0 }) { const ref = useRef(null); const [val, setVal] = useState(0); useEffect(() => { const el = ref.current; if (!el) return; let raf = 0; const io = new IntersectionObserver((entries) => { for (const e of entries) { if (e.isIntersecting) { const start = performance.now(); const tick = (now) => { const t = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - t, 3); setVal(to * eased); if (t < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); io.unobserve(el); } } }, { threshold: 0.4 }); io.observe(el); return () => {io.disconnect();cancelAnimationFrame(raf);}; }, [to, duration]); return {decimals > 0 ? val.toFixed(decimals) : Math.round(val)}; } // ============ Hero h1 reveal ============ function useHeroReveal() { useEffect(() => { const h = document.querySelector('.hero h1'); if (!h) return; const t = setTimeout(() => { h.classList.add('go'); document.body.classList.add('hero-animated'); }, 80); return () => clearTimeout(t); }, []); } // ============ Custom cursor ============ function useCursor() { useEffect(() => { if (!window.matchMedia('(hover: hover) and (pointer: fine)').matches) return; if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; const el = document.getElementById('cursor'); if (!el) return; let raf = 0,x = 0,y = 0,tx = 0,ty = 0; const move = (e) => {tx = e.clientX;ty = e.clientY;if (!raf) raf = requestAnimationFrame(loop);}; const loop = () => { x += (tx - x) * 0.25;y += (ty - y) * 0.25; el.style.transform = `translate(${x}px, ${y}px) translate(-50%,-50%)`; if (Math.abs(tx - x) > 0.1 || Math.abs(ty - y) > 0.1) raf = requestAnimationFrame(loop);else raf = 0; }; const over = (e) => { const t = e.target.closest('a, button, .svc, .pf-item, .team-slot, .chapter, [data-cursor=lg]'); if (t) el.classList.add('lg');else el.classList.remove('lg'); }; const leave = () => el.classList.add('hide'); const enter = () => el.classList.remove('hide'); window.addEventListener('mousemove', move); window.addEventListener('mouseover', over); document.addEventListener('mouseleave', leave); document.addEventListener('mouseenter', enter); return () => { window.removeEventListener('mousemove', move); window.removeEventListener('mouseover', over); document.removeEventListener('mouseleave', leave); document.removeEventListener('mouseenter', enter); cancelAnimationFrame(raf); }; }, []); } // ============ Reveal on scroll ============ function useReveal() { useEffect(() => { let io = null; let safety = 0; const setup = () => { const els = Array.from(document.querySelectorAll('.reveal, .reveal-children')); els.forEach((el) => { el.classList.add('is-hidden'); el.classList.remove('in'); }); if (!('IntersectionObserver' in window)) { els.forEach((el) => {el.classList.remove('is-hidden');el.classList.add('in');}); return null; } const obs = new IntersectionObserver((entries) => { for (const e of entries) { if (e.isIntersecting) { e.target.classList.remove('is-hidden'); e.target.classList.add('in'); obs.unobserve(e.target); } } }, { threshold: 0.18, rootMargin: '0px 0px -80px 0px' }); els.forEach((el) => obs.observe(el)); return obs; }; const start = () => { if (io) return; io = setup(); safety = setTimeout(() => { document.querySelectorAll('.reveal.is-hidden, .reveal-children.is-hidden').forEach((el) => { el.classList.remove('is-hidden');el.classList.add('in'); }); }, 25000); }; if (document.readyState === 'complete') { requestAnimationFrame(() => requestAnimationFrame(start)); } else { window.addEventListener('load', () => requestAnimationFrame(() => requestAnimationFrame(start)), { once: true }); } return () => {if (io) io.disconnect();if (safety) clearTimeout(safety);}; }, []); } // ============ Nav scroll state ============ function useNavScroll() { useEffect(() => { const nav = document.querySelector('.nav'); if (!nav) return; const onScroll = () => { if (window.scrollY > 40) nav.classList.add('scrolled');else nav.classList.remove('scrolled'); }; onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); return () => window.removeEventListener('scroll', onScroll); }, []); } // ============ Star icon ============ function Star({ size = 18 }) { return ( ); } // ============ Pulse line ============ function PulseLine() { return ( ); } // ============ Nav (cross-page aware) ============ function Nav({ onMenu }) { const page = currentPage(); const cls = (p) => 'nav-link' + (page === p ? ' active' : ''); return ( ); } // ============ Mobile menu ============ function MobileMenu({ open, onClose }) { const page = currentPage(); const aCls = (p) => page === p ? 'active' : ''; return (
Home Chi siamo Servizi Lavori Contatti
TEL+39 340 262 2355
EMAILinfo@blasteventservice.com
SEDEBelluno, 32100
EST.2019
); } // ============ Footer (cross-page aware) ============ function Footer() { return ( ); } // ============ Invio form contatti (Web3Forms) ============ // Chiave d'accesso da web3forms.com, associata a info@blasteventservice.com. // Le richieste arrivano via email a quell'indirizzo. Nessun dato salvato da noi. const WEB3FORMS_KEY = 'be567183-29d9-4161-9a10-672b8f197615'; async function submitContactForm(formEl) { const data = new FormData(formEl); data.append('access_key', WEB3FORMS_KEY); data.append('subject', 'Nuova richiesta dal sito Blast Events'); data.append('from_name', 'Sito Blast Events'); // honeypot lato server di Web3Forms (oltre al nostro campo "website") const res = await fetch('https://api.web3forms.com/submit', { method: 'POST', headers: { Accept: 'application/json' }, body: data }); const json = await res.json().catch(() => ({})); return json.success === true; } // Expose everything to window Object.assign(window, { TR, Counter, Star, PulseLine, Nav, MobileMenu, Footer, useCursor, useReveal, useNavScroll, useHeroReveal, currentPage, homeLink, pageLink, submitContactForm });