/* Quantifind Classic IX2 replacements. Native Webflow owns nav/dropdown input. */ (() => { 'use strict'; if (window.qfInteractions) return; // The former FAQ embed queues a Webflow callback guarded by this flag. // This replacement owns that behavior, including keyboard and ARIA state. window.faqAccordionInit = true; const reduced = matchMedia('(prefers-reduced-motion: reduce)'); const questions = new WeakSet(); const dropdowns = new WeakSet(); let mountedNavs = new Set(); const nativeDurations = new WeakMap(); const frames = new WeakMap(); let nextId = 0; let scheduled = false; let ready = false; function answerFor(question) { const answer = question.nextElementSibling; return answer && answer.matches('.faq_answer') ? answer : null; } function setOpen(question, open, animate = true) { const answer = answerFor(question); if (!answer) return; const pending = frames.get(answer); if (pending) cancelAnimationFrame(pending); const currentHeight = answer.getBoundingClientRect().height; question.classList.toggle('open', open); question.setAttribute('aria-expanded', String(open)); answer.setAttribute('aria-hidden', String(!open)); answer.inert = !open; if (question.closest('[data-background-color="dark"]')) { question.closest('.faq_accordion')?.classList.toggle('is-open', open); } if (!animate || reduced.matches) { answer.style.height = open ? 'auto' : '0px'; return; } answer.style.height = `${currentHeight}px`; // Commit the starting height before setting a new transition destination. void answer.offsetHeight; frames.set(answer, requestAnimationFrame(() => { frames.delete(answer); answer.style.height = open ? `${answer.scrollHeight}px` : '0px'; })); } function initializeQuestion(question) { if (questions.has(question)) return; const answer = answerFor(question); if (!answer) return; questions.add(question); question.id ||= `qf-faq-question-${++nextId}`; answer.id ||= `${question.id}-answer`; if (question.tagName !== 'BUTTON') { question.setAttribute('role', 'button'); question.tabIndex = 0; } question.setAttribute('aria-controls', answer.id); answer.setAttribute('role', 'region'); answer.setAttribute('aria-labelledby', question.id); answer.addEventListener('transitionend', event => { if (event.target === answer && event.propertyName === 'height' && question.classList.contains('open')) { answer.style.height = 'auto'; } }); setOpen(question, question.classList.contains('open'), false); } function toggleQuestion(question) { initializeQuestion(question); const open = !question.classList.contains('open'); if (open && question.matches('[js-faq-collapse="true"]')) { const component = question.closest('.faq_component') || question.closest('.faq_list'); component?.querySelectorAll('.faq_question.open').forEach(other => { if (other !== question) setOpen(other, false); }); } setOpen(question, open); } function initializeDropdown(toggle) { if (dropdowns.has(toggle)) return; const list = toggle.nextElementSibling; if (!list?.matches('.navbar_dropdown-list')) return; dropdowns.add(toggle); function measure() { list.style.setProperty('--qf-dropdown-height', `${list.scrollHeight}px`); } new MutationObserver(measure).observe(toggle, { attributes: true, attributeFilter: ['class', 'aria-expanded'] }); if ('ResizeObserver' in window && list.firstElementChild) { new ResizeObserver(measure).observe(list.firstElementChild); } measure(); } function scan() { scheduled = false; const navs = [...document.querySelectorAll('[data-sector-nav]')]; const navChanged = navs.length !== mountedNavs.size || navs.some(nav => !mountedNavs.has(nav)); let durationChanged = false; document.querySelectorAll('.w-nav').forEach(nav => { if (!nativeDurations.has(nav)) nativeDurations.set(nav, nav.getAttribute('data-duration')); const original = nativeDurations.get(nav); const duration = reduced.matches ? '0' : original; if (nav.getAttribute('data-duration') !== duration) { if (duration === null) nav.removeAttribute('data-duration'); else nav.setAttribute('data-duration', duration); durationChanged = true; } }); if (ready && (navChanged || durationChanged)) { // A cached sector navbar may have been detached before Webflow's ready. // Reinitialize only the native modules, preserving GSAP interactions. window.Webflow?.require('navbar')?.ready(); window.Webflow?.require('dropdown')?.ready(); } mountedNavs = new Set(navs); document.querySelectorAll('.navbar_dropdown-toggle').forEach(initializeDropdown); document.querySelectorAll('.faq_question').forEach(initializeQuestion); } function scheduleScan() { if (scheduled) return; scheduled = true; requestAnimationFrame(scan); } function init() { mountedNavs = new Set(document.querySelectorAll('[data-sector-nav]')); ready = true; scan(); document.addEventListener('click', event => { const question = event.target.closest('.faq_question'); if (question) toggleQuestion(question); }); document.addEventListener('keydown', event => { const question = event.target.closest('.faq_question'); if (question && (event.key === 'Enter' || event.key === ' ')) { if (question.tagName === 'BUTTON') return; event.preventDefault(); if (!event.repeat) question.click(); } else if (event.key === 'Escape') { const accordion = event.target.closest('.faq_accordion'); const trigger = accordion?.querySelector('.faq_question.open'); if (trigger) { event.preventDefault(); setOpen(trigger, false); trigger.focus(); } } }); new MutationObserver(records => { if (records.some(record => [...record.addedNodes].some(node => node.nodeType === 1 && (node.matches('[data-sector-nav], .faq_question, .navbar_dropdown-toggle') || node.querySelector('[data-sector-nav], .faq_question, .navbar_dropdown-toggle'))))) scheduleScan(); }).observe(document.body, { childList: true, subtree: true }); window.addEventListener('qf-sector-change', scheduleScan); reduced.addEventListener('change', () => { document.querySelectorAll('.faq_question').forEach(question => { setOpen(question, question.classList.contains('open'), false); }); scheduleScan(); }); } window.qfInteractions = { version: '1.0.0' }; window.Webflow = window.Webflow || []; window.Webflow.push(init); })();