/* ========================================================= Lottie: safe init + lazy load - Mobile Lottie support (≤767px) via .lottie-data-url-mob - Desktop fallback via .lottie-data-url - GSAP-controlled hero safe ========================================================= */ (() => { /* --------------------------- Safe DOM ready helper --------------------------- */ const onReadySafe = window.__DF_UTILS__?.onReady || function (cb) { if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", cb); } else { cb(); } }; /* --------------------------- Wait for bodymovin --------------------------- */ function waitForBodymovin(cb, timeout = 6000) { const start = performance.now(); function check() { if (window.bodymovin) return cb(); if (performance.now() - start > timeout) { console.warn("[Lottie] bodymovin not found (timeout)."); return; } requestAnimationFrame(check); } check(); } /* --------------------------- Resolve correct Lottie path --------------------------- */ function getLottiePath(el) { const isMobile = window.matchMedia("(max-width: 767px)").matches; const desktopEl = el.querySelector(".lottie-data-url"); const mobileEl = el.querySelector(".lottie-data-url-mob"); const desktopUrl = desktopEl?.textContent?.trim(); const mobileUrl = mobileEl?.textContent?.trim(); // Hide holders to avoid layout / selection issues if (desktopEl) desktopEl.style.display = "none"; if (mobileEl) mobileEl.style.display = "none"; // Mobile priority ONLY if URL exists if (isMobile && mobileUrl) { return mobileUrl; } // Desktop fallback if (desktopUrl) { return desktopUrl; } // Attribute fallback return (el.getAttribute("data-lottie-src") || "").trim(); } /* --------------------------- Init single Lottie instance --------------------------- */ function initLottie(el) { if (!window.bodymovin) return; if (el.__lottieAnim) return; const path = getLottiePath(el); if (!path) return; const isGsapControlled = el.hasAttribute("data-hero-lottie-by-gsap"); const playOnHover = !isGsapControlled && el.hasAttribute("data-play-hover"); const loopLottie = el.hasAttribute("data-lottie-loop"); const rendererType = el.getAttribute("data-lottie-renderer") || "svg"; // Build assetsPath from JSON URL let assetsPath = ""; try { const url = new URL(path, window.location.href); assetsPath = url.href.substring( 0, url.href.lastIndexOf("/") + 1 ); } catch (e) {} const anim = bodymovin.loadAnimation({ container: el, renderer: rendererType, path, assetsPath, rendererSettings: { preserveAspectRatio: "xMidYMid slice", }, loop: isGsapControlled ? false : !playOnHover && loopLottie, autoplay: isGsapControlled ? false : !playOnHover, }); el.__lottieAnim = anim; // GSAP-controlled hero → lock on first frame if (isGsapControlled) { anim.stop(); anim.goToAndStop(0, true); return; } // Hover playback logic if (playOnHover) { const parent = el.closest(".lottie-wrapper-hover") || el; parent.addEventListener("mouseenter", () => { anim.setDirection(1); anim.play(); }); parent.addEventListener("mouseleave", () => { anim.setDirection(-1); anim.play(); }); } } /* --------------------------- Main bootstrap --------------------------- */ onReadySafe(() => { waitForBodymovin(() => { const originalLoad = bodymovin.loadAnimation; // Patch: always store anim on container if (!bodymovin.__patchedStoreAnim) { bodymovin.loadAnimation = function (config) { const anim = originalLoad(config); if (config.container) { config.container.__lottieAnim = anim; } return anim; }; bodymovin.__patchedStoreAnim = true; } const observer = new IntersectionObserver( (entries, obs) => { entries.forEach((entry) => { if (!entry.isIntersecting) return; if (!entry.target.__lottieAnim) { initLottie(entry.target); } obs.unobserve(entry.target); }); }, { rootMargin: "0px 0px 200px 0px", threshold: 0.1, } ); document .querySelectorAll(".lottie-element, [data-lottie-src]") .forEach((el) => { el.style.width = "100%"; el.style.height = "100%"; el.style.overflow = "hidden"; // Immediate init cases (hero / forced) if ( el.hasAttribute("data-hero-lottie-by-gsap") || el.hasAttribute("data-hero-lottie") || el.hasAttribute("data-no-wait") ) { initLottie(el); } else { observer.observe(el); } }); }); }); })();