/*Disable all dev mods for complex components if the user forgot to disable them in the admin area*/ document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll(".dev-edite-mode").forEach((el) => { if (el.classList.contains("is-on")) { el.classList.remove("is-on"); } }); }); /**************************************** * Slider card-liner START ***************************************/ document.addEventListener("DOMContentLoaded", () => { const sliders = document.querySelectorAll(".card-swiper-felx-wrapper"); if (!sliders.length) return; sliders.forEach((sliderEl) => { let scrollbarEl = sliderEl.querySelector(".swiper-scrollbar"); if (!scrollbarEl) { const parent = sliderEl.closest(".card-swiper-felx-wrapper-pagination"); if (parent) { scrollbarEl = parent.querySelector(".swiper-scrollbar"); } } if (!scrollbarEl) { console.warn("⚠️ noscrollbar found:", sliderEl); return; } const swiper = new Swiper(sliderEl, { loop: false, slidesPerView: "auto", spaceBetween: 24, grabCursor: true, scrollbar: { el: scrollbarEl, hide: false, draggable: true, }, }); const drag = scrollbarEl.querySelector(".swiper-scrollbar-drag"); if (!drag) return; drag.style.transform = "none"; drag.style.left = "0"; drag.style.position = "absolute"; drag.style.pointerEvents = "none"; function updateScrollbarFill() { const progress = Math.max(0, Math.min(1, swiper.progress || 0)); drag.style.width = progress * 100 + "%"; } updateScrollbarFill(); swiper.on("setTranslate", updateScrollbarFill); swiper.on("slideChange", updateScrollbarFill); swiper.on("resize", updateScrollbarFill); }); }); /**************************************** * Slider card-liner END ***************************************/ /**************************************** * Lightbox video START ***************************************/ /**************************************** * Lightbox video START ***************************************/ document.addEventListener("DOMContentLoaded", function () { const lightboxes = document.querySelectorAll("[class*='lightbox-video']"); lightboxes.forEach((lb) => { const jsonScript = lb.querySelector(".w-json"); const videoEl = lb.querySelector(".video-data-url"); if (!jsonScript || !videoEl) return; const raw = videoEl.textContent || ""; const videoUrl = raw .replace(/&/g, "&") .replace(/\s+/g, "") .replace(/\.+$/, "") .trim(); if (!videoUrl) return; // ------------------------------------ // Vimeo ID // ------------------------------------ function getVimeoId(url) { try { const u = new URL(url); const parts = u.pathname.split("/").filter(Boolean); for (let i = parts.length - 1; i >= 0; i--) { if (/^\d+$/.test(parts[i])) return parts[i]; } } catch {} return /^\d+$/.test(url) ? url : null; } // ------------------------------------ // YouTube ID // ------------------------------------ function getYouTubeId(url) { try { const parsed = new URL(url); if (parsed.searchParams.get("v")) { return parsed.searchParams.get("v"); } const parts = parsed.pathname.split("/"); const embedIndex = parts.indexOf("embed"); if (embedIndex !== -1 && parts[embedIndex + 1]) { return parts[embedIndex + 1].split("?")[0]; } } catch {} const match = url.match(/youtu\.be\/([^?]+)/); return match?.[1] || null; } // ------------------------------------ // Wistia ID // ------------------------------------ function getWistiaId(url) { try { const u = new URL(url); const parts = u.pathname.split("/").filter(Boolean); const mediasIndex = parts.indexOf("medias"); if (mediasIndex !== -1 && parts[mediasIndex + 1]) { return parts[mediasIndex + 1]; } } catch {} return null; } let embedUrl = ""; let htmlCode = ""; const vimeoId = getVimeoId(videoUrl); const youtubeId = getYouTubeId(videoUrl); const wistiaId = getWistiaId(videoUrl); // ------------------------------------ // Vimeo EMBED // ------------------------------------ if (vimeoId) { embedUrl = `https://player.vimeo.com/video/${vimeoId}?autoplay=1&muted=1&loop=1&autopause=0&playsinline=1&title=0&byline=0&portrait=0`; htmlCode = ``; } // ------------------------------------ // YouTube EMBED // ------------------------------------ else if (youtubeId) { embedUrl = `https://www.youtube.com/embed/${youtubeId}?autoplay=1&mute=1&loop=1&playlist=${youtubeId}&playsinline=1&controls=1`; htmlCode = ``; } // ------------------------------------ // Wistia EMBED // ------------------------------------ else if (wistiaId) { embedUrl = `https://fast.wistia.net/embed/iframe/${wistiaId}?autoplay=1&muted=1&playsinline=1`; htmlCode = ` `; } // ------------------------------------ // MP4 / DIRECT VIDEO // ------------------------------------ else if (videoUrl.endsWith(".mp4")) { embedUrl = videoUrl; htmlCode = ` `; } // ------------------------------------ // Unsupported format → пропускаємо // ------------------------------------ else { return; } const jsonData = { items: [ { type: "video", url: embedUrl, html: htmlCode, thumbnailUrl: "", width: 940, height: 528, }, ], group: "", }; jsonScript.textContent = JSON.stringify(jsonData); }); }); /**************************************** * Lightbox video END ***************************************/ /**************************************** * Lightbox video END ***************************************/ /**************************************** * Start code Reveal text from opacity code ***************************************/ const splitTypes = document.querySelectorAll("[reveal-type-color]"); if (splitTypes.length > 0) { splitTypes.forEach((char) => { const text = new SplitType(char, { types: ["chars", "words"] }); gsap.fromTo( text.chars, { opacity: 0.5, // початкове значення }, { scrollTrigger: { trigger: char, start: "top 90%", end: "bottom 40%", scrub: true, markers: false, }, opacity: 1, // кінцеве значення stagger: 0.05, ease: "power2.out", } ); }); } /**************************************** * End code Reveal text from opacity code ***************************************/ /* ============================================================================= 6. Marquee (no jank safeguards) START ============================================================================= */ (() => { function initMarquees(selector, defaultSpeed) { const marquees = document.querySelectorAll(selector); if (!marquees.length) return; marquees.forEach((parent) => { const track = parent.querySelector(".marquee_track") || parent; const original = track.innerHTML; track.insertAdjacentHTML("beforeend", original); track.insertAdjacentHTML("beforeend", original); const speedAttr = parseFloat(parent.getAttribute("data-speed")); const speed = !isNaN(speedAttr) ? speedAttr : defaultSpeed; const direction = parent.dataset.direction === "right" ? 1 : -1; let offset = 0; let paused = false; const cycleWidth = Array.from(track.children) .slice(0, track.children.length / 3) .reduce((sum, el) => sum + el.clientWidth, 0); const animate = () => { if (!paused) { offset += speed * direction; track.style.transform = `translateX(${offset}px)`; if (direction === -1 && Math.abs(offset) > cycleWidth) { offset += cycleWidth; } if (direction === 1 && offset > 0) { offset -= cycleWidth; } } requestAnimationFrame(animate); }; animate(); }); } document.addEventListener("DOMContentLoaded", () => { initMarquees(".marquee", 0.6); }); })(); /* ============================================================================= 6. Marquee (no jank safeguards) END ============================================================================= */ /**************************************** * Slider testimonial START ***************************************/ document.addEventListener("DOMContentLoaded", () => { const testimonialSliders = document.querySelectorAll( ".testimonial-swiper-slider" ); if (!testimonialSliders.length) return; testimonialSliders.forEach((sliderEl) => { const scrollbarEl = sliderEl.querySelector(".swiper-scrollbar"); const swiper = new Swiper(sliderEl, { loop: false, slidesPerView: 1, effect: "fade", fadeEffect: { crossFade: true, }, spaceBetween: 0, centeredSlides: true, grabCursor: true, scrollbar: { el: scrollbarEl, hide: false, }, pagination: { el: sliderEl.querySelector(".swiper-pagination"), clickable: true, }, on: { init: () => { // fade-in ефект при завантаженні const slides = sliderEl.querySelectorAll(".swiper-slide"); slides.forEach((slide, i) => { slide.style.opacity = "0"; slide.style.transform = "translateY(20px)"; setTimeout(() => { slide.style.transition = "opacity 0.6s ease, transform 0.6s ease"; slide.style.opacity = "1"; slide.style.transform = "translateY(0)"; }, 150 * i); }); }, }, }); // ==== Кастомна лінія заповнення ==== const drag = scrollbarEl?.querySelector(".swiper-scrollbar-drag"); if (!drag) return; drag.style.transform = "none"; drag.style.left = "0"; drag.style.position = "absolute"; drag.style.pointerEvents = "none"; function updateScrollbarFill() { const progress = Math.max(0, Math.min(1, swiper.progress || 0)); drag.style.width = progress * 100 + "%"; } updateScrollbarFill(); swiper.on("setTranslate", updateScrollbarFill); swiper.on("slideChange", updateScrollbarFill); swiper.on("resize", updateScrollbarFill); }); }); /**************************************** * Slider testimonial END ***************************************/ /**************************************** * Slider featured mob START ***************************************/ document.addEventListener("DOMContentLoaded", () => { const sliders = document.querySelectorAll(".list-item-featured"); if (!sliders.length) return; const MOBILE_BREAKPOINT = 767; sliders.forEach((sliderEl) => { const scrollbarEl = sliderEl.querySelector(".swiper-scrollbar"); let swiperInstance = null; function initSwiper() { swiperInstance = new Swiper(sliderEl, { loop: false, slidesPerView: "auto", spaceBetween: 24, scrollbar: { el: scrollbarEl, hide: false, }, }); const drag = scrollbarEl.querySelector(".swiper-scrollbar-drag"); if (!drag) return; drag.style.transform = "none"; drag.style.left = "0"; drag.style.position = "absolute"; drag.style.pointerEvents = "none"; function updateScrollbarFill() { const progress = Math.max(0, Math.min(1, swiperInstance.progress || 0)); drag.style.width = progress * 100 + "%"; } updateScrollbarFill(); swiperInstance.on("setTranslate", updateScrollbarFill); swiperInstance.on("slideChange", updateScrollbarFill); swiperInstance.on("resize", updateScrollbarFill); } function destroySwiper() { if (swiperInstance) { swiperInstance.destroy(true, true); swiperInstance = null; } } function checkBreakpoint() { if (window.innerWidth <= MOBILE_BREAKPOINT) { if (!swiperInstance) initSwiper(); } else { destroySwiper(); } } checkBreakpoint(); window.addEventListener("resize", checkBreakpoint); }); }); /**************************************** * Slider featured mob START ***************************************/ /* ============================================================================= Interactive Grid Tabs (video src from .video-data-url) START ============================================================================= */ window.__DF_UTILS__ = window.__DF_UTILS__ || { onReady(fn) { if (document.readyState !== "loading") fn(); else document.addEventListener("DOMContentLoaded", fn); }, debounce(fn, delay) { let t; return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), delay); }; }, waitFor(checkFn, interval = 100, timeout = 5000) { return new Promise((resolve, reject) => { const start = Date.now(); (function check() { if (checkFn()) resolve(); else if (Date.now() - start > timeout) reject("waitFor timeout"); else setTimeout(check, interval); })(); }); }, }; (() => { const { onReady, debounce, waitFor } = window.__DF_UTILS__; const VARIANT_ATTR = "data-wf--item-interactive-tab-gradient--variant"; const VIDEO_VARIANT_VALUE = "is-video"; function isVideoTab(el) { return (el.getAttribute(VARIANT_ATTR) || "") === VIDEO_VARIANT_VALUE; } function findVideoEl(tabEl) { // Prefer your dedicated class if present return ( tabEl.querySelector("video.tab-video-here") || tabEl.querySelector("video.cloud-video-element") || tabEl.querySelector("video") ); } function getDesiredUrlForTab(tabEl, isMobile) { // Mobile URL takes priority on mobile if exists, otherwise fallback to desktop URL const mobileEl = tabEl.querySelector(".video-data-url-mobile"); const desktopEl = tabEl.querySelector(".video-data-url"); const mobileUrl = (mobileEl?.textContent || "").trim(); const desktopUrl = (desktopEl?.textContent || "").trim(); if (isMobile && mobileUrl) return mobileUrl; return desktopUrl || mobileUrl || ""; } function applyVideoSrc(videoEl, url) { if (!videoEl || !url) return false; const sourceEl = videoEl.querySelector("source"); const current = sourceEl ? (sourceEl.getAttribute("src") || "").trim() : (videoEl.getAttribute("src") || videoEl.currentSrc || "").trim(); if (current === url) return false; // Ensure autoplay requirements on iOS/Android try { videoEl.muted = true; videoEl.playsInline = true; videoEl.setAttribute("muted", ""); videoEl.setAttribute("playsinline", ""); if (!videoEl.getAttribute("preload")) videoEl.setAttribute("preload", "metadata"); } catch (e) {} if (sourceEl) sourceEl.setAttribute("src", url); else videoEl.setAttribute("src", url); try { videoEl.load(); } catch (e) {} return true; } class InteractiveGrid { constructor(wrapper) { this.wrapper = wrapper; // Settings this.stepDuration = parseFloat(wrapper.getAttribute("data-step-duration")) || 6; this.switchDelay = 1; // seconds after progress ends before switching this.fadeDuration = parseFloat(wrapper.dataset.fadeDuration) || 0.5; this.autoMediaHeight = wrapper.dataset.mediaWrapperAuto === "true"; // State this.tabs = []; this.activeIndex = 0; this.heightCache = []; this.isInViewport = false; this._isMobile = window.innerWidth < 991; // Timers this.playTimer = null; this.progressTween = null; this._onResize = debounce(() => this.reMeasure(), 120); this.init(); } init() { this.collectTabs(); if (!this.tabs.length) return; // Stop everything by default (only active plays) this.forceStopAllVideos(true); this.measureHeights(); // Respect existing active tab in markup const initial = this.tabs.findIndex((t) => t.container.classList.contains("is-interactive-active") ); this.activeIndex = initial >= 0 ? initial : 0; this.bindClicks(); this.bindScrollTrigger(); window.addEventListener("resize", this._onResize, { passive: true }); // Render initial state, autoplay starts only when in viewport this.activateTab(this.activeIndex, { userClicked: false, startNow: false, }); } collectTabs() { const tabEls = Array.from( this.wrapper.querySelectorAll(".interactive-tab") ); this.tabs = tabEls.map((el) => { const videoEl = isVideoTab(el) ? findVideoEl(el) : null; // Hydrate src from .video-data-url(.mobile) immediately (but preload stays metadata) if (videoEl) { const desiredUrl = getDesiredUrlForTab(el, this._isMobile); applyVideoSrc(videoEl, desiredUrl); } return { container: el, clickArea: el.querySelector(".interactive-tab_content_wrap") || el, hidden: el.querySelector(".interactive-tab_content_hidden"), content: el.querySelector(".interactive-tab_content"), mediaWrapper: el.querySelector(".interactive-tab_media_wrap"), progressBar: el.querySelector(".interactive-progress"), isVideo: isVideoTab(el), videoEl, }; }); } bindScrollTrigger() { ScrollTrigger.create({ trigger: this.wrapper, start: this.wrapper.dataset.scrollStart || "top 100%", end: this.wrapper.dataset.scrollEnd || "bottom 0%", onEnter: () => { this.isInViewport = true; this.start(); }, onEnterBack: () => { this.isInViewport = true; this.start(); }, onLeave: () => { this.isInViewport = false; this.stop(); }, onLeaveBack: () => { this.isInViewport = false; this.stop(); }, }); } bindClicks() { this.tabs.forEach((t, i) => { t.clickArea.addEventListener("click", () => { if (this.activeIndex === i) return; // Removed click blocking: // - no preventDefault() // - no stopPropagation() this.activateTab(i, { userClicked: true, startNow: true }); }); }); } measureHeights() { this.heightCache = this.tabs.map((t) => { if (!t.hidden) return 0; gsap.set(t.hidden, { height: "auto", opacity: 1, position: "absolute", visibility: "hidden", }); const h = t.hidden.scrollHeight; gsap.set(t.hidden, { clearProps: "all" }); return h || 0; }); } reMeasure() { // If breakpoint changed, re-hydrate URLs (desktop vs mobile) const nowMobile = window.innerWidth < 991; const breakpointChanged = nowMobile !== this._isMobile; this._isMobile = nowMobile; if (breakpointChanged) { this.tabs.forEach((t) => { if (!t.videoEl) return; const desiredUrl = getDesiredUrlForTab(t.container, this._isMobile); applyVideoSrc(t.videoEl, desiredUrl); }); } this.measureHeights(); const t = this.tabs[this.activeIndex]; if (!t) return; if (t.hidden) gsap.set(t.hidden, { height: "auto", opacity: 1 }); if (t.mediaWrapper) { if (window.innerWidth < 991) { gsap.set(t.mediaWrapper, { height: this.autoMediaHeight ? "auto" : "80vw", overflow: "hidden", opacity: 1, }); } else { gsap.set(t.mediaWrapper, { clearProps: "height", opacity: 1 }); } } } clearTimers() { if (this.playTimer) this.playTimer.kill?.(); this.playTimer = null; if (this.progressTween) this.progressTween.kill(); this.progressTween = null; } forceStopAllVideos(resetTime) { this.tabs.forEach((t) => { if (!t.videoEl) return; const v = t.videoEl; try { v.autoplay = false; v.loop = false; v.muted = true; v.playsInline = true; v.preload = v.preload || "metadata"; } catch (e) {} try { v.pause(); } catch (e) {} if (resetTime) { try { v.currentTime = 0; } catch (e) { v.addEventListener( "loadedmetadata", () => { try { v.currentTime = 0; } catch (err) {} }, { once: true } ); } } }); } restartAndPlayVideo(tabObj) { const videoEl = tabObj?.videoEl; if (!videoEl) return; // Ensure correct src right before play (important if markup changes dynamically) const desiredUrl = getDesiredUrlForTab(tabObj.container, this._isMobile); applyVideoSrc(videoEl, desiredUrl); try { videoEl.pause(); } catch (e) {} try { videoEl.currentTime = 0; } catch (e) { videoEl.addEventListener( "loadedmetadata", () => { try { videoEl.currentTime = 0; } catch (err) {} }, { once: true } ); } const p = videoEl.play?.(); if (p && typeof p.catch === "function") p.catch(() => {}); } freezeVideoLastFrame(videoEl) { if (!videoEl) return; try { videoEl.pause(); videoEl.currentTime = Math.max(0, this.stepDuration - 0.05); } catch (e) {} } resetUI() { this.tabs.forEach((t) => { t.container.classList.remove("is-interactive-active"); if (t.hidden) gsap.set(t.hidden, { clearProps: "all" }); if (t.content) gsap.set(t.content, { clearProps: "all" }); if (t.mediaWrapper) gsap.set(t.mediaWrapper, { clearProps: "all" }); if (t.progressBar) { gsap.set(t.progressBar, { width: "0%", opacity: 1, clearProps: "transform", }); } }); } stop() { this.clearTimers(); const t = this.tabs[this.activeIndex]; if (t?.videoEl) { try { t.videoEl.pause(); } catch (e) {} } } start() { this.clearTimers(); this.runProgressAndAutoplay(); } activateTab(index, { userClicked, startNow }) { this.activeIndex = index; this.clearTimers(); this.forceStopAllVideos(true); this.resetUI(); const t = this.tabs[index]; if (!t) return; t.container.classList.add("is-interactive-active"); // Content animation if (t.content) { gsap.fromTo( t.content, { opacity: 0, y: 6 }, { opacity: 1, y: 0, duration: this.fadeDuration, ease: "power2.out" } ); } // Expand hidden if (t.hidden) { gsap.fromTo( t.hidden, { height: 0, opacity: 0 }, { height: this.heightCache[index] || 0, opacity: 1, duration: 0.5, ease: "power2.out", onComplete: () => gsap.set(t.hidden, { height: "auto" }), } ); } // Media animation if (t.mediaWrapper) { gsap.fromTo( t.mediaWrapper, { opacity: 0, y: 10 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out", clearProps: "transform", } ); } // User click: allow instant start even if ScrollTrigger hasn't fired yet if (userClicked) this.isInViewport = true; if (startNow && this.isInViewport) this.start(); } runProgressAndAutoplay() { if (!this.isInViewport) return; const t = this.tabs[this.activeIndex]; if (!t) return; // Start active video from 0 (only active plays) if (t.isVideo && t.videoEl) { this.restartAndPlayVideo(t); } // Progress bar: fixed duration from wrapper if (t.progressBar) { gsap.set(t.progressBar, { width: "0%", opacity: 1 }); this.progressTween = gsap.to(t.progressBar, { width: "100%", duration: this.stepDuration, ease: "none", overwrite: true, }); } // After progress ends, freeze video, wait 1s, switch tab this.playTimer = gsap.delayedCall(this.stepDuration, () => { if (t.isVideo && t.videoEl) this.freezeVideoLastFrame(t.videoEl); this.playTimer = gsap.delayedCall(this.switchDelay, () => { const next = (this.activeIndex + 1) % this.tabs.length; this.activateTab(next, { userClicked: false, startNow: true }); }); }); } } function initAll() { const wrappers = Array.from( document.querySelectorAll(".interactive-grid_wrapper") ); wrappers.forEach((w) => { if (w.classList.contains("__inited")) return; w.classList.add("__inited"); new InteractiveGrid(w); }); } onReady(() => { waitFor( () => typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined" ) .then(initAll) .catch(() => {}); }); })(); /* ============================================================================= Interactive Grid Tabs END ============================================================================= */ /* ============================================================================= Open FAQ START ============================================================================= */ document.addEventListener("click", function (event) { const element = event.target.closest('[js-faq-collapse="true"]'); if (element) { if (!element.classList.contains("open")) { document .querySelectorAll('[js-faq-collapse="true"].open') .forEach(function (item) { if (item !== element) { item.click(); } }); element.classList.add("open"); } else { element.classList.remove("open"); } } }); /* ============================================================================= Open FAQ END ============================================================================= */ /* ============================================================================= Slider for scorecard block mobile devices with timeline START ============================================================================= */ document.addEventListener("DOMContentLoaded", () => { let heroSwiper = null; let progressInterval = null; let observer = null; let isInView = false; function initHeroSwiper() { const heroSlider = document.querySelector(".hero-block-swiper-scorecard"); const progressLine = document.querySelector(".active-scrolle-slide"); if (!heroSlider || !progressLine) return; if (window.innerWidth <= 767 && !heroSwiper) { heroSwiper = new Swiper(heroSlider, { slidesPerView: 1, loop: true, effect: "fade", fadeEffect: { crossFade: true }, allowTouchMove: true, grabCursor: true, }); const progressTime = 4000; function startProgress() { if (!isInView) return; clearInterval(progressInterval); progressLine.style.transition = "none"; progressLine.style.width = "0%"; setTimeout(() => { progressLine.style.transition = `width ${progressTime}ms linear`; progressLine.style.width = "100%"; }, 50); progressInterval = setInterval(() => { heroSwiper.slideNext(); startProgress(); }, progressTime); } function stopProgress() { clearInterval(progressInterval); progressLine.style.transition = "none"; } heroSlider.addEventListener("mouseenter", stopProgress); heroSlider.addEventListener("mouseleave", startProgress); observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { isInView = entry.isIntersecting; if (isInView) { startProgress(); } else { stopProgress(); } }); }, { threshold: 0.3, } ); observer.observe(heroSlider); heroSwiper.on("init", startProgress); heroSwiper.on("slideChange", startProgress); heroSwiper.init(); } else if (window.innerWidth > 767 && heroSwiper) { heroSwiper.destroy(true, true); heroSwiper = null; clearInterval(progressInterval); if (observer) observer.disconnect(); progressLine.style.width = "0%"; } } initHeroSwiper(); window.addEventListener("resize", () => { clearTimeout(window.__heroResizeTimer); window.__heroResizeTimer = setTimeout(initHeroSwiper, 300); }); }); /* ============================================================================= Slider for scorecard block mobile devices with timeline END ============================================================================= */ /* ============================================================================= 8. Simple Custom Tabs (.tab-wrapper) START /* ============================================================ AUTO TABS + PROGRESS + MOBILE SWIPER (≤800px) Fully rebuilt, optimized, with complete mobile support =============================================================== */ (() => { const { onReady } = window.__DF_UTILS__ || { onReady: (fn) => { if (document.readyState !== "loading") fn(); else document.addEventListener("DOMContentLoaded", fn); }, }; onReady(() => { const tabs = Array.from(document.querySelectorAll(".menu_tab")); const panels = Array.from(document.querySelectorAll(".content_tab")); const progressLine = document.querySelector(".active-progress-line"); if (!tabs.length || !panels.length || !progressLine) return; // Mobile slider container const mobileSlider = document.querySelector(".menu-tabs-slider"); let currentIndex = 0; const duration = 11500; let rAF = null; let startTs = 0; let elapsedBeforePause = 0; let paused = false; let swiper = null; /* ---------------------------- RESET PROGRESS -----------------------------*/ function resetProgress() { progressLine.style.width = "0%"; cancelAnimationFrame(rAF); elapsedBeforePause = 0; startTs = 0; } /* ---------------------------- START PROGRESS -----------------------------*/ function startProgress() { cancelAnimationFrame(rAF); startTs = 0; const tick = (ts) => { if (paused) return (rAF = requestAnimationFrame(tick)); if (!startTs) startTs = ts; const elapsed = elapsedBeforePause + (ts - startTs); const pct = Math.min((elapsed / duration) * 100, 100); progressLine.style.width = pct + "%"; if (pct >= 100) { goToNextTab(); return; } rAF = requestAnimationFrame(tick); }; rAF = requestAnimationFrame(tick); } /* ---------------------------- SWITCH TAB -----------------------------*/ function switchTab(index) { currentIndex = index; tabs.forEach((t) => t.classList.remove("is-active")); panels.forEach((p) => p.classList.remove("is-active", "visible-anime")); const tab = tabs[index]; const panel = panels[index]; if (tab) tab.classList.add("is-active"); if (panel) { panel.classList.add("is-active"); void panel.offsetWidth; panel.classList.add("visible-anime"); } if (swiper && swiper.activeIndex !== index) { swiper.slideTo(index); } resetProgress(); startProgress(); } /* ---------------------------- NEXT TAB -----------------------------*/ function goToNextTab() { const next = currentIndex + 1 < panels.length ? currentIndex + 1 : 0; switchTab(next); } /* ---------------------------- TAB CLICK (desktop + mobile) -----------------------------*/ tabs.forEach((tab, i) => { tab.addEventListener("click", () => switchTab(i)); }); /* ---------------------------- MOBILE SWIPER (≤800) -----------------------------*/ function initMobileSwiper() { if (!mobileSlider || window.innerWidth > 800) return; mobileSlider.classList.add("swiper"); swiper = new Swiper(".menu-tabs-slider", { slidesPerView: "auto", spaceBetween: 14, freeMode: false, resistanceRatio: 0.8, }); swiper.on("slideChange", () => { const idx = swiper.activeIndex; switchTab(idx); }); } /* ---------------------------- PAUSE ON HOVER (desktop only) -----------------------------*/ const hoverTarget = document.querySelector(".block-card-design") || document.querySelector(".content_tab_wrap"); if (hoverTarget && window.innerWidth > 800) { hoverTarget.addEventListener("mouseenter", () => { paused = true; const w = parseFloat(progressLine.style.width) || 0; elapsedBeforePause = (w / 100) * duration; }); hoverTarget.addEventListener("mouseleave", () => { paused = false; startTs = 0; }); } /* ---------------------------- INIT -----------------------------*/ initMobileSwiper(); switchTab(0); }); })(); /* ============================================================================= 8. Simple Custom Tabs (.tab-wrapper) END ============================================================================= */ /* ========================================================================== 13. “Show More/Less” Button START ========================================================================== */ (function () { const LIMIT_ROWS = 2; const BREAKPOINT = 767; const BUTTON_HTML = `
`; function updateTeamGrid() { document.querySelectorAll(".content-grid-team").forEach((container) => { const items = Array.from( container.querySelectorAll(".card-team:not(.card-team-cta)") ); const cols = getComputedStyle(container).gridTemplateColumns.split(" ").length; const limit = cols * LIMIT_ROWS; const next = container.nextElementSibling; if (next?.classList.contains("show-more-wrapper")) next.remove(); items.forEach((i) => (i.style.display = "")); if (window.innerWidth <= BREAKPOINT && items.length > limit) { items.slice(limit).forEach((i) => (i.style.display = "none")); const wrapper = document.createElement("div"); wrapper.innerHTML = BUTTON_HTML; const btnWrap = wrapper.firstElementChild; container.parentNode.insertBefore(btnWrap, container.nextSibling); const btn = btnWrap.querySelector(".show-more"); const label = btn.querySelector(".button-md"); btn.addEventListener("click", (e) => { e.preventDefault(); const hidden = items.slice(limit); const isExpanded = btn.classList.toggle("expanded"); if (isExpanded) { hidden.forEach((i) => (i.style.display = "")); label.textContent = "Show less team"; } else { hidden.forEach((i) => (i.style.display = "none")); label.textContent = "Show more team"; container.scrollIntoView({ behavior: "smooth" }); } }); } }); } window.addEventListener("resize", updateTeamGrid); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", updateTeamGrid); } else { updateTeamGrid(); } })(); /* ========================================================================== 13. “Show More/Less” Button END ========================================================================== */ /* ========================================================================== Visual-solution-tabs START ========================================================================== */ window.Webflow ||= []; window.Webflow.push(() => { const tabWrapper = document.querySelector(".visual-solution-tabs"); if (!tabWrapper) return; const tabs = tabWrapper.querySelectorAll(".switch-visual-solution-tab"); const contents = tabWrapper.querySelectorAll( ".item-block-content-visual-tab" ); if (!tabs.length || !contents.length) return; contents.forEach((c) => c.classList.remove("is-active", "is-visible")); tabs[0].classList.add("is-active"); contents[0].classList.add("is-active", "is-visible"); tabs.forEach((tab, index) => { tab.addEventListener("click", () => { if (tab.classList.contains("is-active")) return; tabs.forEach((t) => t.classList.remove("is-active")); contents.forEach((c) => c.classList.remove("is-active", "is-visible")); tab.classList.add("is-active"); const target = contents[index]; if (target) { target.classList.add("is-active"); setTimeout(() => target.classList.add("is-visible"), 50); } }); }); }); /* ========================================================================== Visual-solution-tabs END ========================================================================== */ /**************************************** * Slider TESTIMONIAL TIME LINE mob START ***************************************/ document.addEventListener("DOMContentLoaded", () => { const sliders = document.querySelectorAll( ".flex-block-testimonial-and-pagin, .flex-block-testimonial-and-pagin-type-2" ); if (!sliders.length) return; sliders.forEach((slider) => { const swiper = new Swiper(slider, { slidesPerView: 1, effect: "fade", fadeEffect: { crossFade: true }, loop: true, autoplay: { delay: 11500, disableOnInteraction: false, }, speed: 400, }); const progressLine = slider.querySelector(".active-progress-line"); const swiperWrapper = slider.querySelector( ".swiper-wrapper.is-testimonial-line, .swiper-wrapper.is-tab" ); if (progressLine && swiperWrapper) { let progress = 0; const duration = 11500; let interval; let paused = false; function startProgress() { clearInterval(interval); progress = 0; progressLine.style.width = "0%"; interval = setInterval(() => { if (paused) return; progress += 100 / (duration / 20); progressLine.style.width = `${progress}%`; if (progress >= 100) clearInterval(interval); }, 20); } swiper.on("slideChangeTransitionStart", () => { progressLine.style.width = "0%"; }); swiper.on("slideChangeTransitionEnd", startProgress); swiperWrapper.addEventListener("mouseenter", () => { paused = true; swiper.autoplay.stop(); }); swiperWrapper.addEventListener("mouseleave", () => { paused = false; swiper.autoplay.start(); }); startProgress(); } }); }); /**************************************** * Slider TESTIMONIAL TIME LINE mob END ***************************************/ /**************************************** * Slider TAB TIME LINE mob START ***************************************/ document.addEventListener("DOMContentLoaded", () => { const sliders = document.querySelectorAll( ".flex-block-testimonial-and-pagin-tab" ); sliders.forEach((slider) => { if (slider.classList.contains("swiper-initialized")) return; const swiper = new Swiper(slider, { slidesPerView: 1, effect: "fade", fadeEffect: { crossFade: true }, loop: true, autoplay: { delay: 11500, disableOnInteraction: false, }, speed: 400, }); const progressLine = slider.parentElement.querySelector( ".active-progress-line" ); const wrapper = slider.querySelector(".swiper-wrapper.is-tab"); if (!progressLine || !wrapper) return; let progress = 0; const duration = 11500; let interval; let paused = false; function startProgress() { clearInterval(interval); progress = 0; progressLine.style.width = "0%"; interval = setInterval(() => { if (paused) return; progress += 100 / (duration / 20); progressLine.style.width = `${progress}%`; if (progress >= 100) clearInterval(interval); }, 20); } swiper.on("slideChangeTransitionStart", () => { progressLine.style.width = "0%"; }); swiper.on("slideChangeTransitionEnd", startProgress); wrapper.addEventListener("mouseenter", () => { paused = true; swiper.autoplay.stop(); }); wrapper.addEventListener("mouseleave", () => { paused = false; swiper.autoplay.start(); }); startProgress(); }); }); /* ============================================================================= 16. New scroll block component (pairs): desktop scrub & mobile reveal ============================================================================= */ (() => { const { onReady, waitFor } = window.__DF_UTILS__; onReady(() => { waitFor( () => typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined" ) .then(() => { const pairs = []; gsap.utils .toArray(".scrolling_content-and-media-new") .forEach((wrapper) => { const content = wrapper.querySelector(".scrolling_content-box-new"); const media = wrapper.querySelector(".scrolling_media_wrap-new"); if (content && media) pairs.push({ wrapper, content, media }); }); if (!pairs.length) { gsap.utils.toArray(".scrolling_content-box-new").forEach((box) => { const next = box.nextElementSibling; if (next?.matches(".scrolling_media_wrap-new")) { const wrapper = box.closest(".scrolling_content-and-media-new") || box.parentElement; pairs.push({ wrapper, content: box, media: next }); } }); } if (!pairs.length) return; const getOffset = (c, m) => m.getAttribute("data-media-offset") || c.getAttribute("data-media-offset") || "3rem"; pairs.forEach(({ content, media }) => { const off = getOffset(content, media); gsap.set(content, { opacity: 0 }); gsap.set(media, { opacity: 0, y: off }); }); function setupDesktop({ content, media }) { const D1 = 20, D2 = 40, D3 = 30; const offset = getOffset(content, media); const tl = gsap.timeline({ defaults: { ease: "none", overwrite: "auto" }, scrollTrigger: { trigger: content, start: "top 50%", end: "top 0%", scrub: true, invalidateOnRefresh: true, }, }); tl.fromTo(content, { opacity: 0 }, { opacity: 1, duration: D1 }) .to(content, { opacity: 1, duration: D2 }) .to(content, { opacity: 0, duration: D3 }); ScrollTrigger.create({ trigger: content, start: "top 50%", end: "top -11%", invalidateOnRefresh: true, onEnter: () => gsap.fromTo( media, { opacity: 0, y: offset }, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out", overwrite: "auto", } ), onEnterBack: () => gsap.fromTo( media, { opacity: 0, y: offset }, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out", overwrite: "auto", } ), onLeave: () => gsap.to(media, { opacity: 0, y: offset, duration: 0.3, ease: "power2.out", overwrite: "auto", }), onLeaveBack: () => gsap.to(media, { opacity: 0, y: offset, duration: 0.3, ease: "power2.out", overwrite: "auto", }), }); } function setupMobile({ wrapper, content, media }) { const offset = getOffset(content, media); gsap.set([content, media], { opacity: 0 }); gsap.set(media, { y: offset }); gsap .timeline({ defaults: { ease: "power2.out", overwrite: "auto" }, scrollTrigger: { trigger: wrapper || content, start: "top 50%", toggleActions: "play none none none", once: true, invalidateOnRefresh: true, }, }) .fromTo( [content, media], { opacity: 0, y: (i, el) => (el === media ? offset : "3rem") }, { opacity: 1, y: 0, duration: 0.8, stagger: 0 } ); } const mm = gsap.matchMedia(); mm.add("(min-width: 992px)", () => pairs.forEach(setupDesktop)); mm.add("(max-width: 991px)", () => pairs.forEach(setupMobile)); window.addEventListener("load", () => ScrollTrigger.refresh(), { passive: true, }); }) .catch(() => { /* no GSAP */ }); }); })(); /* ============================================================================= 14. Pagination hide state (robust observers) ============================================================================= */ (() => { const { onReady } = window.__DF_UTILS__; const PAGINATION_SELECTOR = ".pagination"; const COUNT_SELECTOR = ".w-page-count"; function normalize(text) { return (text || "") .replace(/\u00A0/g, " ") .replace(/\s+/g, " ") .trim(); } function readTotalPages(paginationEl) { const countEl = paginationEl.querySelector(COUNT_SELECTOR); if (!countEl) return null; const text = normalize(countEl.textContent); const m = text.match(/^(\d+)\s*\/\s*(\d+)$/); if (m) { const total = parseInt(m[2], 10); if (!Number.isNaN(total)) return total; } const aria = countEl.getAttribute("aria-label") || ""; const ariaMatch = aria.match(/of\s+(\d+)/i); if (ariaMatch) { const total = parseInt(ariaMatch[1], 10); if (!Number.isNaN(total)) return total; } return null; } function updateVisibility(paginationEl) { const total = readTotalPages(paginationEl); if (total === null) { paginationEl.removeAttribute("data-hidden"); return; } total <= 1 ? paginationEl.setAttribute("data-hidden", "true") : paginationEl.removeAttribute("data-hidden"); } function observeCount(paginationEl) { const countEl = paginationEl.querySelector(COUNT_SELECTOR); if (!countEl) return; updateVisibility(paginationEl); const mo = new MutationObserver(() => setTimeout(() => updateVisibility(paginationEl), 0) ); mo.observe(countEl, { subtree: true, childList: true, characterData: true, }); } function attach() { const paginationEl = document.querySelector(PAGINATION_SELECTOR); if (!paginationEl) return; observeCount(paginationEl); } onReady(attach); const outer = new MutationObserver(() => { const paginationEl = document.querySelector(PAGINATION_SELECTOR); if (!paginationEl) return; observeCount(paginationEl); }); outer.observe(document.body, { childList: true, subtree: true }); [ "fs-cmsfilter-update", "fs-cmsfilter-reset", "fs-cmsfilter-change", "fs-cmsload", ].forEach((evt) => { window.addEventListener(evt, () => setTimeout(attach, 0), { passive: true, }); }); })(); /**************************************** * Slider featured-new mob START ***************************************/ document.addEventListener("DOMContentLoaded", () => { const solutionSliders = document.querySelectorAll(".list-item-featured"); const featuredSliders = document.querySelectorAll(".list-item-featured-new"); const MOBILE_BREAKPOINT = 980; function initSlider(sliderEl) { const scrollbarEl = sliderEl.parentElement.querySelector(".swiper-scrollbar"); let swiperInstance = null; function createSwiper() { swiperInstance = new Swiper(sliderEl, { loop: false, slidesPerView: "auto", spaceBetween: 38, scrollbar: { el: scrollbarEl, hide: false, }, }); const drag = scrollbarEl.querySelector(".swiper-scrollbar-drag"); if (!drag) return; drag.style.transform = "none"; drag.style.left = "0"; drag.style.position = "absolute"; drag.style.pointerEvents = "none"; function updateScrollbarFill() { const progress = Math.max(0, Math.min(1, swiperInstance.progress || 0)); drag.style.width = progress * 100 + "%"; } updateScrollbarFill(); swiperInstance.on("setTranslate", updateScrollbarFill); swiperInstance.on("slideChange", updateScrollbarFill); swiperInstance.on("resize", updateScrollbarFill); } function destroySwiper() { if (!swiperInstance) return; swiperInstance.destroy(true, true); swiperInstance = null; const wrapper = sliderEl.querySelector(".swiper-wrapper"); if (wrapper) { wrapper.style.transform = ""; wrapper.style.transition = ""; } sliderEl.classList.remove( "swiper-initialized", "swiper-horizontal", "swiper-backface-hidden", "swiper-android" ); } function checkWidth() { if (window.innerWidth <= MOBILE_BREAKPOINT) { if (!swiperInstance) createSwiper(); } else { destroySwiper(); } } checkWidth(); window.addEventListener("resize", checkWidth); } solutionSliders.forEach(initSlider); featuredSliders.forEach(initSlider); }); /**************************************** * Sticky scroll anime CTA START ***************************************/ document.addEventListener("DOMContentLoaded", () => { // Early exit if the block does not exist on the page const wrappers = document.querySelectorAll( ".scrolling-block-animation-wrapper" ); if (!wrappers.length) return; // Guard: if GSAP or ScrollTrigger are not available, do nothing if (typeof gsap === "undefined" || typeof ScrollTrigger === "undefined") return; gsap.registerPlugin(ScrollTrigger); wrappers.forEach((wrapper) => { // Scope targets inside the current wrapper const widthTarget = wrapper.querySelector(".medium-item-image-scrolling"); // Collect ALL fade targets that should behave the same way const fadeTargets = [ wrapper.querySelector(".scrolling-active-content"), wrapper.querySelector(".mask-black-scrolling"), ].filter(Boolean); // remove nulls // Guard clause in case structure is incomplete if (!widthTarget || !fadeTargets.length) return; // Ensure fade targets start hidden (safe even if CSS already sets this) gsap.set(fadeTargets, { opacity: 0 }); // Build a scrubbed timeline tied to the scroll progress of the wrapper const tl = gsap.timeline({ defaults: { ease: "none" }, scrollTrigger: { trigger: wrapper, start: "top bottom", end: "bottom bottom", scrub: true, invalidateOnRefresh: true, // markers: true, }, }); // 0% → 80% of the timeline: grow width 40% → 100% // NOTE: Initial width (40%) should be set in CSS on .medium-item-image-scrolling tl.to(widthTarget, { width: "100%", duration: 0.8 }); // Fade in both elements in the same phase (like the original fadeTarget) tl.to(fadeTargets, { opacity: 1, duration: 0.3 }, 0.5); }); // Refresh after images/fonts load to get correct ScrollTrigger measurements window.addEventListener("load", () => { if (typeof ScrollTrigger !== "undefined") ScrollTrigger.refresh(); }); }); /**************************************** * GSAP SCROLL SECTION HERO SECTION PLATFORM *****************************************/ window.Webflow ||= []; window.Webflow.push(() => { // Guards if (typeof gsap === "undefined" || typeof ScrollTrigger === "undefined") { console.warn("GSAP or ScrollTrigger missing"); return; } // Check for element existence const marquee = document.querySelector(".group-marguee"); if (!marquee) return; gsap.registerPlugin(ScrollTrigger); // Initial state gsap.set(marquee, { rotate: 0, scale: 1, }); // Rotate + scale on scroll gsap.to(marquee, { rotate: -7, scale: 1.25, ease: "none", scrollTrigger: { trigger: marquee, start: "top 40%", end: "bottom top", scrub: 1.3, }, }); // Fade logo const logo = document.querySelector(".logo-platform"); if (logo) { gsap.to(logo, { opacity: 0, duration: 3, ease: "none", scrollTrigger: { trigger: marquee, start: "top 40%", end: "top 20%", scrub: true, }, }); } }); /*Code for Sticky card START */ document.addEventListener("DOMContentLoaded", function () { var wrapper = document.querySelector(".sticky-features_stack-wrapper"); if (!wrapper) return; // No wrapper - no script var stacks = Array.prototype.slice.call( wrapper.querySelectorAll(".sticky-features_stack") ); if (!stacks.length) return; var baseStepVh = 100; // 100vh per stack segment var extraTailVh = 50; // extra 20vh at the end var BLUR_MAX = 15; // max blur var totalStacks = stacks.length; var BREAKPOINT = 991; var stackData = []; var wrapperTop = 0; var wrapperHeightPx = 0; var stepHeightPx = 0; var enabled = false; var ticking = false; // Prepare internal data: index, z-index, image & content refs function buildStackData() { stackData = []; stacks.forEach(function (stack, i) { var index = i + 1; stack.classList.add("index-" + index); stack.dataset.index = index; // first stack on top stack.style.zIndex = String(totalStacks - i); // Move image itself var imageEl = stack.querySelector(".sticky-feature_image"); // Wrapper around image - used for blur effect (preferred) var imageWrapper = stack.querySelector(".sticky-feature_image-wrapper"); var content = stack.querySelector(".flex-content-left-media"); // Initial state for content: // - first item visible immediately // - others hidden & slightly to the right if (content) { if (index === 1) { content.style.opacity = "1"; content.style.transform = "translate3d(0px, 0, 0)"; } else { content.style.opacity = "0"; content.style.transform = "translate3d(40px, 0, 0)"; } } var isLast = index === totalStacks; stackData.push({ el: stack, image: imageEl, imageWrapper: imageWrapper, content: content, index: index, isLast: isLast, }); }); } function enableDesktop() { if (enabled) return; enabled = true; buildStackData(); // Set wrapper height: n * 100vh + 20vh wrapper.style.height = totalStacks * baseStepVh + extraTailVh + "vh"; recalc(); update(); // initial state } function disableDesktop() { if (!enabled) return; enabled = false; // Remove inline styles so mobile layout is clean wrapper.style.height = ""; stackData.forEach(function (item) { if (!item) return; if (item.el) { item.el.style.zIndex = ""; } if (item.image) { item.image.style.transform = ""; } if (item.imageWrapper) { item.imageWrapper.style.filter = ""; } if (item.content) { item.content.style.opacity = ""; item.content.style.transform = ""; } }); } function recalc() { if (!enabled) return; var rect = wrapper.getBoundingClientRect(); wrapperTop = rect.top + window.scrollY; wrapperHeightPx = rect.height; stepHeightPx = wrapperHeightPx / totalStacks || 1; } // Content fade/slide animation based on local progress 0..1 function animateContent(item, local) { if (!item.content) return; var el = item.content; // SPECIAL CASE: first item (index === 1) // It starts visible (no fade-in), stays visible most of the segment, // and only fades out near the end. if (item.index === 1) { var disappearStartFirst = 0.7; var disappearEndFirst = 1.0; var opacityFirst = 1; var xFirst = 0; if (local <= 0) { // Before segment: keep fully visible (matches initial state) opacityFirst = 1; xFirst = 0; } else if (local >= 1) { // After segment: fully hidden to the left opacityFirst = 0; xFirst = -40; } else if (local < disappearStartFirst) { // Inside main part: stay fully visible without extra animation opacityFirst = 1; xFirst = 0; } else { // Fade out + slide left only in 0.9..1 var tOutFirst = (local - disappearStartFirst) / (disappearEndFirst - disappearStartFirst || 0.0001); opacityFirst = 1 - tOutFirst; xFirst = -40 * tOutFirst; } el.style.opacity = opacityFirst.toFixed(3); el.style.transform = "translate3d(" + xFirst.toFixed(1) + "px, 0, 0)"; return; // do not run generic animation for first item } // GENERIC CASE: all other items (2, 3, ...) var opacity = 0; var x = 100; // slide from right if (local <= 0) { opacity = 0; x = 100; } else if (local >= 1) { opacity = 0; x = -40; } else { var appearStart = 0.0; var appearEnd = 0.01; var disappearStart = 0.7; var disappearEnd = 1.0; if (local < appearStart) { opacity = 0; x = 100; } else if (local < appearEnd) { var tIn = (local - appearStart) / (appearEnd - appearStart || 0.0001); opacity = tIn; x = 100 * (1 - tIn); // 40 -> 0 } else if (local < disappearStart) { opacity = 1; x = 0; } else if (local < disappearEnd) { var tOut = (local - disappearStart) / (disappearEnd - disappearStart || 0.0001); opacity = 1 - tOut; x = -40 * tOut; // 0 -> -40 } else { opacity = 0; x = -40; } } el.style.opacity = opacity.toFixed(3); el.style.transform = "translate3d(" + x.toFixed(1) + "px, 0, 0)"; } function update() { if (!enabled) { ticking = false; return; } ticking = false; var scrollTop = window.scrollY || window.pageYOffset; var relative = scrollTop - wrapperTop; // scroll inside wrapper (in px) // Above the wrapper: reset all images and content if (relative <= 0) { stackData.forEach(function (item, i) { if (item.image) { item.image.style.transform = "translate3d(0, 0%, 0)"; } if (item.content) { if (i === 0) { // First item visible before scroll item.content.style.opacity = "1"; item.content.style.transform = "translate3d(0px, 0, 0)"; } else { item.content.style.opacity = "0"; item.content.style.transform = "translate3d(40px, 0, 0)"; } } var blurTarget = item.imageWrapper || item.image; if (blurTarget) { // First stack clear, others blurred with BLUR_MAX var blurValue = i === 0 ? 0 : BLUR_MAX; blurTarget.style.filter = "blur(" + blurValue.toFixed(1) + "px)"; } }); return; } // Below the wrapper: lock all images at final state and remove blur if (relative >= wrapperHeightPx) { stackData.forEach(function (item) { if (item.image) { if (item.isLast) { item.image.style.transform = "translate3d(0, 0%, 0)"; } else { item.image.style.transform = "translate3d(0, -100%, 0)"; } } if (item.content) { item.content.style.opacity = "0"; item.content.style.transform = "translate3d(-40px, 0, 0)"; } var blurTarget = item.imageWrapper || item.image; if (blurTarget) { blurTarget.style.filter = "blur(0px)"; } }); return; } // Inside the wrapper: animate step by step var segmentIndex = Math.floor(relative / stepHeightPx); // 0..totalStacks-1 if (segmentIndex < 0) segmentIndex = 0; if (segmentIndex > totalStacks - 1) segmentIndex = totalStacks - 1; var segStart = segmentIndex * stepHeightPx; var localSeg = (relative - segStart) / stepHeightPx; var segProgress = Math.max(0, Math.min(localSeg, 1)); // 0..1 stackData.forEach(function (item, i) { var start = i * stepHeightPx; var local = (relative - start) / stepHeightPx; var clamped = Math.max(0, Math.min(local, 1)); // 0..1 per stack // Image Y animation (except last stack) if (item.image) { if (item.isLast) { item.image.style.transform = "translate3d(0, 0%, 0)"; } else { var yPercent = -100 * clamped; // 0 -> -100 item.image.style.transform = "translate3d(0," + yPercent + "%,0)"; } } // Content fade/slide (with special case for first item) animateContent(item, clamped); // Blur animation for current / next stack var blurTarget = item.imageWrapper || item.image; if (!blurTarget) return; var blurValue; if (i < segmentIndex) { // All previous stacks fully revealed blurValue = 0; } else if (i === segmentIndex) { // Current stack clear blurValue = 0; } else if (i === segmentIndex + 1 && segmentIndex < totalStacks - 1) { // Next stack: blur BLUR_MAX -> 0 in sync with current segment progress blurValue = BLUR_MAX * (1 - segProgress); } else { // Others still blurred blurValue = BLUR_MAX; } blurTarget.style.filter = "blur(" + blurValue.toFixed(1) + "px)"; }); } function onScroll() { if (!enabled) return; if (!ticking) { window.requestAnimationFrame(update); ticking = true; } } function handleResizeOrInit() { var isDesktop = window.innerWidth > BREAKPOINT; if (isDesktop && !enabled) { enableDesktop(); } else if (!isDesktop && enabled) { disableDesktop(); } else if (isDesktop && enabled) { recalc(); update(); } } // Listeners window.addEventListener("scroll", onScroll); window.addEventListener("resize", handleResizeOrInit); // Initial run handleResizeOrInit(); }); /*Code for Sticky card end */ /*Code SLIDER OLD TAB START */ document.addEventListener("DOMContentLoaded", () => { const sliders = document.querySelectorAll( ".flex-block-testimonial-and-pagin-slider" ); if (!sliders.length) return; sliders.forEach((slider) => { const wrapper = slider.querySelector(".swiper-wrapper"); if (!wrapper) return; const swiper = new Swiper(slider, { slidesPerView: 1, effect: "fade", fadeEffect: { crossFade: true }, loop: true, autoplay: { delay: 11500, disableOnInteraction: false, }, speed: 400, }); // Pause Swiper on hover wrapper.addEventListener("mouseenter", () => swiper.autoplay.stop()); wrapper.addEventListener("mouseleave", () => swiper.autoplay.start()); }); }); /*Code SLIDER OLD TAB END */ /*Form insert code attribute START*/ document.addEventListener("DOMContentLoaded", () => { // Find the wrapper const wrapper = document.querySelector(".form-request-wrapper"); if (!wrapper) return; // Find source element with URL const srcEl = wrapper.querySelector(".data-button-submit-url-src"); if (!srcEl) return; // Extract the URL text const urlValue = srcEl.textContent.trim(); if (!urlValue) return; // Find the submit button inside the form const submitBtn = wrapper.querySelector("[data-button-submit-url]"); if (!submitBtn) return; // Apply URL into data-button-submit-url attribute submitBtn.setAttribute("data-button-submit-url", urlValue); }); /*Form insert code attribute END*/