/* ========================================================================== 1. Lottie Playback on Visibility & Hover Control ========================================================================== */ /*Play lottie when visible and contol hover START */ (function () { const origLoad = bodymovin.loadAnimation; bodymovin.loadAnimation = function (config) { const anim = origLoad(config); if (config.container) config.container.__lottieAnim = anim; return anim; }; })(); document.addEventListener("DOMContentLoaded", function () { const observer = new IntersectionObserver( (entries, observer) => { entries.forEach((entry) => { if (entry.isIntersecting) { const element = entry.target; const lottieSrc = element.getAttribute("data-lottie-src"); const playOnHover = element.hasAttribute("data-play-hover"); const loopLottie = element.hasAttribute("data-lottie-loop"); const rendererType = element.getAttribute("data-lottie-renderer") || "svg"; const animationConfig = { container: element, renderer: rendererType, path: lottieSrc, rendererSettings: { preserveAspectRatio: "xMidYMid slice", }, }; if (playOnHover) { animationConfig.loop = false; animationConfig.autoplay = false; const anim = bodymovin.loadAnimation(animationConfig); const parentWrapper = element.closest(".lottie-wrapper-hover"); if (parentWrapper) { parentWrapper.addEventListener("mouseenter", () => { anim.setDirection(1); anim.play(); }); parentWrapper.addEventListener("mouseleave", () => { anim.setDirection(-1); anim.play(); }); } } else { animationConfig.loop = loopLottie; animationConfig.autoplay = true; bodymovin.loadAnimation(animationConfig); } observer.unobserve(element); } }); }, { rootMargin: "0px", threshold: 0.1, } ); const lottieElements = document.querySelectorAll("[data-lottie-src]"); lottieElements.forEach((element) => { element.style.position = "relative"; element.style.width = "100%"; element.style.height = "100%"; element.style.overflow = "hidden"; observer.observe(element); }); }); /*Play lottie when visible and contol hover END */ /* ========================================================================== 3. Navbar Dropdown Open State Class Toggle ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { const navbar = document.querySelector(".navbar"); const toggles = document.querySelectorAll(".nuvbar-dropdown-toggle"); toggles.forEach((toggle) => { // observe class attribute changes on each toggle const mo = new MutationObserver(() => { const anyOpen = Array.from(toggles).some((t) => t.classList.contains("w--open") ); navbar.classList.toggle("dropdown-open", anyOpen); }); mo.observe(toggle, { attributes: true, attributeFilter: ["class"] }); }); }); /* ========================================================================== 4. Mobile Burger Menu Toggle ========================================================================== */ $(document).ready(() => { $("#burger-toggle").on("click", function () { $(".navbar-menu-wrapper").fadeToggle(300).toggleClass("is-open"); $(".navbar").toggleClass("is-open"); }); }); /* ========================================================================== 5. Navbar Hide/Reveal on Scroll ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { const THRESHOLD = 2; // scroll distance threshold (px) const OPEN_DELAY = 150; // delay before showing nav background (ms) const CLOSE_DELAY = 0; // delay before hiding nav background (ms) const header = document.querySelector(".content-navbar"); const bgLeft = document.querySelector(".navbar-bg-left"); const bgRight = document.querySelector(".navbar-bg-right"); const bgTop = document.querySelector(".bg-nuvbar-line"); let openTO = null; let closeTO = null; window.addEventListener("scroll", () => { const scrolled = window.pageYOffset; if (scrolled >= THRESHOLD) { // cancel any pending hide if (closeTO) { clearTimeout(closeTO); closeTO = null; } // immediately add visibility classes bgLeft.classList.add("is-open-me"); bgRight.classList.add("is-open-me"); bgTop.classList.add("is-open-me"); // delayed add of white background if (!openTO) { openTO = setTimeout(() => { header.classList.add("is-white-bg-navbar"); openTO = null; }, OPEN_DELAY); } } else { // cancel pending open if (openTO) { clearTimeout(openTO); openTO = null; } // immediately remove visibility classes bgLeft.classList.remove("is-open-me"); bgRight.classList.remove("is-open-me"); bgTop.classList.remove("is-open-me"); // delayed remove of background if (!closeTO) { closeTO = setTimeout(() => { header.classList.remove("is-white-bg-navbar"); closeTO = null; }, CLOSE_DELAY); } } }); }); /* ========================================================================== 6. Custom Dropdown Menu (Desktop vs. Mobile Mode) ========================================================================== */ $(function () { const MOBILE_BREAK = 991; function initDesktop($dd) { if ($dd.data("mode") === "desktop") return; $dd.data("mode", "desktop"); // move all panels into the main list const $panels = $dd.find(".hero-dd-menu-wrap").detach(); $dd.find(".list-main-menu").append($panels); // show desktop container $dd.find(".list-main_menu_wrapper").show(); // set initial active tab/panel const $tabs = $dd.find(".nav-link-tab"); const $panelsInside = $dd.find(".list-main-menu .hero-dd-menu-wrap"); $tabs .off("click.mobile") .removeClass("is-active") .first() .addClass("is-active"); $panelsInside .hide() .removeClass("active") .first() .show() .addClass("active"); // hover to switch panels $tabs.off("mouseenter.desktop").on("mouseenter.desktop", function (e) { e.preventDefault(); const $t = $(this); if ($t.hasClass("is-active")) return; const idx = $tabs.index(this); $tabs.removeClass("is-active"); $t.addClass("is-active"); $panelsInside .filter(".active") .removeClass("active") .stop(true, true) .fadeOut(0); $panelsInside.eq(idx).stop(true, true).fadeIn(300).addClass("active"); }); } function initMobile($dd) { if ($dd.data("mode") === "mobile") return; $dd.data("mode", "mobile"); // detach desktop panels const $panels = $dd.find(".list-main-menu .hero-dd-menu-wrap").detach(); // insert panels after each tab const $tabs = $dd.find(".nav-link-tab"); $tabs.each(function (i) { $panels.eq(i).insertAfter($(this)); }); // hide desktop container $dd.find(".list-main_menu_wrapper").hide(); // setup accordion behavior $tabs.off("mouseenter.desktop").removeClass("is-active"); const $inserted = $dd.find(".list-sidebar-links .hero-dd-menu-wrap"); $inserted.hide().removeClass("active"); $tabs.first().addClass("is-active"); $inserted.first().show().addClass("active"); $tabs.off("click.mobile").on("click.mobile", function (e) { e.preventDefault(); const $this = $(this); const $panel = $this.next(".hero-dd-menu-wrap"); const $container = $dd.find(".navbar-sidebar-left .list-sidebar-links"); const headerEl = document.querySelector("header") || document.querySelector(".navbar"); const headerH = headerEl ? headerEl.getBoundingClientRect().height : 0; const extraOff = 30; if ($this.hasClass("is-active")) { // close currently open $this.removeClass("is-active"); $panel.slideUp(300).removeClass("active"); } else { // close others $tabs.removeClass("is-active"); $inserted.filter(".active").slideUp(300).removeClass("active"); // open this one $this.addClass("is-active"); $panel .slideDown(300, () => { const top = $this.position().top; const scrollTarget = $container.scrollTop() + top - headerH - extraOff; $container.animate({ scrollTop: scrollTarget }, 300); }) .addClass("active"); } }); } function handleResponsive() { $(".big-dropdown").each(function () { $(window).width() > MOBILE_BREAK ? initDesktop($(this)) : initMobile($(this)); }); } handleResponsive(); $(window).on("resize", handleResponsive); }); /* ========================================================================== 7. Interactive Time Tabs with Video & Lottie Crossfade ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { const wrappers = document.querySelectorAll(".interactive-grid_wrapper"); if ( !wrappers.length || typeof gsap === "undefined" || typeof ScrollTrigger === "undefined" ) return; gsap.registerPlugin(ScrollTrigger); wrappers.forEach((wrapper) => { const duration = parseFloat(wrapper.dataset.stepDuration) || 6; const autoMediaHeight = wrapper.dataset.mediaWrapperAuto === "true"; const FADE_DURATION = parseFloat(wrapper.dataset.fadeDuration) || 0.5; const animateInterContent = wrapper.dataset.animeInterContent === "true"; const tabEls = Array.from(wrapper.querySelectorAll(".interactive-tab")); if (!tabEls.length) return; // Optional: slide-image-bg handling const slideImageBgId = wrapper.dataset.slideimageBg; const slideImageContainer = slideImageBgId ? document.getElementById(slideImageBgId) : null; const desktopBgImages = slideImageContainer ? Array.from( slideImageContainer.querySelectorAll(".bg-slide-image-desctop") ) : []; const mobileBgImages = slideImageContainer ? Array.from( slideImageContainer.querySelectorAll(".bg-slide-image-mobile") ) : []; // video background? let currentVideoEl = wrapper.dataset.videoBg === "true" ? document.getElementById("product-video") : null; // read tabs & their URLs const tabs = tabEls.map((el) => ({ container: el, hidden: el.querySelector(".interactive-tab_content_hidden"), visible: el.querySelector(".interactive-tab_content_visible"), content: el.querySelector(".interactive-tab_content"), bullet: el.querySelector(".bullets_active"), mediaWrapper: el.querySelector(".interactive-tab_media_wrap"), progressBar: el.querySelector(".interactive-progress"), progressWrapper: el.querySelector(".interactive-progress_wrap"), lottieEl: el.querySelector(".lottie-element"), bgDesktop: el.dataset.bgDesctop, bgMobile: el.dataset.bgMobile, contentInteractiveMedia: el.querySelector( ".content-in-interactive-media" ), })); let heightCache = []; let activeIndex = 0; let playTimer = null; let progressTween = null; let isAutoPlay = true; let isInViewport = false; let resizeDebounce = null; // measure hidden heights const measureHeights = () => { tabs.forEach((tab, i) => { const w = tab.hidden.getBoundingClientRect().width; gsap.set(tab.hidden, { height: "auto", width: `${w}px`, opacity: 1, position: "absolute", visibility: "hidden", }); heightCache[i] = tab.hidden.scrollHeight; gsap.set(tab.hidden, { clearProps: "height,width,opacity,position,visibility", }); }); }; measureHeights(); // crossfade between old and new video function crossfadeVideo(newUrl, initial = false) { if (!currentVideoEl) return; const srcEl = currentVideoEl.querySelector("source"); if (srcEl.src === newUrl) return; if (initial) { currentVideoEl.style.opacity = 0; srcEl.src = newUrl; currentVideoEl.load(); currentVideoEl.addEventListener("canplaythrough", function onReady() { currentVideoEl.removeEventListener("canplaythrough", onReady); currentVideoEl.play(); gsap.to(currentVideoEl, { opacity: 1, duration: FADE_DURATION, ease: "power2.out", }); }); return; } const newVideo = currentVideoEl.cloneNode(true); const newSrcEl = newVideo.querySelector("source"); newSrcEl.src = newUrl; Object.assign(newVideo.style, { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", objectFit: "cover", opacity: 0, }); currentVideoEl.parentNode.appendChild(newVideo); newVideo.addEventListener("canplaythrough", function onCan() { newVideo.removeEventListener("canplaythrough", onCan); newVideo.play(); gsap.to(newVideo, { opacity: 1, duration: FADE_DURATION, ease: "power2.out", }); gsap.to(currentVideoEl, { opacity: 0, duration: FADE_DURATION, ease: "power2.out", onComplete: () => { currentVideoEl.pause(); currentVideoEl.parentNode.removeChild(currentVideoEl); currentVideoEl = newVideo; }, }); }); newVideo.load(); } // preload all videos when wrapper nears viewport if (currentVideoEl) { const preloadStart = wrapper.dataset.preloadStart || "bottom 200px"; ScrollTrigger.create({ trigger: wrapper, start: preloadStart, once: true, onEnter: () => { tabs.forEach((tab) => { [tab.bgDesktop, tab.bgMobile].filter(Boolean).forEach((url) => { const link = document.createElement("link"); link.rel = "preload"; link.as = "video"; link.type = "video/mp4"; link.href = url; document.head.appendChild(link); }); }); }, }); } // handle resize: re-measure & refresh video window.addEventListener("resize", () => { clearTimeout(resizeDebounce); resizeDebounce = setTimeout(() => { measureHeights(); const active = tabs[activeIndex]; gsap.set(active.hidden, { height: "auto", opacity: 1 }); if (window.innerWidth < 991) { if (autoMediaHeight) { gsap.set(active.mediaWrapper, { height: "auto", opacity: 1 }); } else { gsap.set(active.mediaWrapper, { height: "80vw", overflow: "hidden", opacity: 1, }); } } else { gsap.set(active.mediaWrapper, { clearProps: "height", opacity: 1 }); } if (currentVideoEl) { const url = window.innerWidth > 991 ? active.bgDesktop : active.bgMobile; crossfadeVideo(url); } }, 100); }); // progress & autoplay helpers const clearPlayTimer = () => { clearTimeout(playTimer); playTimer = null; }; const clearProgressTween = () => { progressTween?.kill(); progressTween = null; }; const startProgress = () => { if (!isAutoPlay || !isInViewport) return; const { progressBar, progressWrapper } = tabs[activeIndex]; clearPlayTimer(); clearProgressTween(); gsap.set(progressWrapper, { opacity: 1 }); gsap.set(progressBar, { opacity: 1, height: 0 }); progressTween = gsap.fromTo( progressBar, { height: 0 }, { height: "100%", ease: "none", duration } ); playTimer = setTimeout( () => activateTab((activeIndex + 1) % tabs.length, false), duration * 1000 ); }; const stopProgress = () => { clearPlayTimer(); clearProgressTween(); }; // viewport trigger for autoplay start/stop const scrollStart = wrapper.dataset.scrollStart || "top center"; const scrollEnd = wrapper.dataset.scrollEnd || "bottom center"; ScrollTrigger.create({ trigger: wrapper, start: scrollStart, end: scrollEnd, onEnter: () => { isInViewport = true; startProgress(); }, onEnterBack: () => { isInViewport = true; startProgress(); }, onLeave: () => { isInViewport = false; stopProgress(); }, onLeaveBack: () => { isInViewport = false; stopProgress(); }, }); // reset all tabs const resetTabs = () => { tabs.forEach((tab) => { tab.container.classList.remove("is-interactive-active"); gsap.set(tab.hidden, { clearProps: "height,opacity,width" }); gsap.set(tab.mediaWrapper, { clearProps: "height,opacity" }); gsap.set(tab.progressWrapper, { clearProps: "opacity" }); gsap.set(tab.progressBar, { clearProps: "height,opacity" }); gsap.set(tab.content, { clearProps: "opacity" }); gsap.set(tab.bullet, { clearProps: "opacity" }); if (animateInterContent && tab.contentInteractiveMedia) { gsap.set(tab.contentInteractiveMedia, { clearProps: "opacity,y,zIndex,position", }); } // clear slide-image-bg states desktopBgImages.forEach((img) => img.classList.remove("is-active-bg")); mobileBgImages.forEach((img) => img.classList.remove("is-active-bg")); }); }; // activate a tab function activateTab(index, userClicked, initial = false) { activeIndex = index; resetTabs(); clearPlayTimer(); clearProgressTween(); const tab = tabs[index]; tab.container.classList.add("is-interactive-active"); // fade in content, bullet & visible gsap.to(tab.content, { opacity: 1, duration: 0.4, ease: "power2.out" }); gsap.to(tab.bullet, { opacity: 1, duration: 0.4, ease: "power2.out" }); gsap.to(tab.visible, { opacity: 1, duration: 0.4, ease: "power2.out" }); // expand hidden panel gsap.fromTo( tab.hidden, { height: 0, opacity: 0 }, { height: `${heightCache[index]}px`, opacity: 1, duration: 0.5, ease: "power2.out", onComplete: () => gsap.set(tab.hidden, { height: "auto" }), } ); // mobile mediaWrapper animation if (window.innerWidth < 991) { if (autoMediaHeight) { const mw = tab.mediaWrapper; const w = mw.getBoundingClientRect().width; gsap.set(mw, { height: "auto", width: `${w}px`, position: "absolute", visibility: "hidden", }); const targetH = mw.scrollHeight; gsap.set(mw, { clearProps: "width,position,visibility", height: 0, overflow: "hidden", opacity: 1, }); gsap.to(mw, { height: `${targetH}px`, duration: 0.5, ease: "power2.out", onComplete: () => gsap.set(mw, { height: "auto" }), }); } else { gsap.to(tab.mediaWrapper, { height: "80vw", overflow: "hidden", opacity: 1, duration: 0.5, ease: "power2.out", }); } } else { gsap.to(tab.mediaWrapper, { opacity: 1, duration: 0.5, ease: "power2.out", }); } // update background video if (currentVideoEl) { const url = window.innerWidth > 991 ? tab.bgDesktop : tab.bgMobile; crossfadeVideo(url, initial); } // animate inter content if enabled & on desktop if ( animateInterContent && window.innerWidth >= 991 && tab.contentInteractiveMedia ) { tab.contentInteractiveMedia.style.position = "relative"; tab.contentInteractiveMedia.style.zIndex = "10"; gsap.fromTo( tab.contentInteractiveMedia, { y: 40, opacity: 0 }, { y: 0, opacity: 1, duration: 0.5, ease: "power2.out" } ); } // slide-image-bg: activate matching image if (slideImageContainer) { if (window.innerWidth >= 991) { const img = desktopBgImages[index]; if (img) img.classList.add("is-active-bg"); } else { const img = mobileBgImages[index]; if (img) img.classList.add("is-active-bg"); } } // progress bar logic if (userClicked) { isAutoPlay = false; gsap.set(tab.progressWrapper, { opacity: 1 }); gsap.set(tab.progressBar, { opacity: 1, height: "100%" }); } else { startProgress(); } // scroll into view on mobile click if (userClicked && window.innerWidth < 991) { const header = document.querySelector("header") || document.querySelector(".navbar"); const headerH = header ? header.getBoundingClientRect().height : 0; const extraOff = 30; const topY = tab.container.getBoundingClientRect().top + window.pageYOffset; window.scrollTo({ top: topY - headerH - extraOff, behavior: "smooth" }); } // restart Lottie if present if (tab.lottieEl?.__lottieAnim) { tab.lottieEl.__lottieAnim.goToAndPlay(0, true); } } // attach click handlers tabs.forEach((tab, i) => { const clickArea = tab.container.querySelector( ".interactive-tab_content_wrap" ); if (clickArea) clickArea.addEventListener("click", () => activateTab(i, true)); }); // initial activation (with video fade-in & inter-content if present) activateTab(0, false, true); }); }); /* END */ /* ========================================================================== 8. Hide Last Progress Bullet If No Hidden Content ========================================================================== */ document.addEventListener("DOMContentLoaded", updateLastBulletVisibility); window.addEventListener("resize", updateLastBulletVisibility); function updateLastBulletVisibility() { const wide = window.innerWidth > 991; document.querySelectorAll(".interactive-tab-list").forEach((list) => { const tabs = list.querySelectorAll(".interactive-tab"); if (!tabs.length) return; const last = tabs[tabs.length - 1]; const hidden = last.querySelector(".interactive-tab_content_hidden"); const progressWrap = last.querySelector(".interactive-progress_wrap"); if (!progressWrap) return; if ( wide && hidden && hidden.textContent.trim() === "" && hidden.children.length === 0 ) { // on wide screens & empty hidden panel → hide progress progressWrap.style.display = "none"; } else { // otherwise restore default progressWrap.style.display = ""; } }); } /* ========================================================================== 9. Simple Custom Tabs ========================================================================== */ document.querySelectorAll(".tab-wrappper").forEach((wrapper) => { const tabs = wrapper.querySelectorAll( ".menu_tab, .switch_tab, .tab-img_switch" ); const panels = wrapper.querySelectorAll(".content_tab"); tabs.forEach((tab, idx) => { tab.addEventListener("click", () => { tabs.forEach((t) => t.classList.remove("is-active")); tab.classList.add("is-active"); panels.forEach((p) => p.classList.remove("is-active", "visible-anime")); const target = panels[idx]; if (!target) return; target.classList.add("is-active"); // force reflow for CSS animation void target.offsetWidth; target.classList.add("visible-anime"); }); }); // optionally activate the first tab if (tabs.length) tabs[0].click(); }); /* ========================================================================== 10. Mobile Sliders Initialization & Destruction ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { const BREAKPOINT = 768; const instances = new Map(); function initSliders() { document.querySelectorAll(".menu-tabs-slider").forEach((el) => { if (!instances.has(el)) { let space = parseInt(el.dataset.sliderSpace, 10); if (isNaN(space)) space = 8; const swiper = new Swiper(el, { slidesPerView: "auto", spaceBetween: space, }); instances.set(el, swiper); } }); document.querySelectorAll(".winter-slider").forEach((el) => { if (!instances.has(el)) { const swiper = new Swiper(el, { slidesPerView: 2.1, spaceBetween: 8, loop: true, pagination: { el: ".swiper-bullet-wrapper.is-slider-winter", clickable: true, bulletClass: "swiper-bullet-winter", bulletActiveClass: "is_active_winter", }, }); instances.set(el, swiper); } }); } function destroySliders() { instances.forEach((swiper, el) => { swiper.destroy(true, true); instances.delete(el); }); } function checkSliders() { window.innerWidth <= BREAKPOINT ? initSliders() : destroySliders(); } checkSliders(); window.addEventListener("resize", checkSliders); }); /* ========================================================================== 11. Scrolling Component with Sticky Media & Lottie Reset ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { const mm = gsap.matchMedia(); mm.add("(min-width: 992px)", () => { document.querySelectorAll(".scrolling_component").forEach((comp) => { comp.querySelectorAll(".scrolling_content-and-media").forEach((sec) => { ScrollTrigger.create({ trigger: sec, start: "top 50%", end: "bottom 50%", onEnter: () => activateSection(sec), onEnterBack: () => activateSection(sec), }); }); }); function activateSection(sec) { const parent = sec.closest(".scrolling_component"); parent .querySelectorAll(".scrolling_content-and-media") .forEach((s) => s.classList.remove("is-active-scrolling")); sec.classList.add("is-active-scrolling"); const lottie = sec.querySelector(".lottie-element"); if (lottie?.__lottieAnim) { lottie.__lottieAnim.goToAndPlay(0, true); } } return () => ScrollTrigger.getAll().forEach((t) => t.kill()); }); }); /* ========================================================================== 12. Card Popup Open/Close ========================================================================== */ $(document).ready(() => { $(".card-and-popup .click-view-popup").click(function (e) { e.preventDefault(); const $item = $(this).closest(".card-and-popup"); $item.find(".item-popup-wrap").fadeIn(); $("body").addClass("no-scroll"); }); $(".card-and-popup .close-popup").click(function () { const $item = $(this).closest(".card-and-popup"); $item.find(".item-popup-wrap").fadeOut(); $("body").removeClass("no-scroll"); }); }); /* ========================================================================== 13. Logos Grid “Show More/Less” Button ========================================================================== */ (function () { const LIMIT_ROWS = 5; const BREAKPOINT = 767; const BUTTON_HTML = `
`; function updateGrids() { document.querySelectorAll(".gid-with-logos").forEach((container) => { const items = Array.from(container.children); const cols = getComputedStyle(container).gridTemplateColumns.split(" ").length; const limit = cols * LIMIT_ROWS; // remove existing button if present const nxt = container.nextElementSibling; if (nxt?.classList.contains("show-more-wrapper")) nxt.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); if (btn.classList.contains("expanded")) { hidden.forEach((i) => (i.style.display = "none")); btn.classList.remove("expanded"); label.textContent = "Show more"; container.scrollIntoView({ behavior: "smooth" }); } else { hidden.forEach((i) => (i.style.display = "")); btn.classList.add("expanded"); label.textContent = "Show less"; } }); } }); } window.addEventListener("resize", updateGrids); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", updateGrids); } else { updateGrids(); } })(); /* ========================================================================== 14. Footer Slider with Responsive Pagination ========================================================================== */ /** Return the correct pagination selector based on screen width */ function getPaginationSelector() { return window.innerWidth < 768 ? ".slider-main_bottom-wrapper.desk-none .swiper-bullet-wrapper.is-slider-footer" : ".slider-and-paginations-footer .swiper-bullet-wrapper.is-slider-footer"; } const footerSwiper = new Swiper("#footer-slider-new", { slidesPerView: 2, spaceBetween: 16, grabCursor: true, a11y: true, loop: true, speed: 700, pagination: { el: getPaginationSelector(), clickable: true, bulletClass: "swiper-bullet", bulletActiveClass: "is_active_footer", }, breakpoints: { 992: { slidesPerView: 2, spaceBetween: 16 }, 768: { slidesPerView: 2.2, spaceBetween: 8 }, 0: { slidesPerView: 1.1, spaceBetween: 8 }, }, }); // update pagination on breakpoint change footerSwiper.on("breakpoint", (swiper) => { const newEl = getPaginationSelector(); if (swiper.params.pagination.el !== newEl) { swiper.pagination.destroy(); swiper.params.pagination.el = newEl; swiper.pagination.init(); swiper.pagination.render(); swiper.pagination.update(); } }); /* ========================================================================== 15. Continuous Marquee Initialization ========================================================================== */ function initMarquees(selector, speed) { const marquees = document.querySelectorAll(selector); if (!marquees.length) return; marquees.forEach((parent) => { const original = parent.innerHTML; // duplicate content twice for seamless loop parent.insertAdjacentHTML("beforeend", original); parent.insertAdjacentHTML("beforeend", original); let offset = 0; let paused = false; // uncomment if pause-on-hover is desired /* parent.addEventListener("mouseenter", () => { paused = true; }); parent.addEventListener("mouseleave", () => { paused = false; }); */ setInterval(() => { if (paused) return; const first = parent.firstElementChild; first.style.marginLeft = `-${offset}px`; if (offset > first.clientWidth) offset = 0; else offset += speed; }, 16); }); } document.addEventListener("DOMContentLoaded", () => { initMarquees(".marquee", 0.9); }); /* ========================================================================== 16. FAQ Accordion Toggle ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { // no FAQ items → do nothing if (!document.querySelector('[js-faq-collapse="true"]')) return; document.addEventListener("click", (event) => { const el = event.target.closest('[js-faq-collapse="true"]'); if (!el) return; if (!el.classList.contains("open")) { // close all other open items document .querySelectorAll('[js-faq-collapse="true"].open') .forEach((item) => { if (item !== el) item.click(); }); el.classList.add("open"); } else { el.classList.remove("open"); } }); }); /* ========================================================================== 17. Footer Dropdown Toggle Icon Animation ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { const toggles = document.querySelectorAll(".footer-dropdown-toggle"); if (!toggles.length) return; toggles.forEach((toggle) => { const vertLine = toggle.querySelector(".accordion-icon_vertical-line"); if (!vertLine) return; function sync() { vertLine.classList.toggle( "is-active", toggle.classList.contains("w--open") ); } sync(); const mo = new MutationObserver((muts) => { if ( muts.some((m) => m.type === "attributes" && m.attributeName === "class") ) { sync(); } }); mo.observe(toggle, { attributes: true }); }); }); /* ========================================================================== 18. Auto-Open First FAQ Item on Load ========================================================================== */ document.addEventListener("DOMContentLoaded", () => { const lists = document.querySelectorAll(".faq_list"); if (!lists.length) return; lists.forEach((list) => { const firstQ = list.querySelector(".faq_question, .faq-question"); if (firstQ) firstQ.click(); }); });