(function () { 'use strict'; var VERSION = 116; var LINK_SHIFT_X = '0.8em'; var NAV_ENTER_DUR = 0.62; var LINK_OUT_DUR = 0.34; var LINK_IN_DUR = 0.34; var LINK_OUT_Y = -16; var LINK_IN_Y = -16; var LINK_STAGGER_OUT = { each: 0.032, from: 'start' }; var LINK_STAGGER_IN = { each: 0.032, from: 'end' }; var MENU_IN_DUR = 0.46; var MENU_OUT_DUR = 0.36; var MENU_TOGGLE_DUR = 0.16; var EXPAND_REVERSE_SPEED = 1.5; var MENU_IN_Y = 10; var MENU_OUT_Y = -8; var MENU_REVEAL_AFTER_LINKS = 2; var EXPAND_LINKS_IN_AT = 0.32; var MENU_LINKS_COLLAPSE_DUR = 0.2; var DESKTOP_EXPAND_AT = 0.22; var DESKTOP_LINK_EASE_OUT = 'expo.out'; var DESKTOP_LINK_EASE_IN = 'expo.in'; var DESKTOP_MORPH_EASE = 'power2.out'; var DESKTOP_HEIGHT_EASE = 'power2.inOut'; var BLOCK_COLLAPSE_DUR = 0.5; var BLOCK_EXPAND_DUR = 0.54; var MOBILE_ANIM_DUR = 0.65; var MOBILE_MENU_IN_DUR = MOBILE_ANIM_DUR; var MOBILE_MENU_OUT_DUR = MOBILE_ANIM_DUR; var MOBILE_OVERLAY_IN_DUR = MOBILE_ANIM_DUR; var MOBILE_OVERLAY_OUT_DUR = MOBILE_ANIM_DUR; var MOBILE_BLOCK_COLLAPSE_DUR = MOBILE_ANIM_DUR; var MOBILE_BLOCK_EXPAND_DUR = MOBILE_ANIM_DUR; var SCROLL_LOCK_PAD_MS = 100; var DESKTOP_EASE = 'power2.inOut'; var DESKTOP_EASE_OUT = 'power2.out'; var DESKTOP_EASE_IN = 'power2.in'; var MOBILE_EASE = 'power2.inOut'; var MOBILE_EASE_OUT = 'power2.out'; var MOBILE_EASE_IN = 'power2.in'; var GSAP_URL = (window.MENU_SCROLL_COLLAPSE_CONFIG && window.MENU_SCROLL_COLLAPSE_CONFIG.gsapUrl) || 'https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/gsap.min.js'; if (window.__menuScrollCollapseVersion === VERSION) return; window.__menuScrollCollapseVersion = VERSION; var CFG = window.MENU_SCROLL_COLLAPSE_CONFIG || {}; var DESKTOP_MQ = CFG.desktopMq || CFG.enabledMq || '(min-width: 768px)'; var MOBILE_MQ = CFG.mobileMq || '(max-width: 767px)'; var TOP_EXPAND = CFG.topExpandPx != null ? CFG.topExpandPx : 32; var MODAL_OPEN_SELECTOR = CFG.modalOpenSelector != null ? CFG.modalOpenSelector : '.archive_lightbox.is-open'; var NATIVE_DIR_DELTA = CFG.nativeDirDelta != null ? CFG.nativeDirDelta : 2; var LENIS_VEL_MIN = CFG.lenisVelMin != null ? CFG.lenisVelMin : 0.05; var MOBILE_DIR_THRESHOLD = CFG.mobileDirThreshold != null ? CFG.mobileDirThreshold : 18; var DESKTOP_DIR_THRESHOLD = CFG.desktopDirThreshold != null ? CFG.desktopDirThreshold : 16; var PATH_MATCH = CFG.pathMatch; var DEFAULT_DESKTOP_EXCLUDE = ['/', '/work']; var DEFAULT_MOBILE_SCROLL_EXCLUDE = ['/', '/work']; var DESKTOP_EXCLUDE = CFG.excludePaths != null ? CFG.excludePaths : DEFAULT_DESKTOP_EXCLUDE; var MOBILE_SCROLL_EXCLUDE = CFG.mobileScrollExcludePaths != null ? CFG.mobileScrollExcludePaths : CFG.mobileExcludePaths != null ? CFG.mobileExcludePaths : DEFAULT_MOBILE_SCROLL_EXCLUDE; var desktopMq = window.matchMedia(DESKTOP_MQ); var mobileMq = window.matchMedia(MOBILE_MQ); var nav = null; var menuLinks = null; var bottomSide = null; var burger = null; var mobileMenu = null; var mobileOverlay = null; var mobileMenuOpen = false; var mobileMenuTl = null; var mobileTouchHandled = false; var lastY = 0; var collapsed = false; var forceExpanded = false; var heightsCached = false; var ready = false; var bootstrapped = false; var resizeTimer = 0; var desktopClickBound = false; var mobileClickBound = false; var closingMobileMenu = false; var lenisScrollBound = false; var overlayTrackRaf = 0; var scrollAnimLocked = false; var mobileNavAnimLocked = false; var mobileScrollAccum = 0; var desktopScrollAccum = 0; var mobilePageScrollLocked = false; var gsapLoadPromise = null; var sizeCache = { bottom: 0, bottomMarginTop: 0, bottomPaddingTop: 0, bottomPaddingBottom: 0, linksExpanded: 0, linksCollapsed: 0, navLinkHeights: [], menuLinksGap: 0 }; function gsapRef() { return window.gsap || null; } function loadGsap() { if (gsapRef()) return Promise.resolve(gsapRef()); if (gsapLoadPromise) return gsapLoadPromise; gsapLoadPromise = new Promise(function (resolve, reject) { var script = document.createElement('script'); script.src = GSAP_URL; script.async = true; script.onload = function () { if (gsapRef()) resolve(gsapRef()); else reject(new Error('GSAP failed to load')); }; script.onerror = function () { reject(new Error('GSAP script error')); }; document.head.appendChild(script); }); return gsapLoadPromise; } function bindDesktopClick() { if (!burger || desktopClickBound || !desktopMq.matches) return; burger.addEventListener('click', onMenuClickDesktop, true); desktopClickBound = true; } function bindMobileMenuClick() { if (!burger || mobileClickBound) return; burger.addEventListener('touchend', onMenuTouchMobile, { passive: false }); burger.addEventListener('click', onMenuClickMobile, true); mobileClickBound = true; } function getScrollY() { if (window.lenis && typeof window.lenis.scroll === 'number') { return window.lenis.scroll; } return window.scrollY || window.pageYOffset || 0; } function getMode() { if (desktopMq.matches) return 'desktop'; if (mobileMq.matches) return 'mobile'; return null; } function isMobileMode() { return getMode() === 'mobile'; } function resetScrollDirectionState() { mobileScrollAccum = 0; desktopScrollAccum = 0; } function getScrollDirection(y) { var dy = y - lastY; if (Math.abs(dy) < NATIVE_DIR_DELTA) { if (Math.abs(desktopScrollAccum) > 0) { desktopScrollAccum *= 0.5; if (Math.abs(desktopScrollAccum) < 1) desktopScrollAccum = 0; } return 0; } if (lenisScrollBound && window.lenis && !collapsed) { var vel = Math.abs(window.lenis.velocity || 0); if (vel >= LENIS_VEL_MIN) { var lenisDir = window.lenis.direction || 0; if (lenisDir === 1 && dy < 0) return 0; if (lenisDir === -1 && dy > 0) return 0; } } desktopScrollAccum += dy; if (desktopScrollAccum >= DESKTOP_DIR_THRESHOLD) { desktopScrollAccum = 0; return 1; } if (desktopScrollAccum <= -DESKTOP_DIR_THRESHOLD) { desktopScrollAccum = 0; return -1; } return 0; } function getMobileScrollDirection(y) { mobileScrollAccum += y - lastY; if (mobileScrollAccum >= MOBILE_DIR_THRESHOLD) { mobileScrollAccum = 0; return 1; } if (mobileScrollAccum <= -MOBILE_DIR_THRESHOLD) { mobileScrollAccum = 0; return -1; } return 0; } function lockMobileNavAnim() { if (!isMobileMode()) return; mobileNavAnimLocked = true; } function unlockMobileNavAnim() { mobileNavAnimLocked = false; resetScrollDirectionState(); } function readBottomSideMotion() { if (!bottomSide) { return { height: 0, marginTop: 0, paddingTop: 0, paddingBottom: 0, opacity: 0 }; } var cs = window.getComputedStyle(bottomSide); var opacity = gsapRef() ? gsap.getProperty(bottomSide, 'opacity') : parseFloat(cs.opacity); if (typeof opacity !== 'number' || isNaN(opacity)) opacity = parseFloat(cs.opacity) || 0; return { height: bottomSide.offsetHeight || 0, marginTop: parseFloat(cs.marginTop) || 0, paddingTop: parseFloat(cs.paddingTop) || 0, paddingBottom: parseFloat(cs.paddingBottom) || 0, opacity: opacity }; } function isMobileMenuOpen() { if (!isMobileMode()) return false; return mobileMenuOpen; } function neutralizeWebflowMenu(g) { if (!mobileMenu || !g) return; g.set(mobileMenu, { transform: 'none', maxHeight: 'none', clearProps: 'transform,maxHeight,scale,rotate,rotateX,rotateY,rotateZ,skewX,skewY' }); var nodes = mobileMenu.querySelectorAll('*'); if (nodes.length) g.set(nodes, { clearProps: 'transform' }); } function measureMobileMenuHeight() { if (!mobileMenu) return 0; var g = gsapRef(); if (!g) return mobileMenu.scrollHeight || 0; neutralizeWebflowMenu(g); g.set(mobileMenu, { height: 'auto', display: 'flex', overflow: 'visible', visibility: 'hidden', position: 'absolute', opacity: 0, transform: 'none' }); var h = mobileMenu.offsetHeight; g.set(mobileMenu, { clearProps: 'height,display,overflow,visibility,position,opacity' }); return h; } function killMobileMenuTweens() { var g = gsapRef(); if (!g) return; if (mobileMenuTl) { mobileMenuTl.kill(); mobileMenuTl = null; } if (mobileMenu) { g.killTweensOf(mobileMenu); g.killTweensOf(mobileMenu.children); } if (mobileOverlay) g.killTweensOf(mobileOverlay); } function getNavLinks(excludeBurger) { if (!menuLinks) return []; var all = menuLinks.querySelectorAll('.h_link'); if (!excludeBurger) return Array.prototype.slice.call(all); return Array.prototype.filter.call(all, function (el) { return !el.classList.contains('menu_hamburger'); }); } function isNavLinkCurrent(link) { return ( link.classList.contains('w--current') || link.getAttribute('aria-current') === 'page' || link.classList.contains('is-nav-link-current') ); } function navLinkShiftX(link) { return isNavLinkCurrent(link) ? LINK_SHIFT_X : 0; } function prepNavLinksForMotion(g, navLinks, opts) { if (!g || !navLinks.length) return; opts = opts || {}; navLinks.forEach(function (link) { var props = { display: opts.display != null ? opts.display : 'flex', autoAlpha: opts.opacity != null ? opts.opacity : 1, x: opts.shift === false ? 0 : navLinkShiftX(link), y: opts.y != null ? opts.y : 0 }; g.set(link, props); }); } function setNavLinksMotion(g, navLinks, props) { if (!g || !navLinks.length) return; g.set(navLinks, props); } function syncCurrentNavLinkMarkers() { if (!menuLinks) return; getNavLinks(true).forEach(function (link) { var isCurrent = link.classList.contains('w--current') || link.getAttribute('aria-current') === 'page'; link.classList.toggle('is-nav-link-current', isCurrent); }); } function scheduleNavEnterAnimation() { if (!nav || isMobileMode() || collapsed) return; clearTimeout(nav._navEnterScheduleTimer); nav._navEnterScheduleTimer = setTimeout(function () { nav._navEnterScheduleTimer = null; playNavEnterAnimation(); }, 48); } function playNavEnterAnimation() { if (!nav || isMobileMode() || collapsed) return; syncCurrentNavLinkMarkers(); var g = gsapRef(); if (!g) return; var currentLinks = getNavLinks(true).filter(isNavLinkCurrent); if (!currentLinks.length) { nav.classList.remove('is-nav-pending-enter'); return; } if (nav._navEnterTl) { nav._navEnterTl.kill(); nav._navEnterTl = null; } clearTimeout(nav._navEnterTimer); nav.classList.add('is-nav-pending-enter'); currentLinks.forEach(function (link) { g.killTweensOf(link); g.set(link, { x: 0, y: 0, autoAlpha: 1, clearProps: 'transform' }); var arrow = link.querySelector('.arrow_menu_nav'); if (arrow) { g.killTweensOf(arrow); g.set(arrow, { opacity: 0, x: '-0.35em', yPercent: -50, clearProps: 'transform' }); } }); nav._navEnterTl = g.timeline({ onComplete: function () { nav._navEnterTl = null; nav.classList.remove('is-nav-pending-enter'); currentLinks.forEach(function (link) { g.set(link, { clearProps: 'transform,x,y' }); var arrow = link.querySelector('.arrow_menu_nav'); if (arrow) g.set(arrow, { clearProps: 'all' }); }); } }); nav._navEnterTl .to(currentLinks, { x: LINK_SHIFT_X, duration: NAV_ENTER_DUR, ease: DESKTOP_LINK_EASE_OUT }, 0) .to( currentLinks.map(function (link) { return link.querySelector('.arrow_menu_nav'); }).filter(Boolean), { opacity: 1, x: 0, duration: NAV_ENTER_DUR, ease: DESKTOP_LINK_EASE_OUT }, 0 ); } function getLinkMotionEl(link) { if (!link) return null; return link.querySelector('.txt') || link; } function getNavLinkMotionTargets() { return getNavLinks(true) .map(getLinkMotionEl) .filter(Boolean); } function getBurgerMotionEl() { return getLinkMotionEl(burger); } function getBottomKids() { return bottomSide ? Array.prototype.slice.call(bottomSide.children) : []; } function clearBottomMotion(g) { if (!bottomSide || !g) return; var kids = getBottomKids(); if (kids.length) g.set(kids, { clearProps: 'opacity,transform' }); var txts = bottomSide.querySelectorAll('.txt'); if (txts.length) g.set(txts, { clearProps: 'transform,opacity' }); } function isMobileMenuBusy() { return ( mobileMenuOpen || closingMobileMenu || (nav && nav.classList.contains('is-mobile-menu-animating')) ); } function lockMobilePageScroll() { if (!isMobileMode() || mobilePageScrollLocked) return; mobilePageScrollLocked = true; document.documentElement.classList.add('menu-mobile-scroll-lock'); document.body.classList.add('menu-mobile-scroll-lock'); if (window.lenis && typeof window.lenis.stop === 'function') window.lenis.stop(); } function unlockMobilePageScroll() { if (!mobilePageScrollLocked) return; mobilePageScrollLocked = false; document.documentElement.classList.remove('menu-mobile-scroll-lock'); document.body.classList.remove('menu-mobile-scroll-lock'); if (window.lenis && typeof window.lenis.start === 'function') window.lenis.start(); } function lockScrollAnim(ms) { if (!nav || isMobileMode()) return; scrollAnimLocked = true; resetScrollDirectionState(); clearTimeout(nav._scrollAnimLockTimer); nav._scrollAnimLockTimer = setTimeout(function () { scrollAnimLocked = false; lastY = getScrollY(); resetScrollDirectionState(); }, ms); } function unlockScrollAnimEarly() { scrollAnimLocked = false; if (nav) clearTimeout(nav._scrollAnimLockTimer); } function freezeNavAnim() { if (!nav || !gsapRef()) return; var g = gsapRef(); if (nav._menuTl) { nav._menuTl.kill(); nav._menuTl = null; } if (nav._menuContentTl) { nav._menuContentTl.kill(); nav._menuContentTl = null; } var targets = [bottomSide, burger]; if (menuLinks) targets.push(menuLinks); targets = targets.concat(getNavLinks(true)); if (getBurgerMotionEl()) targets.push(getBurgerMotionEl()); if (bottomSide) targets = targets.concat(getBottomKids()); g.killTweensOf(targets); nav.classList.remove('is-menu-animating'); clearTimeout(nav._scrollAnimLockTimer); scrollAnimLocked = false; } function readMenuLinksHeight() { if (!menuLinks) return 0; var h = gsap.getProperty(menuLinks, 'height'); if (typeof h === 'number') return h; return menuLinks.offsetHeight || 0; } function pinMenuLinksMotionHeight(g, height) { if (!g || !menuLinks || height == null) return; g.set(menuLinks, { height: height, overflow: 'hidden' }); } function isDesktopMidCollapse() { if (isMobileMode() || !menuLinks) return false; var navLinks = getNavLinks(true); if (!navLinks.length) return false; var linksVisible = navLinks.some(function (link) { return window.getComputedStyle(link).display !== 'none'; }); var height = readMenuLinksHeight(); return linksVisible && height < sizeCache.linksExpanded * 0.98; } function isDesktopMidExpand() { if (isMobileMode() || !menuLinks) return false; var navLinks = getNavLinks(true); if (!navLinks.length) return false; var linksPartial = navLinks.some(function (link) { var opacity = gsap.getProperty(link, 'opacity'); return opacity < 0.98; }); var height = readMenuLinksHeight(); return linksPartial && height > sizeCache.linksCollapsed * 1.02; } function getInitialCollapsedState(y) { if (isMobileMode()) return true; return scrollShouldCollapse(y); } function isMobileExpandPage() { return isMobileMode() && pathScrollExcluded(); } function getShouldCollapse(y) { if (isMobileExpandPage()) return false; return scrollCollapseEnabled() && getInitialCollapsedState(y) && !forceExpanded; } function resolveLinkPath(href) { if (!href || href.charAt(0) === '#') return null; try { var url = new URL(href, location.href); if (url.origin !== location.origin) return null; return normalizePath(url.pathname); } catch (err) { return null; } } function isMobileScrollCollapsePath(path) { if (!path) return false; if (!MOBILE_SCROLL_EXCLUDE || !MOBILE_SCROLL_EXCLUDE.length) return true; return !MOBILE_SCROLL_EXCLUDE.some(function (entry) { return pathMatchesExcludeEntry(path, entry); }); } function willDestinationCollapse(href) { if (!isMobileMode()) return false; return isMobileScrollCollapsePath(resolveLinkPath(href)); } function isModifiedLinkClick(e) { return ( e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || (typeof e.button === 'number' && e.button !== 0) ); } function estimateAnimLockMs(expanding) { if (isMobileMode()) { return Math.round( (expanding ? MOBILE_BLOCK_EXPAND_DUR : MOBILE_BLOCK_COLLAPSE_DUR) * 1000 + SCROLL_LOCK_PAD_MS ); } var count = getNavLinks(true).length; var linkSpan = getLinksStaggerDuration(count, LINK_OUT_DUR, LINK_STAGGER_OUT.each); var contentEnd = Math.max(linkSpan + 0.02 + MENU_LINKS_COLLAPSE_DUR, linkSpan * 0.55 + getBlockCollapseDur() + 0.28); var total = contentEnd + 0.02 + MENU_TOGGLE_DUR; if (expanding) total = total / EXPAND_REVERSE_SPEED; return Math.round(total * 1000 + SCROLL_LOCK_PAD_MS); } function configureGsap() { var g = gsapRef(); if (!g || g._menuScrollCollapseReady) return; g.defaults({ ease: DESKTOP_EASE_OUT, overwrite: 'auto' }); g.config({ force3D: true }); g._menuScrollCollapseReady = true; } function readBottomSpacing() { if (!bottomSide) return; var cs = window.getComputedStyle(bottomSide); sizeCache.bottomMarginTop = parseFloat(cs.marginTop) || 0; sizeCache.bottomPaddingTop = parseFloat(cs.paddingTop) || 0; sizeCache.bottomPaddingBottom = parseFloat(cs.paddingBottom) || 0; } function measureCollapsedLinksHeight() { if (!burger) return 0; var prev = burger.style.display; burger.style.display = 'inline-flex'; var h = burger.offsetHeight; burger.style.display = prev; return h; } function clearDesktopLinkHover() { if (!nav) return; nav.classList.remove('is-nav-link-hover'); if (!menuLinks) return; menuLinks._hh = null; menuLinks.querySelectorAll('.is-nav-link-hovered').forEach(function (l) { l.classList.remove('is-nav-link-hovered'); }); } function setDesktopLinkHover(link) { if (!nav || !menuLinks || !link || menuLinks._hh === link) return; menuLinks._hh = link; nav.classList.add('is-nav-link-hover'); getNavLinks(true).forEach(function (l) { l.classList.toggle('is-nav-link-hovered', l === link); }); } function bindDesktopLinkHover() { if (!menuLinks || menuLinks._dh) return; menuLinks._dh = true; menuLinks.addEventListener('mouseover', function (e) { if (!isActive() || isMobileMode() || collapsed || nav.classList.contains('is-menu-animating')) return; var link = e.target.closest('.h_link:not(.menu_hamburger)'); if (!link || !menuLinks.contains(link)) return; setDesktopLinkHover(link); }); menuLinks.addEventListener('mouseleave', clearDesktopLinkHover); } function releaseDesktopNavArrows(g) { if (!g || !menuLinks || isMobileMode()) return; var arrows = menuLinks.querySelectorAll('.h_link:not(.menu_hamburger) .arrow_menu_nav'); if (arrows.length) g.set(arrows, { clearProps: 'opacity,visibility,transform' }); } function releaseDesktopLinkOpacity(g) { if (!g || !menuLinks || isMobileMode() || collapsed) return; var navLinks = getNavLinks(true); var motionTargets = getNavLinkMotionTargets(); if (navLinks.length) g.set(navLinks, { autoAlpha: 1, y: 0, clearProps: 'transform' }); if (motionTargets.length) g.set(motionTargets, { clearProps: 'opacity,transform' }); releaseDesktopNavArrows(g); } function restoreLinkDisplay() { if (!menuLinks) return; var g = gsapRef(); var links = menuLinks.querySelectorAll('.h_link'); var txts = menuLinks.querySelectorAll('.h_link .txt'); if (g) { g.set(links, { clearProps: 'display,opacity,transform,visibility,height,padding' }); if (txts.length) g.set(txts, { clearProps: 'transform,opacity' }); } else { links.forEach(function (el) { el.style.display = ''; el.style.opacity = ''; el.style.transform = ''; el.style.visibility = ''; el.style.height = ''; }); txts.forEach(function (el) { el.style.transform = ''; el.style.opacity = ''; }); } } function ensureMobileOverlay() { if (mobileOverlay) return mobileOverlay; mobileOverlay = document.createElement('div'); mobileOverlay.className = 'menu-mobile-overlay'; mobileOverlay.setAttribute('aria-hidden', 'true'); mobileOverlay.addEventListener('click', closeMobileMenu); document.body.appendChild(mobileOverlay); return mobileOverlay; } function updateMobileOverlayBounds() { if (!mobileOverlay || !nav) return; var top = Math.max(0, Math.round(nav.getBoundingClientRect().bottom)); mobileOverlay.style.setProperty('--menu-overlay-top', top + 'px'); } function stopOverlayBoundsTracking() { if (overlayTrackRaf) cancelAnimationFrame(overlayTrackRaf); overlayTrackRaf = 0; } function startOverlayBoundsTracking() { stopOverlayBoundsTracking(); if (!mobileOverlay || !mobileOverlay.classList.contains('is-visible')) return; function tick() { if (!mobileOverlay || !mobileOverlay.classList.contains('is-visible')) { overlayTrackRaf = 0; return; } updateMobileOverlayBounds(); if (nav.classList.contains('is-menu-animating')) { overlayTrackRaf = requestAnimationFrame(tick); return; } overlayTrackRaf = 0; } overlayTrackRaf = requestAnimationFrame(tick); } function setMobileOverlay(open, immediate, timeline, position) { if (!isMobileMode()) { if (mobileOverlay) { stopOverlayBoundsTracking(); if (!timeline && gsapRef()) gsapRef().killTweensOf(mobileOverlay); mobileOverlay.classList.remove('is-visible'); gsapRef() && gsapRef().set(mobileOverlay, { opacity: 0, visibility: 'hidden', pointerEvents: 'none' }); } return; } var g = gsapRef(); if (!g) return; var el = ensureMobileOverlay(); if (!timeline) g.killTweensOf(el); if (open) { updateMobileOverlayBounds(); el.classList.add('is-visible'); el.setAttribute('aria-hidden', 'false'); g.set(el, { visibility: 'visible' }); startOverlayBoundsTracking(); if (immediate) { g.set(el, { opacity: 1, pointerEvents: 'auto' }); return; } if (timeline) { timeline.set(el, { opacity: 0, pointerEvents: 'none' }, position || 0); timeline.to( el, { opacity: 1, duration: MOBILE_OVERLAY_IN_DUR, ease: 'power2.out', onStart: function () { g.set(el, { pointerEvents: 'auto' }); } }, position || 0 ); return; } g.set(el, { opacity: 0, pointerEvents: 'none' }); g.to(el, { opacity: 1, duration: MOBILE_OVERLAY_IN_DUR, ease: 'power2.out', onStart: function () { g.set(el, { pointerEvents: 'auto' }); } }); return; } el.setAttribute('aria-hidden', 'true'); stopOverlayBoundsTracking(); if (immediate) { el.classList.remove('is-visible'); g.set(el, { opacity: 0, visibility: 'hidden', pointerEvents: 'none' }); return; } if (timeline) { timeline.to( el, { opacity: 0, duration: MOBILE_OVERLAY_OUT_DUR, ease: 'power2.in', onComplete: function () { el.classList.remove('is-visible'); g.set(el, { visibility: 'hidden', pointerEvents: 'none' }); } }, position || 0 ); return; } g.to(el, { opacity: 0, duration: MOBILE_OVERLAY_OUT_DUR, ease: 'power2.in', onComplete: function () { el.classList.remove('is-visible'); g.set(el, { visibility: 'hidden', pointerEvents: 'none' }); } }); } function openMobileMenuAnim() { var g = gsapRef(); if (!g || !mobileMenu || !burger || mobileMenuOpen || !isMobileMode()) return; var needsNavExpand = collapsed; if (!heightsCached) measureHeights(false); killMobileMenuTweens(); if (needsNavExpand) freezeNavAnim(); var kids = Array.prototype.slice.call(mobileMenu.children); var motionKids = kids.map(getLinkMotionEl).filter(Boolean); var targetHeight = measureMobileMenuHeight(); var overlay = ensureMobileOverlay(); neutralizeWebflowMenu(g); mobileMenuOpen = true; forceExpanded = true; lockMobilePageScroll(); nav.classList.add('menu-mobile-open', 'is-mobile-menu-animating'); burger.classList.add('is-active'); burger.setAttribute('aria-expanded', 'true'); if (needsNavExpand) { lockMobileNavAnim(); collapsed = false; nav.setAttribute('data-menu-collapsed', 'false'); nav.classList.remove('is-scroll-collapsed'); nav.classList.add('is-scroll-collapse-ready', 'is-menu-animating'); g.set(bottomSide, { height: 0, marginTop: 0, paddingTop: 0, paddingBottom: 0, opacity: 0, overflow: 'hidden', pointerEvents: 'none' }); clearBottomMotion(g); g.set(getBottomKids(), { opacity: 1 }); } g.set(mobileMenu, { display: 'flex', flexDirection: 'column', overflow: 'hidden', height: 0, opacity: 0, pointerEvents: 'auto', transform: 'none', maxHeight: 'none' }); if (motionKids.length) g.set(motionKids, { opacity: 1, clearProps: 'transform' }); mobileMenuTl = g.timeline({ defaults: { ease: MOBILE_EASE, overwrite: 'auto' }, onComplete: function () { if (needsNavExpand) { applyCollapsedVisual(false); finishMenuAnimation(); } g.set(mobileMenu, { clearProps: 'transform' }); if (motionKids.length) g.set(motionKids, { clearProps: 'transform' }); nav.classList.remove('is-mobile-menu-animating'); mobileMenuTl = null; updateMobileOverlayBounds(); } }); updateMobileOverlayBounds(); overlay.classList.add('is-visible'); overlay.setAttribute('aria-hidden', 'false'); g.set(overlay, { visibility: 'visible', opacity: 0, pointerEvents: 'none' }); startOverlayBoundsTracking(); mobileMenuTl.to( overlay, { opacity: 1, duration: MOBILE_OVERLAY_IN_DUR, ease: MOBILE_EASE, onStart: function () { g.set(overlay, { pointerEvents: 'auto' }); } }, 0 ); mobileMenuTl.to( mobileMenu, { height: targetHeight, opacity: 1, duration: MOBILE_MENU_IN_DUR, ease: MOBILE_EASE }, 0 ); if (needsNavExpand) { mobileMenuTl.to( bottomSide, { height: sizeCache.bottom, marginTop: sizeCache.bottomMarginTop, paddingTop: sizeCache.bottomPaddingTop, paddingBottom: sizeCache.bottomPaddingBottom, opacity: 1, pointerEvents: 'auto', duration: MOBILE_BLOCK_EXPAND_DUR, ease: MOBILE_EASE }, 0 ); } } function closeMobileMenuAnim(options) { options = options || {}; var navigateTo = options.navigateTo; var g = gsapRef(); if (!g || !mobileMenu || !burger || !mobileMenuOpen || closingMobileMenu) return false; closingMobileMenu = true; killMobileMenuTweens(); neutralizeWebflowMenu(g); mobileMenuOpen = false; burger.classList.remove('is-active'); burger.setAttribute('aria-expanded', 'false'); nav.classList.add('is-mobile-menu-animating'); var needsNavCollapse = options.forceNavCollapse || (scrollCollapseEnabled() && scrollShouldCollapse(getScrollY())); if (needsNavCollapse) { if (nav._menuTl) { nav._menuTl.kill(); nav._menuTl = null; } g.killTweensOf(bottomSide); collapsed = true; forceExpanded = false; nav.setAttribute('data-menu-collapsed', 'true'); nav.classList.add('is-scroll-collapse-ready', 'is-menu-animating', 'is-scroll-collapsed'); lockMobileNavAnim(); var bottomNow = readBottomSideMotion(); g.set(bottomSide, { height: bottomNow.height || sizeCache.bottom, marginTop: bottomNow.marginTop, paddingTop: bottomNow.paddingTop || sizeCache.bottomPaddingTop, paddingBottom: bottomNow.paddingBottom || sizeCache.bottomPaddingBottom, opacity: bottomNow.opacity || 1, overflow: 'hidden', pointerEvents: 'auto' }); clearBottomMotion(g); g.set(getBottomKids(), { opacity: 1 }); } var kids = Array.prototype.slice.call(mobileMenu.children); var motionKids = kids.map(getLinkMotionEl).filter(Boolean); var overlay = mobileOverlay; mobileMenuTl = g.timeline({ defaults: { ease: MOBILE_EASE, overwrite: 'auto' }, onComplete: function () { g.set(mobileMenu, { display: 'none', height: 0, opacity: 0, pointerEvents: 'none', transform: 'none', clearProps: 'overflow,flexDirection,maxHeight' }); if (motionKids.length) g.set(motionKids, { clearProps: 'opacity,transform' }); if (overlay) { overlay.classList.remove('is-visible'); overlay.setAttribute('aria-hidden', 'true'); g.set(overlay, { opacity: 0, visibility: 'hidden', pointerEvents: 'none' }); } stopOverlayBoundsTracking(); nav.classList.remove('menu-mobile-open', 'is-mobile-menu-animating'); mobileMenuTl = null; closingMobileMenu = false; forceExpanded = false; unlockMobilePageScroll(); if (needsNavCollapse) { applyCollapsedVisual(true); finishMenuAnimation(); } if (navigateTo) { window.location.assign(navigateTo); } } }); if (overlay) { overlay.setAttribute('aria-hidden', 'true'); stopOverlayBoundsTracking(); mobileMenuTl.to( overlay, { opacity: 0, duration: MOBILE_OVERLAY_OUT_DUR, ease: MOBILE_EASE, onStart: function () { g.set(overlay, { pointerEvents: 'none' }); } }, 0 ); } mobileMenuTl.to( mobileMenu, { height: 0, opacity: 0, duration: MOBILE_MENU_OUT_DUR, ease: MOBILE_EASE }, 0 ); if (needsNavCollapse) { mobileMenuTl.to( bottomSide, { height: 0, marginTop: 0, paddingTop: 0, paddingBottom: 0, opacity: 0, pointerEvents: 'none', duration: MOBILE_BLOCK_COLLAPSE_DUR, ease: MOBILE_EASE }, 0 ); } return true; } function closeMobileMenu() { if (!isMobileMode() || !mobileMenuOpen || closingMobileMenu) return; closeMobileMenuAnim(); } function bindMobileOverlayTracking() { if (!nav || nav._menuOverlayResizeObs) return; var onResize = function () { if (mobileOverlay && mobileOverlay.classList.contains('is-visible')) { updateMobileOverlayBounds(); } }; if (typeof ResizeObserver === 'function') { var ro = new ResizeObserver(onResize); ro.observe(nav); if (bottomSide) ro.observe(bottomSide); nav._menuOverlayResizeObs = ro; } } function initMobileMenuPanel() { var g = gsapRef(); if (!mobileMenu || !g) return; neutralizeWebflowMenu(g); g.set(mobileMenu, { display: 'none', height: 0, opacity: 0, overflow: 'hidden', pointerEvents: 'none', transform: 'none', maxHeight: 'none' }); } function bindWebflowMenuStyleGuard() { if (!mobileMenu || mobileMenu._wfStyleGuard) return; var obs = new MutationObserver(function () { if (!mobileMenuOpen && !nav.classList.contains('is-mobile-menu-animating')) return; var g = gsapRef(); if (!g || !mobileMenu.style.transform) return; g.set(mobileMenu, { clearProps: 'transform', transform: 'none' }); }); obs.observe(mobileMenu, { attributes: true, attributeFilter: ['style', 'class'] }); mobileMenu._wfStyleGuard = obs; } function bindMobileMenuLinks() { if (!mobileMenu || mobileMenu._menuLinksBound) return; mobileMenu.querySelectorAll('a').forEach(function (link) { if (link === burger) return; link.addEventListener('click', function (e) { if (!isMobileMode() || !mobileMenuOpen) return; if (isModifiedLinkClick(e)) return; var href = link.getAttribute('href'); if (!href || href.charAt(0) === '#') return; if (!resolveLinkPath(href)) { closeMobileMenuAnim(); return; } e.preventDefault(); var collapseDest = willDestinationCollapse(href); if (!closeMobileMenuAnim({ navigateTo: href, forceNavCollapse: collapseDest })) { window.location.assign(href); } }); }); mobileMenu._menuLinksBound = true; } function bindMobileMenuSync() { mobileMenu = document.querySelector('.menu_mobile'); if (!mobileMenu) return; initMobileMenuPanel(); bindMobileMenuLinks(); bindWebflowMenuStyleGuard(); if (burger) { burger.setAttribute('role', 'button'); burger.setAttribute('aria-expanded', 'false'); if (!mobileMenu.id) mobileMenu.id = 'menu_mobile'; burger.setAttribute('aria-controls', mobileMenu.id); } if (!document._menuMobileEscBound) { document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && isMobileMode() && mobileMenuOpen) closeMobileMenuAnim(); }); document._menuMobileEscBound = true; } } function normalizePath(pathname) { var path = pathname || '/'; if (path.length > 1 && path.charAt(path.length - 1) === '/') { path = path.slice(0, -1); } return path; } function getScrollExcludeList() { return isMobileMode() ? MOBILE_SCROLL_EXCLUDE : DESKTOP_EXCLUDE; } function pathScrollExcluded() { var excludeList = getScrollExcludeList(); if (!excludeList || !excludeList.length) return false; var path = normalizePath(location.pathname); return excludeList.some(function (entry) { return pathMatchesExcludeEntry(path, entry); }); } function pathMatchAllowed() { if (!PATH_MATCH) return true; if (typeof PATH_MATCH === 'function') return PATH_MATCH(location.pathname); if (PATH_MATCH instanceof RegExp) return PATH_MATCH.test(location.pathname); if (Array.isArray(PATH_MATCH)) { return PATH_MATCH.some(function (part) { return location.pathname.indexOf(part) === 0; }); } return true; } function scrollCollapseEnabled() { return isActive() && pathMatchAllowed() && !pathScrollExcluded(); } function pathMatchesExcludeEntry(path, entry) { if (typeof entry === 'function') return entry(path, location.pathname); if (entry instanceof RegExp) return entry.test(location.pathname); var normalized = normalizePath(entry); if (normalized === '/') return path === '/'; return path === normalized; } function isActive() { return !!getMode() && !!nav; } function scrollShouldCollapse(y) { return y > TOP_EXPAND; } function getBlockCollapseDur() { return isMobileMode() ? MOBILE_BLOCK_COLLAPSE_DUR : BLOCK_COLLAPSE_DUR; } function getBlockExpandDur() { return isMobileMode() ? MOBILE_BLOCK_EXPAND_DUR : BLOCK_EXPAND_DUR; } function killMenuTweens() { var g = gsapRef(); if (!g || !nav) return; if (nav._menuTl) { nav._menuTl.kill(); nav._menuTl = null; } if (nav._menuContentTl) { nav._menuContentTl.kill(); nav._menuContentTl = null; } var targets = [bottomSide, burger]; if (menuLinks) targets.push(menuLinks); targets = targets.concat(getNavLinks(true)); targets = targets.concat(getNavLinkMotionTargets()); if (getBurgerMotionEl()) targets.push(getBurgerMotionEl()); if (bottomSide) targets = targets.concat(Array.prototype.slice.call(bottomSide.children)); if (!mobileMenuOpen && !nav.classList.contains('is-mobile-menu-animating')) { if (mobileOverlay) targets.push(mobileOverlay); if (mobileMenu) { targets.push(mobileMenu); targets = targets.concat(Array.prototype.slice.call(mobileMenu.children)); } if (mobileMenuTl) { mobileMenuTl.kill(); mobileMenuTl = null; } } g.killTweensOf(targets); } function cancelMenuAnimation() { if (!nav) return; clearTimeout(nav._navEnterScheduleTimer); nav._navEnterScheduleTimer = null; if (nav._navEnterTl) { nav._navEnterTl.kill(); nav._navEnterTl = null; } clearTimeout(nav._menuAnimTimer); killMenuTweens(); nav.classList.remove('is-menu-animating'); nav._menuAnimTimer = null; unlockMobileNavAnim(); } function finishMenuAnimation() { if (!nav) return; nav.classList.remove('is-menu-animating'); nav._menuTl = null; nav._menuContentTl = null; clearTimeout(nav._menuAnimTimer); nav._menuAnimTimer = null; unlockMobileNavAnim(); if (!collapsed && gsapRef()) { restoreLinkDisplay(); if (isMobileMode()) clearBottomMotion(gsapRef()); else releaseDesktopLinkOpacity(gsapRef()); } syncCurrentNavLinkMarkers(); if (mobileOverlay && mobileOverlay.classList.contains('is-visible')) { updateMobileOverlayBounds(); } } function readElementHeight(el) { if (!el) return 0; el.style.height = 'auto'; var h = el.offsetHeight; el.style.height = ''; return h; } function measureBottomSideExpandedSilent() { if (!bottomSide) return; nav.classList.add('is-menu-measuring'); var style = bottomSide.style; var saved = { position: style.position, visibility: style.visibility, left: style.left, height: style.height, marginTop: style.marginTop, paddingTop: style.paddingTop, paddingBottom: style.paddingBottom, opacity: style.opacity, overflow: style.overflow, pointerEvents: style.pointerEvents }; style.position = 'absolute'; style.visibility = 'hidden'; style.left = '-9999px'; style.height = 'auto'; style.marginTop = ''; style.paddingTop = ''; style.paddingBottom = ''; style.opacity = '1'; style.overflow = 'visible'; style.pointerEvents = 'none'; sizeCache.bottom = readElementHeight(bottomSide); readBottomSpacing(); style.position = saved.position; style.visibility = saved.visibility; style.left = saved.left; style.height = saved.height; style.marginTop = saved.marginTop; style.paddingTop = saved.paddingTop; style.paddingBottom = saved.paddingBottom; style.opacity = saved.opacity; style.overflow = saved.overflow; style.pointerEvents = saved.pointerEvents; nav.classList.remove('is-menu-measuring'); } function measureHeights(force, opts) { if (!nav || !bottomSide) return; if (heightsCached && !force) return; opts = opts || {}; if (opts.silentExpanded && isMobileMode()) { killMenuTweens(); var gSilent = gsapRef(); if (gSilent) gSilent.killTweensOf(bottomSide); measureBottomSideExpandedSilent(); heightsCached = true; return; } var g = gsapRef(); var wasCollapsed = nav.classList.contains('is-scroll-collapsed'); killMenuTweens(); nav.classList.add('is-menu-measuring'); nav.classList.remove('is-scroll-collapsed', 'is-menu-animating'); if (g) { g.set([bottomSide, menuLinks], { clearProps: 'height,opacity,transform,margin,padding' }); restoreLinkDisplay(); clearBottomMotion(g); } else { bottomSide.style.height = ''; bottomSide.style.marginTop = ''; bottomSide.style.paddingTop = ''; bottomSide.style.paddingBottom = ''; if (menuLinks) menuLinks.style.height = ''; restoreLinkDisplay(); } sizeCache.bottom = readElementHeight(bottomSide); readBottomSpacing(); if (!isMobileMode() && menuLinks) { cacheExpandedNavLinkMetrics(g); if (g) { g.set(getNavLinks(true), { clearProps: 'display,opacity,transform,visibility' }); g.set(menuLinks, { clearProps: 'height,overflow' }); if (burger) g.set(burger, { clearProps: 'display' }); } } if (wasCollapsed) nav.classList.add('is-scroll-collapsed'); nav.classList.remove('is-menu-measuring'); heightsCached = true; } function applyCollapsedVisual(state) { var g = gsapRef(); if (!g || !nav) return; var navLinks = getNavLinks(true); var bottomKids = getBottomKids(); var motionTargets = getNavLinkMotionTargets(); if (state) { nav.classList.add('is-scroll-collapsed'); g.set(bottomSide, { height: 0, marginTop: 0, paddingTop: 0, paddingBottom: 0, opacity: 0, overflow: 'hidden', pointerEvents: 'none' }); if (isMobileMode()) { clearBottomMotion(g); g.set(bottomKids, { opacity: 1 }); } else { g.set(bottomKids, { opacity: 0 }); } if (!isMobileMode() && menuLinks) { g.set(menuLinks, { height: sizeCache.linksCollapsed, overflow: 'hidden' }); g.set(navLinks, { display: 'none', opacity: 1, clearProps: 'transform' }); if (motionTargets.length) g.set(motionTargets, { clearProps: 'opacity,transform' }); if (burger) { g.set(burger, { display: 'inline-flex', opacity: 1, position: 'absolute', top: 0, left: 0, margin: 0, pointerEvents: 'auto' }); var burgerMotion = getBurgerMotionEl(); if (burgerMotion) g.set(burgerMotion, { clearProps: 'transform' }); } } return; } nav.classList.remove('is-scroll-collapsed'); restoreLinkDisplay(); clearBottomMotion(g); g.set(bottomSide, { height: sizeCache.bottom, marginTop: sizeCache.bottomMarginTop, paddingTop: sizeCache.bottomPaddingTop, paddingBottom: sizeCache.bottomPaddingBottom, opacity: 1, overflow: 'hidden', pointerEvents: 'auto' }); g.set(bottomKids, { opacity: 1 }); if (!isMobileMode() && menuLinks) { g.set(menuLinks, { height: sizeCache.linksExpanded, overflow: 'hidden' }); g.set(navLinks, { display: 'flex', autoAlpha: 1, clearProps: 'transform' }); if (motionTargets.length) g.set(motionTargets, { clearProps: 'opacity,transform' }); releaseDesktopNavArrows(g); } syncCurrentNavLinkMarkers(); } function applyCollapsedInstant(state) { if (!nav) return; cancelMenuAnimation(); collapsed = !!state; nav.classList.add('is-scroll-collapse-ready', 'is-menu-instant'); nav.setAttribute('data-menu-collapsed', collapsed ? 'true' : 'false'); applyCollapsedVisual(collapsed); requestAnimationFrame(function () { nav.classList.remove('is-menu-instant'); if (collapsed || isMobileMode()) { nav.classList.remove('is-nav-pending-enter'); return; } releaseDesktopLinkOpacity(gsapRef()); scheduleNavEnterAnimation(); }); } function getLinksStaggerDuration(count, itemDur, stagger) { if (!count) return 0; return itemDur + Math.max(0, count - 1) * stagger; } function getBurgerRevealAt(linkCount) { if (!linkCount) return 0; var revealAfter = Math.min(MENU_REVEAL_AFTER_LINKS, linkCount); return (revealAfter - 1) * LINK_STAGGER_OUT.each + LINK_OUT_DUR * 0.82; } function ensureExpandedMetrics(g, preserveLinksHeight) { if ( !menuLinks || sizeCache.linksExpanded <= sizeCache.linksCollapsed + 1 || !sizeCache.navLinkHeights.length || !sizeCache.navLinkHeights.some(function (h) { return h > 0; }) ) { cacheExpandedNavLinkMetrics(g, preserveLinksHeight); } } function cacheExpandedNavLinkMetrics(g, preserveMenuLinksHeight) { if (!menuLinks || isMobileMode()) return; var navLinksMeasured = getNavLinks(true); if (!navLinksMeasured.length) return; if (g) { if (burger) g.set(burger, { display: 'none' }); g.set(navLinksMeasured, { display: 'flex', autoAlpha: 1, clearProps: 'transform' }); g.set(menuLinks, { height: 'auto', overflow: 'visible' }); } else { navLinksMeasured.forEach(function (link) { link.style.display = 'flex'; }); menuLinks.style.height = 'auto'; menuLinks.style.overflow = 'visible'; } sizeCache.linksExpanded = readElementHeight(menuLinks); sizeCache.navLinkHeights = measureNavLinkHeights(navLinksMeasured); sizeCache.menuLinksGap = getMenuLinksGap(); sizeCache.linksCollapsed = measureCollapsedLinksHeight(); if (g && preserveMenuLinksHeight != null) { pinMenuLinksMotionHeight(g, preserveMenuLinksHeight); } } function getMenuLinksGap() { if (!menuLinks) return 0; var cs = window.getComputedStyle(menuLinks); var gap = parseFloat(cs.rowGap); if (isNaN(gap) || gap <= 0) gap = parseFloat(cs.gap) || 0; return isNaN(gap) ? 0 : gap; } function measureNavLinkHeights(navLinks) { return navLinks.map(function (link) { return link.offsetHeight || 0; }); } function prepLinkTextsForCollapse(g, navLinks) { prepNavLinksForMotion(g, navLinks, { display: 'flex', opacity: 1, y: 0, shift: true }); var texts = getNavLinkMotionTargets(); if (texts.length) { g.set(texts, { y: 0, autoAlpha: 1, scaleY: 1, transformOrigin: '50% 0%' }); } return texts; } function prepLinkTextsForExpand(g, navLinks) { prepNavLinksForMotion(g, navLinks, { display: 'flex', opacity: 1, y: 0, shift: true }); var texts = getNavLinkMotionTargets(); if (texts.length) { g.set(texts, { y: LINK_OUT_Y, autoAlpha: 0, scaleY: 0.84, transformOrigin: '50% 0%' }); } return texts; } function addDesktopLinkMotion(g, navLinks, tl, direction, startAt) { startAt = startAt || 0; var entering = direction === 'in'; var gap; var heights; var linkSpan = getLinksStaggerDuration(navLinks.length, LINK_OUT_DUR, LINK_STAGGER_OUT.each); var texts; var arrows = []; var remaining; var linksStartH; var i; if (entering) { linksStartH = readMenuLinksHeight() || sizeCache.linksCollapsed; ensureExpandedMetrics(g, linksStartH); prepLinkTextsForExpand(g, navLinks); pinMenuLinksMotionHeight(g, linksStartH); heights = measureNavLinkHeights(navLinks); if (!heights.some(function (h) { return h > 0; })) { cacheExpandedNavLinkMetrics(g, linksStartH); prepLinkTextsForExpand(g, navLinks); pinMenuLinksMotionHeight(g, linksStartH); heights = sizeCache.navLinkHeights.slice(); } else { sizeCache.navLinkHeights = heights; } } else { prepLinkTextsForCollapse(g, navLinks); heights = measureNavLinkHeights(navLinks); } gap = entering ? sizeCache.menuLinksGap || getMenuLinksGap() : getMenuLinksGap(); var linkStagger = entering ? LINK_STAGGER_IN : LINK_STAGGER_OUT; texts = getNavLinkMotionTargets(); navLinks.forEach(function (link) { var arrow = link.querySelector('.arrow_menu_nav'); if (arrow) arrows.push(arrow); }); remaining = entering ? linksStartH : menuLinks.offsetHeight || sizeCache.linksExpanded; if (texts.length) { tl.to( texts, { y: entering ? 0 : LINK_OUT_Y, autoAlpha: entering ? 1 : 0, scaleY: entering ? 1 : 0.84, duration: LINK_OUT_DUR, stagger: linkStagger, ease: DESKTOP_MORPH_EASE }, startAt ); } if (arrows.length) { if (entering) { g.set(arrows, { autoAlpha: 0 }); tl.to( arrows, { autoAlpha: function (index, target) { var link = target.closest('.h_link'); return link && isNavLinkCurrent(link) ? 1 : 0; }, duration: LINK_OUT_DUR * 0.5, stagger: linkStagger, ease: DESKTOP_LINK_EASE_OUT }, startAt ); } else { tl.to( arrows, { autoAlpha: 0, duration: LINK_OUT_DUR * 0.5, stagger: linkStagger, ease: DESKTOP_LINK_EASE_IN }, startAt ); } } var heightStart = startAt + LINK_OUT_DUR * 0.3; var heightDur = Math.max(linkSpan - LINK_OUT_DUR * 0.3, 0.12) + MENU_LINKS_COLLAPSE_DUR; tl.to( menuLinks, { height: entering ? sizeCache.linksExpanded : sizeCache.linksCollapsed, duration: heightDur, ease: DESKTOP_HEIGHT_EASE }, heightStart ); if (entering) { tl.call( function () { if (texts.length) g.set(texts, { clearProps: 'transform,opacity,visibility' }); if (arrows.length) g.set(arrows, { clearProps: 'opacity,visibility,transform' }); }, null, startAt + linkSpan ); } else { tl.set(navLinks, { display: 'none' }, startAt + linkSpan); } return linkSpan; } function addDesktopBurgerIn(g, tl, position) { if (!burger) return; var burgerMotion = getBurgerMotionEl(); tl.to( burger, { autoAlpha: 1, duration: MENU_TOGGLE_DUR, ease: DESKTOP_LINK_EASE_OUT, pointerEvents: 'auto' }, position ); if (burgerMotion) { tl.fromTo( burgerMotion, { y: MENU_IN_Y, autoAlpha: 0 }, { y: 0, autoAlpha: 1, duration: MENU_TOGGLE_DUR, ease: DESKTOP_LINK_EASE_OUT }, position ); } } function addDesktopBurgerOut(g, tl, position) { if (!burger) return; var burgerMotion = getBurgerMotionEl(); g.set(burger, { display: 'inline-flex', autoAlpha: 1, pointerEvents: 'auto', position: 'absolute', top: 0, left: 0, margin: 0 }); if (burgerMotion) g.set(burgerMotion, { y: 0, autoAlpha: 1, clearProps: 'transform' }); tl.to( burger, { autoAlpha: 0, duration: MENU_OUT_DUR, ease: DESKTOP_LINK_EASE_IN, pointerEvents: 'none' }, position ); if (burgerMotion) { tl.to( burgerMotion, { y: MENU_OUT_Y, autoAlpha: 0, duration: MENU_OUT_DUR, ease: DESKTOP_LINK_EASE_IN }, position ); } tl.call( function () { hideBurger(g); }, null, position + MENU_OUT_DUR ); } function buildDesktopCollapseTimeline(g, mid) { var navLinks = getNavLinks(true); prepDesktopMenuMotion(g, false, mid); var tl = g.timeline({ paused: true, defaults: { ease: DESKTOP_EASE, overwrite: 'auto' }, onComplete: function () { applyCollapsedVisual(true); finishMenuAnimation(); }, onReverseComplete: function () { applyCollapsedVisual(false); finishMenuAnimation(); } }); var linkSpan = addDesktopLinkMotion(g, navLinks, tl, 'out', 0); addDesktopBottomBlockMotion(tl, linkSpan, false, 0); addDesktopBurgerIn(g, tl, tl.duration() + 0.02); return tl; } function addDesktopBottomBlockMotion(tl, linkSpan, expanding, phaseStart) { phaseStart = phaseStart || 0; var bottomKids = getBottomKids(); var blockAt = phaseStart + linkSpan * 0.55; var blockDur = expanding ? getBlockExpandDur() : getBlockCollapseDur(); if (bottomKids.length) { tl.to( bottomKids, expanding ? { opacity: 1, y: 0, duration: 0.32, stagger: { each: 0.045, from: 'start' }, ease: DESKTOP_LINK_EASE_OUT } : { opacity: 0, y: -8, duration: 0.32, stagger: 0.045, ease: DESKTOP_LINK_EASE_IN }, blockAt * 0.35 ); } tl.to( bottomSide, expanding ? { height: sizeCache.bottom, marginTop: sizeCache.bottomMarginTop, paddingTop: sizeCache.bottomPaddingTop, paddingBottom: sizeCache.bottomPaddingBottom, duration: blockDur, ease: DESKTOP_MORPH_EASE } : { height: 0, marginTop: 0, paddingTop: 0, paddingBottom: 0, duration: blockDur, ease: DESKTOP_MORPH_EASE }, blockAt ); tl.to( bottomSide, expanding ? { opacity: 1, pointerEvents: 'auto', duration: 0.28, ease: DESKTOP_LINK_EASE_OUT } : { opacity: 0, duration: 0.28, ease: DESKTOP_LINK_EASE_IN }, blockAt + blockDur * 0.35 ); } function prepDesktopMenuMotion(g, expanding, mid) { var navLinks = getNavLinks(true); var bottomKids = getBottomKids(); var currentBottom = bottomSide.offsetHeight || sizeCache.bottom; var currentLinks = menuLinks ? menuLinks.offsetHeight || sizeCache.linksExpanded : 0; var currentMargin = parseFloat(window.getComputedStyle(bottomSide).marginTop) || sizeCache.bottomMarginTop; if (mid && menuLinks) currentLinks = readMenuLinksHeight(); if (mid) { currentBottom = bottomSide.offsetHeight || 0; currentMargin = parseFloat(window.getComputedStyle(bottomSide).marginTop) || 0; } if (expanding) { nav.classList.remove('is-scroll-collapsed'); if (mid) { g.set(bottomSide, { height: currentBottom, marginTop: currentMargin, paddingTop: sizeCache.bottomPaddingTop, paddingBottom: sizeCache.bottomPaddingBottom, opacity: Math.max(gsap.getProperty(bottomSide, 'opacity') || 0, 0.01), overflow: 'hidden', pointerEvents: 'auto' }); g.set(bottomKids, { opacity: 1, clearProps: 'transform' }); g.set(menuLinks, { height: currentLinks, overflow: 'hidden' }); hideBurger(g); } else { g.set(bottomSide, { height: 0, marginTop: 0, paddingTop: 0, paddingBottom: 0, opacity: 0, overflow: 'hidden', pointerEvents: 'none' }); g.set(bottomKids, { opacity: 0, y: -8 }); g.set(menuLinks, { height: sizeCache.linksCollapsed, overflow: 'hidden' }); if (burger) { g.set(burger, { display: 'inline-flex', autoAlpha: 1, pointerEvents: 'auto', position: 'absolute', top: 0, left: 0, margin: 0 }); var burgerMotion = getBurgerMotionEl(); if (burgerMotion) g.set(burgerMotion, { y: 0, autoAlpha: 1 }); } pinMenuLinksMotionHeight(g, sizeCache.linksCollapsed); } return; } g.set(bottomSide, { height: currentBottom, marginTop: currentMargin, paddingTop: sizeCache.bottomPaddingTop, paddingBottom: sizeCache.bottomPaddingBottom, opacity: 1, overflow: 'hidden' }); g.set(menuLinks, { height: currentLinks, overflow: 'hidden' }); prepareBurgerHidden(g); } function animateDesktopMenu(expanding) { var g = gsapRef(); if (!g || !bottomSide) return; if (expanding) { reverseDesktopCollapse(g); return; } nav._menuTl = buildDesktopCollapseTimeline(g, isDesktopMidExpand()); nav._menuTl.play(); } function reverseDesktopCollapse(g) { var tl = nav._menuTl; var live = tl && nav.classList.contains('is-menu-animating') && tl.isActive(); if (!live) { if (tl) tl.kill(); applyCollapsedVisual(false); tl = buildDesktopCollapseTimeline(g, false); nav._menuTl = tl; tl.progress(1, true); } nav.classList.remove('is-scroll-collapsed'); clearDesktopLinkHover(); nav.classList.add('is-scroll-collapse-ready', 'is-menu-animating'); g.set(getNavLinks(true), { display: 'flex' }); tl.timeScale(EXPAND_REVERSE_SPEED); tl.reverse(); } function prepareBurgerHidden(g) { if (!burger) return; g.set(burger, { display: 'inline-flex', position: 'absolute', top: 0, left: 0, margin: 0, autoAlpha: 0, pointerEvents: 'none' }); var burgerMotion = getBurgerMotionEl(); if (burgerMotion) g.set(burgerMotion, { y: MENU_IN_Y, autoAlpha: 0 }); } function hideBurger(g) { if (!burger) return; g.set(burger, { display: 'none', opacity: 0, pointerEvents: 'none' }); } function showBurger(g) { if (!burger) return; g.set(burger, { display: 'inline-flex', opacity: 1, pointerEvents: 'auto', position: 'absolute', top: 0, left: 0, margin: 0 }); var burgerMotion = getBurgerMotionEl(); if (burgerMotion) g.set(burgerMotion, { clearProps: 'transform' }); } function animateCollapse() { var g = gsapRef(); if (!g || !bottomSide) return; var bottomKids = getBottomKids(); if (isMobileMode()) { clearBottomMotion(g); g.set(bottomKids, { opacity: 1 }); var bottomNow = readBottomSideMotion(); g.set(bottomSide, { height: bottomNow.height, marginTop: bottomNow.marginTop, paddingTop: bottomNow.paddingTop, paddingBottom: bottomNow.paddingBottom, opacity: bottomNow.opacity, overflow: 'hidden' }); nav._menuTl = g.timeline({ defaults: { ease: MOBILE_EASE, overwrite: 'auto' }, onComplete: function () { applyCollapsedVisual(true); finishMenuAnimation(); } }); var collapseDur = getBlockCollapseDur(); nav._menuTl.to( bottomSide, { height: 0, marginTop: 0, paddingTop: 0, paddingBottom: 0, opacity: 0, duration: collapseDur, ease: MOBILE_EASE }, 0 ); return; } animateDesktopMenu(false); } function animateExpand() { var g = gsapRef(); if (!g || !bottomSide) return; var bottomKids = getBottomKids(); if (!isMobileMode()) { animateDesktopMenu(true); return; } var bottomNow = readBottomSideMotion(); g.set(bottomSide, { height: bottomNow.height, marginTop: bottomNow.marginTop, paddingTop: bottomNow.paddingTop, paddingBottom: bottomNow.paddingBottom, opacity: bottomNow.opacity, overflow: 'hidden', pointerEvents: bottomNow.height > 0 ? 'auto' : 'none' }); clearBottomMotion(g); g.set(bottomKids, { opacity: 1 }); nav._menuTl = g.timeline({ defaults: { ease: MOBILE_EASE, overwrite: 'auto' }, onComplete: function () { applyCollapsedVisual(false); finishMenuAnimation(); } }); var expandDur = getBlockExpandDur(); nav._menuTl.to( bottomSide, { height: sizeCache.bottom, marginTop: sizeCache.bottomMarginTop, paddingTop: sizeCache.bottomPaddingTop, paddingBottom: sizeCache.bottomPaddingBottom, opacity: 1, pointerEvents: 'auto', duration: expandDur, ease: MOBILE_EASE }, 0 ); } function setCollapsed(state, options) { options = options || {}; if (!nav || !gsapRef()) return; if (forceExpanded && state && !options.fromMenuOpen && !options.fromPageEnter) return; if (isMobileMode() && isMobileMenuBusy() && !options.fromMenuOpen && !options.fromPageEnter) return; var next = !!state; var reversing = !isMobileMode() && !!options.reverse && nav.classList.contains('is-menu-animating'); if (!next && reversing && nav._menuTl) { collapsed = false; nav.setAttribute('data-menu-collapsed', 'false'); animateDesktopMenu(true); if (!isMobileMode()) lockScrollAnim(estimateAnimLockMs(true)); if (mobileOverlay && mobileOverlay.classList.contains('is-visible')) { startOverlayBoundsTracking(); } return; } if (!reversing && !options.fromMenuOpen && !options.fromPageEnter && !options.fromScroll && !forceExpanded) { if (!isMobileMode() && scrollAnimLocked) return; if (!isMobileMode() && nav.classList.contains('is-menu-animating')) return; if (isMobileMode() && (mobileNavAnimLocked || nav.classList.contains('is-menu-animating'))) return; } if (reversing && !nav._menuTl) freezeNavAnim(); if (next === collapsed && !reversing) return; if (!heightsCached) measureHeights(false); if (!reversing) killMenuTweens(); else if (nav._menuTl) { nav._menuTl.kill(); nav._menuTl = null; } clearTimeout(nav._menuAnimTimer); collapsed = next; nav.setAttribute('data-menu-collapsed', next ? 'true' : 'false'); clearDesktopLinkHover(); nav.classList.add('is-scroll-collapse-ready', 'is-menu-animating'); if (next) { nav.classList.add('is-scroll-collapsed'); animateCollapse(); } else { nav.classList.remove('is-scroll-collapsed'); animateExpand(); } if (!isMobileMode()) lockScrollAnim(estimateAnimLockMs(!next)); else if (!options.fromMenuOpen) lockMobileNavAnim(); if (mobileOverlay && mobileOverlay.classList.contains('is-visible')) { startOverlayBoundsTracking(); } } function requestDesktopExpand() { unlockScrollAnimEarly(); setCollapsed(false, { fromScroll: true, reverse: nav.classList.contains('is-menu-animating') }); } function expandMenu() { forceExpanded = true; requestDesktopExpand(); } function onMenuTouchMobile(e) { if (!isActive() || !isMobileMode()) return; mobileTouchHandled = true; onMenuClickMobile(e); } function onMenuClickMobile(e) { if (!isActive() || !isMobileMode()) return; if (e && e.type === 'click' && mobileTouchHandled) { mobileTouchHandled = false; return; } if (e) { e.preventDefault(); e.stopPropagation(); if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation(); } if (mobileMenuOpen) closeMobileMenuAnim(); else openMobileMenuAnim(); } function onMenuClickDesktop(e) { if (!isActive() || isMobileMode() || !collapsed) return; if (e) { e.preventDefault(); e.stopPropagation(); } expandMenu(); } function isModalOpen() { if (!MODAL_OPEN_SELECTOR) return false; return !!document.querySelector(MODAL_OPEN_SELECTOR); } function onScrollPosition(y) { if (!isActive()) return; if (isModalOpen()) return; if (!bootstrapped) { lastY = y; return; } if (isMobileMode() && (isMobileMenuBusy() || mobileNavAnimLocked || nav.classList.contains('is-menu-animating'))) { lastY = y; return; } if (!scrollCollapseEnabled()) { lastY = y; return; } if (!isMobileMode() && y <= TOP_EXPAND) { forceExpanded = false; if (collapsed || nav.classList.contains('is-menu-animating')) { requestDesktopExpand(); } lastY = y; return; } if (!isMobileMode() && (scrollAnimLocked || nav.classList.contains('is-menu-animating'))) { var dirDuringAnim = getScrollDirection(y); if (collapsed && dirDuringAnim === -1) { requestDesktopExpand(); lastY = y; return; } lastY = y; resetScrollDirectionState(); return; } var dir = isMobileMode() ? getMobileScrollDirection(y) : getScrollDirection(y); if (!dir) { lastY = y; return; } forceExpanded = false; if (dir === 1) { if (!collapsed) setCollapsed(true); } else if (dir === -1) { if (collapsed) requestDesktopExpand(); } lastY = y; } function resetNav() { collapsed = false; forceExpanded = false; bootstrapped = false; heightsCached = false; mobileMenuOpen = false; closingMobileMenu = false; scrollAnimLocked = false; unlockMobileNavAnim(); resetScrollDirectionState(); unlockMobilePageScroll(); killMobileMenuTweens(); stopOverlayBoundsTracking(); if (nav) clearTimeout(nav._scrollAnimLockTimer); setMobileOverlay(false, true); if (!nav) return; cancelMenuAnimation(); clearDesktopLinkHover(); nav.classList.remove( 'is-scroll-collapsed', 'is-scroll-collapse-ready', 'is-menu-measuring', 'is-menu-instant', 'is-menu-animating' ); nav.setAttribute('data-menu-collapsed', 'false'); clearTimeout(nav._menuAnimTimer); if (gsapRef()) { gsapRef().set([bottomSide, menuLinks], { clearProps: 'all' }); restoreLinkDisplay(); if (bottomSide) gsapRef().set(bottomSide.children, { clearProps: 'all' }); if (mobileMenu) { gsapRef().set(mobileMenu, { clearProps: 'all' }); if (mobileMenu.children.length) gsapRef().set(mobileMenu.children, { clearProps: 'all' }); mobileMenu.classList.remove('is-open', 'is-ready'); } } if (nav) nav.classList.remove('menu-mobile-open'); if (burger) { burger.classList.remove('is-active'); burger.setAttribute('aria-expanded', 'false'); } } function syncScrollAnchor() { lastY = getScrollY(); resetScrollDirectionState(); } function syncState() { if (!isActive()) { resetNav(); lastY = getScrollY(); return; } lastY = getScrollY(); resetScrollDirectionState(); if (!bootstrapped && isMobileMode()) { collapsed = true; nav.classList.add('is-scroll-collapse-ready', 'is-scroll-collapsed', 'is-menu-instant'); nav.setAttribute('data-menu-collapsed', 'true'); if (gsapRef()) applyCollapsedVisual(true); measureHeights(false, { silentExpanded: true }); bootstrapped = true; nav.classList.remove('is-menu-instant'); if (isMobileExpandPage()) { setCollapsed(false, { fromPageEnter: true }); } else { applyCollapsedInstant(true); } return; } if (isMobileExpandPage() && (nav.classList.contains('is-menu-animating') || !collapsed)) { return; } measureHeights(false, { silentExpanded: false }); if (!bootstrapped) { applyCollapsedInstant(getShouldCollapse(lastY)); bootstrapped = true; } } function syncMobileMenuHeight() { if (!isMobileMode() || !mobileMenuOpen || !mobileMenu || !gsapRef()) return; gsapRef().set(mobileMenu, { height: measureMobileMenuHeight() }); } function onResize() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function () { if (!isActive()) { resetNav(); return; } heightsCached = false; measureHeights(true); applyCollapsedInstant(collapsed); lastY = getScrollY(); resetScrollDirectionState(); syncMobileMenuHeight(); if (mobileOverlay && mobileOverlay.classList.contains('is-visible')) { updateMobileOverlayBounds(); } }, 160); } function bindLenisScroll() { if (!window.lenis || typeof window.lenis.on !== 'function' || lenisScrollBound) return false; lenisScrollBound = true; window.lenis.on('scroll', function (payload) { var y = payload && typeof payload.scroll === 'number' ? payload.scroll : getScrollY(); onScrollPosition(y); }); requestAnimationFrame(function () { if (isActive()) syncState(); }); return true; } function bindScroll() { if (bindLenisScroll()) return; var n = 0; var wait = setInterval(function () { if (bindLenisScroll() || ++n > 80) clearInterval(wait); }, 50); window.addEventListener('scroll', function () { if (lenisScrollBound) return; onScrollPosition(getScrollY()); }, { passive: true }); } function bind() { nav = document.querySelector('.fixed_menu'); menuLinks = nav ? nav.querySelector('.menu_links') : null; bottomSide = nav ? nav.querySelector('.bottom_side') : null; burger = nav ? nav.querySelector('.h_link.menu_hamburger') : null; if (!nav || !bottomSide || ready) return false; ready = true; nav.classList.add('is-scroll-collapse-mounted', 'is-nav-pending-enter'); bindMobileMenuClick(); bindDesktopClick(); bindDesktopLinkHover(); syncCurrentNavLinkMarkers(); bindMobileMenuSync(); bindMobileOverlayTracking(); desktopMq.addEventListener('change', function () { bindDesktopClick(); syncState(); }); mobileMq.addEventListener('change', syncState); window.addEventListener('resize', onResize, { passive: true }); window.addEventListener('pageshow', function (e) { heightsCached = false; syncState(); if (e.persisted && nav && !isMobileMode() && !collapsed) scheduleNavEnterAnimation(); }); if (document.fonts && document.fonts.ready) { document.fonts.ready.then(function () { if (!isActive()) return; heightsCached = false; syncState(); }); } bindScroll(); syncState(); console.info('[menu-scroll-collapse v' + VERSION + ' gsap] ready'); return true; } function init() { loadGsap() .then(function () { configureGsap(); if (bind()) return; var tries = 0; var poll = setInterval(function () { if (bind() || ++tries > 40) clearInterval(poll); }, 100); }) .catch(function (err) { console.error('[menu-scroll-collapse] GSAP load failed', err); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } if (window.Webflow) { window.Webflow.push(function () { loadGsap().then(function () { configureGsap(); syncState(); }); }); } window.menuScrollCollapse = { version: VERSION, expand: expandMenu, collapse: function () { forceExpanded = false; setCollapsed(true); }, refresh: syncState, syncScroll: syncScrollAnchor, measure: function () { heightsCached = false; measureHeights(true); applyCollapsedInstant(collapsed); } }; })();