'use strict'; const SELECTORS = { navbar: '#navbar', navbarToggle: '#navbarToggle', navbarMenu: '#navbarMenu', productsNavItem: '#productsNavItem', faqItems: '.faq-item', faqQuestion: '.faq-question', faqAnswer: '.faq-answer', fadeInElements: '.fade-in', citationCopy: '.citation-copy', }; const MOBILE_NAV_MQ = window.matchMedia('(max-width: 768px)'); function isMobileNav() { return MOBILE_NAV_MQ.matches; } function initNavbarScroll() { const navbar = document.querySelector(SELECTORS.navbar); if (!navbar) return; const onScroll = () => { if (window.scrollY > 20) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } }; window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); } function closeMobileNavMenu() { const toggle = document.querySelector(SELECTORS.navbarToggle); const menu = document.querySelector(SELECTORS.navbarMenu); const productsItem = document.querySelector(SELECTORS.productsNavItem); if (!toggle || !menu) return; menu.classList.remove('open'); toggle.classList.remove('open'); toggle.setAttribute('aria-expanded', 'false'); if (productsItem) { productsItem.classList.remove('open'); const productsTrigger = productsItem.querySelector('.nav-link'); if (productsTrigger) productsTrigger.setAttribute('aria-expanded', 'false'); productsItem.querySelectorAll('.megamenu-product-item.has-subproducts.open').forEach((item) => { item.classList.remove('open'); }); } } function initMobileMenu() { const toggle = document.querySelector(SELECTORS.navbarToggle); const menu = document.querySelector(SELECTORS.navbarMenu); if (!toggle || !menu) return; toggle.addEventListener('click', () => { const isOpen = menu.classList.toggle('open'); toggle.classList.toggle('open', isOpen); toggle.setAttribute('aria-expanded', String(isOpen)); if (!isOpen) { const productsItem = document.querySelector(SELECTORS.productsNavItem); if (productsItem) { productsItem.classList.remove('open'); const productsTrigger = productsItem.querySelector('.nav-link'); if (productsTrigger) productsTrigger.setAttribute('aria-expanded', 'false'); } } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && menu.classList.contains('open')) { closeMobileNavMenu(); toggle.focus(); } }); document.addEventListener('click', (e) => { if (!menu.contains(e.target) && !toggle.contains(e.target)) { closeMobileNavMenu(); } }); } function initMegaMenu() { const productsItem = document.querySelector(SELECTORS.productsNavItem); if (!productsItem) return; const trigger = productsItem.querySelector('.nav-link'); const megamenu = productsItem.querySelector('.megamenu'); if (!trigger || !megamenu) return; const CLOSE_DELAY_MS = 150; let hideTimer = null; function isPointerInMenu(target) { if (!target || !(target instanceof Node)) return false; return productsItem.contains(target) || megamenu.contains(target); } function cancelClose() { clearTimeout(hideTimer); hideTimer = null; megamenu.classList.remove('is-closing'); } function openMenu() { if (isMobileNav()) return; cancelClose(); productsItem.classList.add('open'); trigger.setAttribute('aria-expanded', 'true'); } function beginClose() { if (isMobileNav()) return; productsItem.classList.remove('open'); trigger.setAttribute('aria-expanded', 'false'); megamenu.classList.add('is-closing'); } function scheduleClose(event) { if (isMobileNav()) return; if (event && isPointerInMenu(event.relatedTarget)) return; clearTimeout(hideTimer); hideTimer = setTimeout(beginClose, CLOSE_DELAY_MS); } megamenu.addEventListener('transitionend', (event) => { if (event.target !== megamenu) return; if (event.propertyName !== 'opacity' && event.propertyName !== 'visibility') return; if (productsItem.classList.contains('open')) return; megamenu.classList.remove('is-closing'); }); productsItem.addEventListener('mouseenter', openMenu); productsItem.addEventListener('mouseleave', scheduleClose); megamenu.addEventListener('mouseenter', openMenu); megamenu.addEventListener('mouseleave', scheduleClose); trigger.addEventListener('click', (e) => { if (!isMobileNav()) return; e.preventDefault(); const isOpen = productsItem.classList.toggle('open'); trigger.setAttribute('aria-expanded', String(isOpen)); }); trigger.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (isMobileNav()) { const isOpen = productsItem.classList.toggle('open'); trigger.setAttribute('aria-expanded', String(isOpen)); return; } const isOpen = productsItem.classList.toggle('open'); if (isOpen) { openMenu(); } else { cancelClose(); beginClose(); } trigger.setAttribute('aria-expanded', String(isOpen)); } if (e.key === 'Escape') { if (isMobileNav()) { productsItem.classList.remove('open'); trigger.setAttribute('aria-expanded', 'false'); return; } cancelClose(); beginClose(); } }); productsItem.querySelectorAll('.megamenu-product-item.has-subproducts').forEach((item) => { const link = item.querySelector('.megamenu-product-link'); if (!link) return; link.addEventListener('click', (e) => { if (!isMobileNav()) return; if (item.classList.contains('open')) return; e.preventDefault(); item.classList.add('open'); }); }); } function initFAQAccordion() { const faqItems = document.querySelectorAll(SELECTORS.faqItems); faqItems.forEach((item) => { const question = item.querySelector(SELECTORS.faqQuestion); const answer = item.querySelector(SELECTORS.faqAnswer); if (!question || !answer) return; question.addEventListener('click', () => { const isOpen = item.classList.contains('open'); faqItems.forEach((other) => { if (other !== item) { other.classList.remove('open'); const otherQuestion = other.querySelector(SELECTORS.faqQuestion); const otherAnswer = other.querySelector(SELECTORS.faqAnswer); if (otherQuestion) otherQuestion.setAttribute('aria-expanded', 'false'); if (otherAnswer) otherAnswer.hidden = true; } }); item.classList.toggle('open', !isOpen); question.setAttribute('aria-expanded', String(!isOpen)); answer.hidden = isOpen; }); }); } function initIntersectionObserver() { const elements = document.querySelectorAll(SELECTORS.fadeInElements); if (!elements.length) return; if (!('IntersectionObserver' in window)) { elements.forEach((el) => el.classList.add('visible')); return; } const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' } ); elements.forEach((el, index) => { el.style.transitionDelay = `${index * 0.06}s`; observer.observe(el); }); } function initReadMore() { var chevronSVG = ''; document.querySelectorAll('[data-readmore]').forEach(function (el) { var threshold = parseInt(el.getAttribute('data-readmore'), 10) || 120; var fullHeight = el.scrollHeight; if (fullHeight <= threshold + 12) return; var storedHeight = fullHeight; el.classList.add('readmore-wrap', 'is-clamped'); el.style.maxHeight = threshold + 'px'; var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'readmore-btn'; btn.setAttribute('aria-expanded', 'false'); btn.innerHTML = 'Read more ' + chevronSVG; el.insertAdjacentElement('afterend', btn); btn.addEventListener('click', function () { var collapsed = el.classList.toggle('is-clamped'); if (collapsed) { el.style.maxHeight = threshold + 'px'; btn.innerHTML = 'Read more ' + chevronSVG; btn.setAttribute('aria-expanded', 'false'); btn.classList.remove('is-open'); } else { el.style.maxHeight = storedHeight + 'px'; btn.innerHTML = 'Read less ' + chevronSVG; btn.setAttribute('aria-expanded', 'true'); btn.classList.add('is-open'); } }); }); } function initCitationCopy() { const copyButtons = document.querySelectorAll(SELECTORS.citationCopy); if (!copyButtons.length) return; copyButtons.forEach((btn) => { const url = btn.getAttribute('data-citation-url'); const feedback = btn.querySelector('.citation-copy-feedback'); if (!url || !feedback) return; let resetTimer = null; btn.addEventListener('click', async () => { clearTimeout(resetTimer); try { await navigator.clipboard.writeText(url); } catch { const textarea = document.createElement('textarea'); textarea.value = url; textarea.setAttribute('readonly', ''); textarea.style.position = 'fixed'; textarea.style.left = '-9999px'; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); } feedback.hidden = false; btn.classList.add('is-copied'); btn.setAttribute('aria-label', 'Link copied'); resetTimer = setTimeout(() => { feedback.hidden = true; btn.classList.remove('is-copied'); btn.setAttribute('aria-label', 'Copy citation link'); }, 2000); }); }); } function initProjectFilters() { document.querySelectorAll('[data-projects-section]').forEach((section) => { const buttons = section.querySelectorAll('[data-project-filter]'); const cards = section.querySelectorAll('[data-project-status]'); if (!buttons.length || !cards.length) return; const applyFilter = (filter) => { buttons.forEach((btn) => { const isActive = btn.getAttribute('data-project-filter') === filter; btn.classList.toggle('is-active', isActive); btn.setAttribute('aria-selected', String(isActive)); }); cards.forEach((card) => { const status = card.getAttribute('data-project-status') || ''; const visible = filter === 'all' || status === filter; card.classList.toggle('is-hidden', !visible); }); }; buttons.forEach((btn) => { btn.addEventListener('click', () => { applyFilter(btn.getAttribute('data-project-filter') || 'all'); }); }); }); } function init() { initNavbarScroll(); initMobileMenu(); initMegaMenu(); initFAQAccordion(); initIntersectionObserver(); initReadMore(); initCitationCopy(); initProjectFilters(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); }