(function () { if (window.location.hash) { return; } function resetTinyInitialScroll() { var scrollY = window.scrollY || window.pageYOffset || 0; if (scrollY > 0 && scrollY <= 16) { window.scrollTo(0, 0); } } window.addEventListener('pageshow', function () { window.requestAnimationFrame(resetTinyInitialScroll); }); window.addEventListener('load', function () { window.requestAnimationFrame(resetTinyInitialScroll); }); })(); (function () { var body = document.body; var header = document.querySelector('.site-header'); if (!header) { return; } var compactClass = 'is-sticky-compact'; var bodyCompactClass = 'has-sticky-header-compact'; var backdropExitingClass = 'is-sticky-backdrop-exiting'; var logoGrowingClass = 'is-sticky-logo-growing'; var glassClass = 'sticky-header-glass-effect'; var isTicking = false; var isCompact = false; var threshold = parseInt(header.getAttribute('data-sticky-threshold'), 10); if (isNaN(threshold) || threshold < 0) { threshold = 80; } // Logo chiaro swap for the glass sticky header. While the compact bar shows its // dark translucent background, swap the header logo to the light variant exposed // by PHP (data-kalm-sticky-light-*). The a11y dark-mode swap owns the logo src // when the colour scheme is dark (the logo is already light there), so we never // write in that case to avoid the two swaps fighting. var stickyLogos = header.querySelectorAll('.site-branding--header img[data-kalm-sticky-light-src]'); function getColorScheme() { return document.documentElement.getAttribute('data-kalm-color-scheme') || ''; } function setOrRemoveAttr(el, attr, value) { if (value) { el.setAttribute(attr, value); } else { el.removeAttribute(attr); } } function applyStickyLogos() { if (!stickyLogos.length) { return; } var isDarkScheme = getColorScheme() === 'dark'; var useLight = isCompact && body.classList.contains(glassClass) && !isDarkScheme; Array.prototype.forEach.call(stickyLogos, function (logo) { // Capture the rendered (light-scheme) source once, only while the a11y // dark swap is not in control, so the stored base is the real base. if (!isDarkScheme && !logo.dataset.kalmStickyBaseSrc) { logo.dataset.kalmStickyBaseSrc = logo.getAttribute('src') || ''; logo.dataset.kalmStickyBaseSrcset = logo.getAttribute('srcset') || ''; logo.dataset.kalmStickyBaseSizes = logo.getAttribute('sizes') || ''; logo.dataset.kalmStickyBaseWidth = logo.getAttribute('width') || ''; logo.dataset.kalmStickyBaseHeight = logo.getAttribute('height') || ''; } if (useLight) { logo.setAttribute('src', logo.dataset.kalmStickyLightSrc); setOrRemoveAttr(logo, 'srcset', logo.dataset.kalmStickyLightSrcset); setOrRemoveAttr(logo, 'sizes', logo.dataset.kalmStickyLightSizes); setOrRemoveAttr(logo, 'width', logo.dataset.kalmStickyLightWidth); setOrRemoveAttr(logo, 'height', logo.dataset.kalmStickyLightHeight); return; } // Dark scheme: leave the logo to the a11y swap. Light scheme: restore base. if (isDarkScheme || !logo.dataset.kalmStickyBaseSrc) { return; } logo.setAttribute('src', logo.dataset.kalmStickyBaseSrc); setOrRemoveAttr(logo, 'srcset', logo.dataset.kalmStickyBaseSrcset); setOrRemoveAttr(logo, 'sizes', logo.dataset.kalmStickyBaseSizes); setOrRemoveAttr(logo, 'width', logo.dataset.kalmStickyBaseWidth); setOrRemoveAttr(logo, 'height', logo.dataset.kalmStickyBaseHeight); }); } function getScrollY() { var lockedScrollY = body.getAttribute('data-kalm-locked-scroll-y'); if (body.classList.contains('has-open-menu') && lockedScrollY !== null) { return parseInt(lockedScrollY, 10) || 0; } return window.scrollY || window.pageYOffset || 0; } function updateStickyState() { var scrollY = getScrollY(); var shouldCompact = scrollY >= threshold; if (shouldCompact !== isCompact) { isCompact = shouldCompact; if (isCompact) { header.classList.remove(backdropExitingClass, logoGrowingClass); header.classList.add(compactClass); body.classList.add(bodyCompactClass); } else { header.classList.remove(compactClass, backdropExitingClass, logoGrowingClass); body.classList.remove(bodyCompactClass); } applyStickyLogos(); } isTicking = false; } function requestStickyUpdate() { if (isTicking) { return; } isTicking = true; window.requestAnimationFrame(updateStickyState); } updateStickyState(); window.addEventListener('scroll', requestStickyUpdate, { passive: true }); window.addEventListener('resize', requestStickyUpdate); window.addEventListener('kalmInn:offcanvas-open', requestStickyUpdate); window.addEventListener('kalmInn:offcanvas-close', requestStickyUpdate); // Re-evaluate the logo swap when the accessibility panel toggles the colour // scheme, so the glass sticky bar keeps the correct logo after the a11y swap. if (stickyLogos.length && typeof window.MutationObserver === 'function') { new window.MutationObserver(applyStickyLogos).observe(document.documentElement, { attributes: true, attributeFilter: ['data-kalm-color-scheme'] }); } })(); (function () { var doc = document.documentElement; var body = document.body; var toggle = document.querySelector('.menu-toggle'); var panel = document.querySelector('#offcanvas-panel'); var overlay = document.querySelector('.offcanvas-overlay'); var closeButton = document.querySelector('.offcanvas-close'); var toggleLabel = toggle ? toggle.querySelector('.menu-toggle__label') : null; var prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; var lastFocusedElement = null; var closeTimer = null; var lockedScrollY = 0; var previousDocOverflow = ''; var previousBodyOverflow = ''; if (!toggle || !panel || !overlay || !closeButton) { return; } function setToggleState(isOpen) { var label = isOpen ? toggle.dataset.closeLabel : toggle.dataset.openLabel; toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); if (toggleLabel && label) { toggleLabel.textContent = label; } } function getFocusableElements() { return panel.querySelectorAll( 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])' ); } function emitOffcanvasEvent(name) { var event; if (typeof window.CustomEvent === 'function') { event = new window.CustomEvent(name); } else { event = document.createEvent('CustomEvent'); event.initCustomEvent(name, false, false, null); } window.dispatchEvent(event); } function restoreFocus() { focusWithoutScroll(lastFocusedElement); } function focusWithoutScroll(element) { if (!element || !element.isConnected || typeof element.focus !== 'function') { return; } try { element.focus({ preventScroll: true }); } catch (error) { element.focus(); } } function openMenu() { if (!panel.hidden) { return; } lastFocusedElement = document.activeElement; lockedScrollY = window.scrollY || window.pageYOffset || 0; previousDocOverflow = doc.style.overflow; previousBodyOverflow = body.style.overflow; if (closeTimer) { window.clearTimeout(closeTimer); closeTimer = null; } panel.hidden = false; panel.setAttribute('aria-hidden', 'false'); overlay.hidden = false; body.classList.add('has-open-menu'); body.setAttribute('data-kalm-locked-scroll-y', String(lockedScrollY)); doc.style.overflow = 'hidden'; body.style.overflow = 'hidden'; setToggleState(true); emitOffcanvasEvent('kalmInn:offcanvas-open'); window.requestAnimationFrame(function () { panel.classList.add('is-open'); focusWithoutScroll(closeButton); }); } function closeMenu() { if (panel.hidden && !body.classList.contains('has-open-menu')) { return; } panel.classList.remove('is-open'); setToggleState(false); closeTimer = window.setTimeout(function () { var restoredScrollY = lockedScrollY; panel.hidden = true; panel.setAttribute('aria-hidden', 'true'); overlay.hidden = true; doc.style.overflow = previousDocOverflow; body.style.overflow = previousBodyOverflow; if ((window.scrollY || window.pageYOffset || 0) !== restoredScrollY) { window.scrollTo(0, restoredScrollY); } window.requestAnimationFrame(function () { body.classList.remove('has-open-menu'); body.removeAttribute('data-kalm-locked-scroll-y'); emitOffcanvasEvent('kalmInn:offcanvas-close'); restoreFocus(); }); }, prefersReducedMotion ? 0 : 220); } toggle.addEventListener('click', function () { if (panel.hidden) { openMenu(); return; } closeMenu(); }); closeButton.addEventListener('click', closeMenu); overlay.addEventListener('click', closeMenu); document.addEventListener('keydown', function (event) { if (panel.hidden) { return; } if (event.key === 'Escape') { event.preventDefault(); closeMenu(); return; } if (event.key !== 'Tab') { return; } var focusable = getFocusableElements(); if (!focusable.length) { event.preventDefault(); panel.focus(); return; } var first = focusable[0]; var last = focusable[focusable.length - 1]; if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); } }); })(); (function () { var slider = document.querySelector('[data-slider]'); if (!slider) { return; } var slides = Array.prototype.slice.call(slider.querySelectorAll('[data-slide]')); var previousButton = slider.querySelector('[data-slider-prev]'); var nextButton = slider.querySelector('[data-slider-next]'); var toggleButton = slider.querySelector('[data-slider-toggle]'); var reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; var currentIndex = 0; var autoplayTimer = null; var autoplayDelay = parseInt(slider.getAttribute('data-autoplay-delay'), 10) || 6000; var canAutoplay = slider.getAttribute('data-autoplay') === 'true' && slides.length > 1 && !reducedMotion; var isPaused = !canAutoplay; if (slides.length <= 1) { return; } function loadSlideImage(index) { var image = slides[index] ? slides[index].querySelector('[data-slide-image]') : null; var src = image ? image.getAttribute('data-slide-image') : ''; if (!src) { return; } image.style.backgroundImage = 'url("' + src.replace(/"/g, '%22') + '")'; image.removeAttribute('data-slide-image'); } function updateSlides(nextIndex) { slides[currentIndex].classList.remove('is-active'); slides[currentIndex].setAttribute('aria-hidden', 'true'); Array.prototype.forEach.call(slides[currentIndex].querySelectorAll('[data-slide-focus]'), function (element) { element.setAttribute('tabindex', '-1'); }); currentIndex = (nextIndex + slides.length) % slides.length; loadSlideImage(currentIndex); slides[currentIndex].classList.add('is-active'); slides[currentIndex].setAttribute('aria-hidden', 'false'); Array.prototype.forEach.call(slides[currentIndex].querySelectorAll('[data-slide-focus]'), function (element) { element.removeAttribute('tabindex'); }); } function stopAutoplay() { if (autoplayTimer) { window.clearInterval(autoplayTimer); autoplayTimer = null; } } function startAutoplay() { if (!canAutoplay || autoplayTimer) { return; } autoplayTimer = window.setInterval(function () { updateSlides(currentIndex + 1); }, autoplayDelay); } function setPaused(paused) { isPaused = paused; if (isPaused) { stopAutoplay(); } else { startAutoplay(); } if (toggleButton) { toggleButton.textContent = isPaused ? toggleButton.dataset.playLabel : toggleButton.dataset.pauseLabel; } } if (previousButton) { previousButton.addEventListener('click', function () { updateSlides(currentIndex - 1); setPaused(true); }); } if (nextButton) { nextButton.addEventListener('click', function () { updateSlides(currentIndex + 1); setPaused(true); }); } if (toggleButton) { toggleButton.addEventListener('click', function () { setPaused(!isPaused); }); } if (canAutoplay) { setPaused(false); } else if (toggleButton) { setPaused(true); } })(); (function () { var menu = document.querySelector('#offcanvas-menu'); if (!menu) { return; } var nav = menu.closest ? menu.closest('.offcanvas-navigation') : null; var parents = menu.querySelectorAll('.menu-item-has-children'); if (!parents.length) { return; } var counter = 0; function directChild(parent, selector) { var children = parent.children; for (var i = 0; i < children.length; i++) { if (children[i].matches && children[i].matches(selector)) { return children[i]; } } return null; } Array.prototype.forEach.call(parents, function (item) { var submenu = directChild(item, '.sub-menu'); var link = directChild(item, 'a'); if (!submenu || !link) { return; } if (!submenu.id) { counter += 1; submenu.id = 'offcanvas-submenu-' + counter; } // The parent label itself toggles the accordion; the chevron is a // decorative marker appended inside the link so the label stays centred. var toggle = document.createElement('span'); toggle.className = 'offcanvas-submenu-toggle'; toggle.setAttribute('aria-hidden', 'true'); var icon = document.createElement('span'); icon.className = 'offcanvas-submenu-toggle__icon'; toggle.appendChild(icon); link.appendChild(toggle); link.setAttribute('aria-expanded', 'false'); link.setAttribute('aria-controls', submenu.id); link.addEventListener('click', function (event) { event.preventDefault(); var expanded = item.classList.toggle('is-expanded'); link.setAttribute('aria-expanded', expanded ? 'true' : 'false'); }); }); if (nav) { nav.classList.add('has-accordion'); } })();