. */ (function () { "use strict"; // Run when DOM is ready and after Elementor renders const onReady = (fn) => { if (document.readyState !== "loading") fn(); else document.addEventListener("DOMContentLoaded", fn); if (window.elementorFrontend && window.elementorFrontend.hooks) { window.elementorFrontend.hooks.addAction("frontend/element_ready/global", fn); } }; function initSnap(container) { if (!container || container.__snapInit) return; container.__snapInit = true; // Lock body scroll to avoid double scrollbars document.body.classList.add("has-snap-wrap"); const panels = Array.from(container.children).filter(el => el.offsetParent !== null); if (!panels.length) return; // Ensure the container itself can receive keyboard focus (for Arrow/PageDown) if (!container.hasAttribute("tabindex")) container.setAttribute("tabindex", "0"); let snapping = false; let timer = null; // Find nearest panel to current scrollTop const nearest = () => { const top = container.scrollTop; let best = panels[0], dBest = Math.abs(panels[0].offsetTop - top); for (let i = 1; i < panels.length; i++) { const d = Math.abs(panels[i].offsetTop - top); if (d < dBest) { best = panels[i]; dBest = d; } } return best; }; const smoothTo = (y) => { snapping = true; container.scrollTo({ top: y, behavior: "smooth" }); // release after typical smooth duration setTimeout(() => { snapping = false; }, 500); }; // After user stops scrolling, gently land exactly on the closest panel const schedule = () => { if (snapping) return; if (timer) clearTimeout(timer); timer = setTimeout(() => { const t = nearest(); if (!t) return; const delta = Math.abs(container.scrollTop - t.offsetTop); if (delta > 1) smoothTo(t.offsetTop); }, 120); }; container.addEventListener("scroll", schedule, { passive: true }); // Keyboard nav feels great with snap container.addEventListener("keydown", (e) => { if (!container.contains(document.activeElement)) return; const t = nearest(); const idx = panels.indexOf(t); const go = (i) => { if (i < 0 || i >= panels.length) return; smoothTo(panels[i].offsetTop); }; const code = e.code || e.key; if (["PageDown", "ArrowDown", "ArrowRight", "Space"].includes(code)) { e.preventDefault(); go(idx + 1); } else if (["PageUp", "ArrowUp", "ArrowLeft"].includes(code)) { e.preventDefault(); go(idx - 1); } else if (code === "Home") { e.preventDefault(); go(0); } else if (code === "End") { e.preventDefault(); go(panels.length - 1); } }); // Keep panels matched to viewport on resize / mobile URL bar changes const ro = new ResizeObserver(() => schedule()); ro.observe(container); // Initial landing (in case the page opens mid-way) schedule(); } onReady(() => { document.querySelectorAll(".snap-wrap").forEach(initSnap); }); })();;