'use strict'; /* ══════════════════════════════════════ NAVIGATION — DROPDOWN (click-based) ══════════════════════════════════════ */ function initDropdowns() { document.querySelectorAll('.nav__dropdown').forEach(item => { const trigger = item.querySelector('.nav__trigger'); const menu = item.querySelector('.nav__dropdown-menu'); if (!trigger || !menu) return; trigger.addEventListener('click', (e) => { e.stopPropagation(); const isOpen = menu.classList.toggle('open'); trigger.setAttribute('aria-expanded', isOpen); // Close siblings document.querySelectorAll('.nav__dropdown-menu.open').forEach(m => { if (m !== menu) { m.classList.remove('open'); m.closest('.nav__dropdown').querySelector('.nav__trigger')?.setAttribute('aria-expanded','false'); } }); }); }); // Close on outside click document.addEventListener('click', () => { document.querySelectorAll('.nav__dropdown-menu.open').forEach(m => { m.classList.remove('open'); m.closest('.nav__dropdown').querySelector('.nav__trigger')?.setAttribute('aria-expanded','false'); }); }); // Close on Escape document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { document.querySelectorAll('.nav__dropdown-menu.open').forEach(m => { m.classList.remove('open'); m.closest('.nav__dropdown').querySelector('.nav__trigger')?.setAttribute('aria-expanded','false'); }); } }); } /* ══════════════════════════════════════ NAVIGATION — MOBILE BURGER ══════════════════════════════════════ */ function initMobileNav() { const burger = document.getElementById('nav-burger'); const navLinks = document.getElementById('nav-links'); if (!burger || !navLinks) return; burger.addEventListener('click', (e) => { e.stopPropagation(); const isOpen = navLinks.classList.toggle('nav--open'); burger.setAttribute('aria-expanded', isOpen); document.body.style.overflow = isOpen ? 'hidden' : ''; burger.innerHTML = isOpen ? iconX() : iconMenu(); }); // Close on link click navLinks.querySelectorAll('a').forEach(link => { link.addEventListener('click', () => { navLinks.classList.remove('nav--open'); burger.setAttribute('aria-expanded','false'); document.body.style.overflow = ''; burger.innerHTML = iconMenu(); }); }); // Close on outside click document.addEventListener('click', (e) => { if (!burger.contains(e.target) && !navLinks.contains(e.target)) { navLinks.classList.remove('nav--open'); burger.setAttribute('aria-expanded','false'); document.body.style.overflow = ''; burger.innerHTML = iconMenu(); } }); } function iconMenu() { return ``; } function iconX() { return ``; } /* ══════════════════════════════════════ ACTIVE NAV LINK ══════════════════════════════════════ */ function initActiveNav() { const path = window.location.pathname; const file = path.split('/').pop() || 'index.html'; document.querySelectorAll('.nav__links a').forEach(a => { const href = a.getAttribute('href'); if (href && (href === file || (file === '' && href === 'index.html'))) { a.classList.add('active'); } }); } /* ══════════════════════════════════════ STICKY NAV SHADOW ══════════════════════════════════════ */ function initStickyNav() { const nav = document.querySelector('.nav'); if (!nav) return; const update = () => nav.classList.toggle('scrolled', window.scrollY > 30); window.addEventListener('scroll', update, { passive: true }); update(); } /* ══════════════════════════════════════ SCROLL REVEAL ══════════════════════════════════════ */ function initReveal() { const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); io.unobserve(e.target); } }); }, { threshold: 0.06, rootMargin: '0px 0px -24px 0px' }); // Individual reveal elements document.querySelectorAll('.reveal').forEach(el => io.observe(el)); // Stagger groups (observe the container, trigger class on it) document.querySelectorAll('.stagger').forEach(el => io.observe(el)); } /* ══════════════════════════════════════ SMOOTH ANCHOR SCROLL ══════════════════════════════════════ */ function initAnchorScroll() { document.querySelectorAll('a[href^="#"]').forEach(a => { a.addEventListener('click', (e) => { const id = a.getAttribute('href'); const target = document.querySelector(id); if (target) { e.preventDefault(); const top = target.getBoundingClientRect().top + window.scrollY - 76; window.scrollTo({ top, behavior: 'smooth' }); } }); }); } /* ══════════════════════════════════════ FORM HANDLER ══════════════════════════════════════ */ const formShownAt = Date.now(); function initForms() { document.querySelectorAll('.intake-form').forEach(form => { form.addEventListener('submit', async (e) => { e.preventDefault(); const btn = form.querySelector('[type="submit"]'); const originalText = btn.textContent; btn.textContent = 'Sending…'; btn.disabled = true; // Collect all named fields const data = {}; new FormData(form).forEach((val, key) => { data[key] = val; }); // Spam check: how long the visitor took to fill the form (measured on their own clock) data.fill_ms = Date.now() - formShownAt; try { const res = await fetch('/contact.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const json = await res.json(); if (json.ok) { form.innerHTML = `

Sent.

We'll be back within one business day with a clear recommendation and price.

`; } else { throw new Error(json.error || 'Send failed'); } } catch (err) { // Show inline error — don't wipe the form let errEl = form.querySelector('.form-error'); if (!errEl) { errEl = document.createElement('p'); errEl.className = 'form-error'; errEl.style.cssText = 'color:#E8352A;font-size:14px;margin-top:0.75rem;'; form.querySelector('.intake-form__actions').appendChild(errEl); } errEl.textContent = err.message || 'Something went wrong. Please email project@help4bis.com directly.'; btn.textContent = originalText; btn.disabled = false; } }); }); } /* ══════════════════════════════════════ BACK TO TOP ══════════════════════════════════════ */ function initBackToTop() { const btn = document.getElementById('back-to-top'); if (!btn) return; window.addEventListener('scroll', () => { btn.classList.toggle('visible', window.scrollY > 500); }, { passive: true }); btn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' })); } /* ══════════════════════════════════════ INIT ══════════════════════════════════════ */ document.addEventListener('DOMContentLoaded', () => { initDropdowns(); initMobileNav(); initActiveNav(); initStickyNav(); initReveal(); initAnchorScroll(); initForms(); initBackToTop(); });