(() => { const $ = (s, root = document) => root.querySelector(s); const $$ = (s, root = document) => [...root.querySelectorAll(s)]; const header = $('#siteHeader'); const progress = $('#scrollProgress'); const bottomCta = $('#bottomCta'); const toTop = $('#toTop'); const menu = $('#mobileMenu'); const menuToggle = $('#menuToggle'); const accessPanel = $('#accessPanel'); const root = document.documentElement; const setLock = (locked) => document.body.classList.toggle('menu-lock', locked); function onScroll(){ const y = window.scrollY; const h = Math.max(1, document.documentElement.scrollHeight - window.innerHeight); header?.classList.toggle('scrolled', y > 18); if(progress) progress.style.width = `${Math.min(100, (y / h) * 100)}%`; const ctaSection = $('#cta'); const footer = $('#contact'); const ctaTop = ctaSection ? ctaSection.getBoundingClientRect().top + y : Infinity; const footerTop = footer ? footer.getBoundingClientRect().top + y : Infinity; const shouldShow = y > window.innerHeight * .78 && y + window.innerHeight < Math.min(ctaTop + 120, footerTop); bottomCta?.classList.toggle('show', shouldShow); toTop?.classList.toggle('show', y > 650); } addEventListener('scroll', onScroll, {passive:true}); addEventListener('resize', onScroll, {passive:true}); onScroll(); if('IntersectionObserver' in window){ const revealObserver = new IntersectionObserver(entries => { entries.forEach(entry => { if(entry.isIntersecting){ entry.target.classList.add('in'); revealObserver.unobserve(entry.target); } }); }, {threshold:.12, rootMargin:'0px 0px -30px 0px'}); $$('.reveal').forEach(el => revealObserver.observe(el)); const sections = $$('main section[id]'); const navLinks = $$('.nav-links a'); const activeObserver = new IntersectionObserver(entries => { entries.forEach(entry => { if(entry.isIntersecting){ navLinks.forEach(a => a.classList.toggle('active', a.getAttribute('href') === `#${entry.target.id}`)); } }); }, {rootMargin:'-40% 0px -50% 0px'}); sections.forEach(section => activeObserver.observe(section)); } else { $$('.reveal').forEach(el => el.classList.add('in')); } function openMenu(){ menu?.classList.add('open'); menu?.setAttribute('aria-hidden','false'); menuToggle?.setAttribute('aria-expanded','true'); setLock(true); } function closeMenu(){ menu?.classList.remove('open'); menu?.setAttribute('aria-hidden','true'); menuToggle?.setAttribute('aria-expanded','false'); if(!accessPanel?.classList.contains('open')) setLock(false); } menuToggle?.addEventListener('click', openMenu); $('#closeMenu')?.addEventListener('click', closeMenu); menu?.addEventListener('click', e => { if(e.target === menu || e.target.closest('a')) closeMenu(); }); function openAccess(){ accessPanel?.classList.add('open'); accessPanel?.setAttribute('aria-hidden','false'); setLock(true); } function closeAccess(){ accessPanel?.classList.remove('open'); accessPanel?.setAttribute('aria-hidden','true'); if(!menu?.classList.contains('open')) setLock(false); } $$('.access-open').forEach(btn => btn.addEventListener('click', openAccess)); $('#closeAccess')?.addEventListener('click', closeAccess); accessPanel?.addEventListener('click', e => { if(e.target === accessPanel) closeAccess(); }); let saved = {}; try { saved = JSON.parse(localStorage.getItem('psrcAccess') || '{}'); } catch(_) {} if(saved.scale) root.style.setProperty('--font-scale', saved.scale); if(saved.contrast) document.body.classList.add('high-contrast'); if(saved.motion) document.body.classList.add('reduce-motion'); function saveAccess(){ localStorage.setItem('psrcAccess', JSON.stringify({ scale: getComputedStyle(root).getPropertyValue('--font-scale').trim() || '1', contrast: document.body.classList.contains('high-contrast'), motion: document.body.classList.contains('reduce-motion') })); } accessPanel?.addEventListener('click', e => { const action = e.target.dataset.access; if(!action) return; const scale = parseFloat(getComputedStyle(root).getPropertyValue('--font-scale')) || 1; if(action === 'fontPlus') root.style.setProperty('--font-scale', Math.min(1.18, scale + .06)); if(action === 'fontMinus') root.style.setProperty('--font-scale', Math.max(.92, scale - .06)); if(action === 'contrast') document.body.classList.toggle('high-contrast'); if(action === 'motion') document.body.classList.toggle('reduce-motion'); if(action === 'reset'){ root.style.setProperty('--font-scale', 1); document.body.classList.remove('high-contrast','reduce-motion'); localStorage.removeItem('psrcAccess'); return; } saveAccess(); }); toTop?.addEventListener('click', () => window.scrollTo({top:0, behavior: document.body.classList.contains('reduce-motion') ? 'auto' : 'smooth'})); addEventListener('keydown', e => { if(e.key === 'Escape'){ closeMenu(); closeAccess(); } }); })();