const body = document.body; const menuToggle = document.querySelector('.menu-toggle'); const navLinks = document.querySelectorAll('.site-nav a'); const progress = document.querySelector('.scroll-progress span'); if (menuToggle) { menuToggle.addEventListener('click', () => { const isOpen = body.classList.toggle('menu-open'); menuToggle.setAttribute('aria-expanded', String(isOpen)); }); } navLinks.forEach((link) => { link.addEventListener('click', () => { body.classList.remove('menu-open'); menuToggle?.setAttribute('aria-expanded', 'false'); }); }); const updateProgress = () => { if (!progress) return; const max = document.documentElement.scrollHeight - window.innerHeight; const value = max > 0 ? window.scrollY / max : 0; progress.style.transform = `scaleX(${value})`; }; window.addEventListener('scroll', updateProgress, { passive: true }); updateProgress(); const revealObserver = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); revealObserver.unobserve(entry.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -5% 0px' }); document.querySelectorAll('.reveal').forEach((element) => revealObserver.observe(element)); const currentPage = body.dataset.page; document.querySelectorAll(`[data-nav="${currentPage}"]`).forEach((link) => { link.setAttribute('aria-current', 'page'); }); const dot = document.querySelector('.cursor-dot'); const ring = document.querySelector('.cursor-ring'); if (dot && ring && window.matchMedia('(pointer: fine)').matches) { let mouseX = 0; let mouseY = 0; let ringX = 0; let ringY = 0; window.addEventListener('mousemove', (event) => { mouseX = event.clientX; mouseY = event.clientY; dot.classList.add('is-active'); ring.classList.add('is-active'); dot.style.left = `${mouseX}px`; dot.style.top = `${mouseY}px`; }); const animateRing = () => { ringX += (mouseX - ringX) * 0.16; ringY += (mouseY - ringY) * 0.16; ring.style.left = `${ringX}px`; ring.style.top = `${ringY}px`; requestAnimationFrame(animateRing); }; animateRing(); document.querySelectorAll('a, button, input, textarea, select').forEach((target) => { target.addEventListener('mouseenter', () => ring.classList.add('is-hovering')); target.addEventListener('mouseleave', () => ring.classList.remove('is-hovering')); }); } document.querySelectorAll('[data-count]').forEach((element) => { const target = Number(element.dataset.count); const suffix = element.dataset.suffix || ''; const counterObserver = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (!entry.isIntersecting) return; const start = performance.now(); const duration = 1300; const tick = (time) => { const progressValue = Math.min((time - start) / duration, 1); const eased = 1 - Math.pow(1 - progressValue, 4); element.textContent = `${Math.round(target * eased)}${suffix}`; if (progressValue < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); counterObserver.disconnect(); }); }, { threshold: 0.5 }); counterObserver.observe(element); }); const contactForm = document.querySelector('[data-contact-form]'); if (contactForm) { contactForm.addEventListener('submit', (event) => { event.preventDefault(); const status = contactForm.querySelector('.form-status'); const name = new FormData(contactForm).get('name') || 'there'; status.textContent = `Thank you, ${name}. Your collaboration brief is ready for review.`; contactForm.reset(); }); } const applicationForm = document.querySelector('[data-application-form]'); if (applicationForm) { applicationForm.addEventListener('submit', (event) => { event.preventDefault(); const status = applicationForm.querySelector('.form-status'); const name = new FormData(applicationForm).get('name') || 'builder'; status.textContent = `Application received, ${name}. Connect this form to a submission service before launch.`; applicationForm.reset(); }); } document.querySelectorAll('[data-year]').forEach((node) => { node.textContent = new Date().getFullYear(); });