/* ===================================== DD NAVBAR ANIMATION BG LINK _ START ===================================== */ document.addEventListener("DOMContentLoaded", () => { if (!window.gsap) return; // Select ALL dropdown lists const dropdownLists = document.querySelectorAll(".navbar_dropdown-list"); if (!dropdownLists.length) return; dropdownLists.forEach((dropdownList) => { const backgrounds = dropdownList.querySelectorAll(".bg-dd-nav"); if (!backgrounds.length) return; function playAnimation() { gsap.killTweensOf(backgrounds); gsap.set(backgrounds, { scaleX: 0 }); gsap.to(backgrounds, { scaleX: 1, duration: 0.45, ease: "power2.out", stagger: 0.07, }); } const observer = new MutationObserver(() => { if (dropdownList.classList.contains("w--open")) { playAnimation(); } }); observer.observe(dropdownList, { attributes: true, attributeFilter: ["class"], }); }); }); /* ===================================== DD NAVBAR ANIMATION BG LINK _ END ===================================== */ /* ===================================== INTERACTIVE SLIDER _ START ===================================== */ window.Webflow ||= []; window.Webflow.push(() => { const ACTIVE_CLASS = "is-active"; const CARD = ".input-interactive-slider"; const CONTENT = ".inner-card-content"; const IMAGE_BOX = ".box-inner-image-card"; const TEXT = ".opacity-text-card"; const DESKTOP_BREAKPOINT = 992; // ====== CONTROL DELAYS HERE ====== const IMAGE_DELAY = 80; // delay before adding is-on (ms) const TEXT_DELAY = 330; // delay before text appears (ms) function isDesktop() { return window.innerWidth >= DESKTOP_BREAKPOINT; } function closeAll($cards) { $cards.removeClass(ACTIVE_CLASS); $cards.each(function () { const $image = $(this).find(IMAGE_BOX); const $text = $(this).find(TEXT); $image.removeClass("is-on"); $text.removeClass("is-visible"); }); if (!isDesktop()) { $cards.find(CONTENT).stop(true, true).slideUp(300); } } function openCard($card) { $card.addClass(ACTIVE_CLASS); const $image = $card.find(IMAGE_BOX); const $text = $card.find(TEXT); // IMAGE activation setTimeout(() => { $image.addClass("is-on"); }, IMAGE_DELAY); // TEXT fade-in setTimeout(() => { $text.addClass("is-visible"); }, TEXT_DELAY); if (!isDesktop()) { $card.find(CONTENT).stop(true, true).slideDown(300); } } function initInteractiveSlider() { const $cards = $(CARD); if (!$cards.length) return; const $defaultActive = $cards.filter("." + ACTIVE_CLASS).first(); closeAll($cards); if ($defaultActive.length) { openCard($defaultActive); } else { openCard($cards.first()); } // DESKTOP в†’ HOVER $cards.on("mouseenter", function () { if (!isDesktop()) return; const $hovered = $(this); if ($hovered.hasClass(ACTIVE_CLASS)) return; closeAll($cards); openCard($hovered); }); // MOBILE в†’ CLICK $cards.on("click", function () { if (isDesktop()) return; const $clicked = $(this); if ($clicked.hasClass(ACTIVE_CLASS)) return; closeAll($cards); openCard($clicked); }); } initInteractiveSlider(); }); /* ===================================== INTERACTIVE SLIDER _ END ===================================== */ /* ===================================== GSAP-TIMELINE-START ===================================== */ gsap.registerPlugin(ScrollTrigger); document.addEventListener("DOMContentLoaded", () => { const section = document.querySelector(".block-line-and-stcky"); const progress = document.querySelector(".progress-line-framework"); if (!section || !progress) return; // Fade in on enter (no scrub) ScrollTrigger.create({ trigger: section, start: "top 70%", onEnter: () => gsap.to(progress, { opacity: 1, duration: 0.25, overwrite: "auto" }), onLeaveBack: () => gsap.to(progress, { opacity: 0, duration: 0.2, overwrite: "auto" }), }); // Height growth with scrub gsap.fromTo( progress, { height: "3%" }, { height: "100%", ease: "none", scrollTrigger: { trigger: section, start: "top 70%", end: "bottom 40%", scrub: true, }, } ); }); /* ===================================== GSAP-TIMELINE-END ===================================== */ /* ===================================== OPEN MENU GUIDE START ===================================== */ document.addEventListener("DOMContentLoaded", () => { // ========================= // ELEMENTS // ========================= const openBtnLeft = document.querySelector(".icon-open-menu-left"); const menuLeft = document.querySelector(".block-left-menu-guide-wrap"); const menuItems = document.querySelectorAll(".item-menu-guide"); const innerDetail = document.querySelector(".inner-block-menu-guide-detail"); const openBtnRight = document.querySelector(".icon-open-menu-right"); const closeBtnRight = document.querySelector(".icon-closse"); // close button const menuRight = document.querySelector(".menu-guide-right-wrapper"); // ========================= // SETTINGS // ========================= const OPEN_DELAY = 250; const CLOSE_DELAY = 0; let timeoutId = null; // ========================= // LEFT MENU FUNCTIONS // ========================= function openLeftMenu() { if (!menuLeft) return; menuLeft.classList.add("is-on"); if (innerDetail) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { innerDetail.classList.add("is-visible"); }, OPEN_DELAY); } } function closeLeftMenu() { if (!menuLeft) return; if (innerDetail) { innerDetail.classList.remove("is-visible"); } clearTimeout(timeoutId); setTimeout(() => { menuLeft.classList.remove("is-on"); }, CLOSE_DELAY); } function toggleLeftMenu() { if (!menuLeft) return; const isOpen = menuLeft.classList.contains("is-on"); if (isOpen) { closeLeftMenu(); } else { openLeftMenu(); } } // ========================= // RIGHT MENU FUNCTIONS // ========================= function toggleRightMenu() { if (!menuRight) return; menuRight.classList.toggle("is-on"); } function closeRightMenu() { if (!menuRight) return; menuRight.classList.remove("is-on"); } // ========================= // LEFT BUTTON // ========================= if (openBtnLeft) { openBtnLeft.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); toggleLeftMenu(); }); } // ========================= // RIGHT BUTTON (OPEN / TOGGLE) // ========================= if (openBtnRight) { openBtnRight.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); toggleRightMenu(); }); } // ========================= // RIGHT CLOSE BUTTON // ========================= if (closeBtnRight) { closeBtnRight.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); closeRightMenu(); }); } // ========================= // CLICK ON LEFT MENU ITEM в†’ CLOSE LEFT // ========================= menuItems.forEach((item) => { item.addEventListener("click", () => { closeLeftMenu(); }); }); // ========================= // CLOSE RIGHT MENU ON TICK CLICK // ========================= document.addEventListener( "pointerdown", (e) => { const tick = e.target.closest(".supper-nav-tick"); if (!tick) return; closeRightMenu(); }, true ); // ========================= // CLICK OUTSIDE в†’ CLOSE MENUS // ========================= document.addEventListener( "click", (e) => { // LEFT if (menuLeft && openBtnLeft) { const isInside = menuLeft.contains(e.target); const isBtn = openBtnLeft.contains(e.target); if (!isInside && !isBtn) { closeLeftMenu(); } } // RIGHT if (menuRight && openBtnRight) { const isInside = menuRight.contains(e.target); const isBtn = openBtnRight.contains(e.target); if (!isInside && !isBtn) { closeRightMenu(); } } }, true ); }); /* ===================================== OPEN MENU GUIDE END ===================================== */ /*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"); } }); }); /* ============================================================================= 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 ============================================================================= */ /* ============================================================================= Sticky block - START ============================================================================= */ (function () { function initLockScrollSection() { const DESKTOP_BREAKPOINT = 992; if (window.innerWidth < DESKTOP_BREAKPOINT) return; const section = document.querySelector(".scroll-lock-section-wrapper"); if (!section) return; // Use a unique init flag to avoid collisions with any existing attributes if (section.dataset.lockscrollInit === "1") return; section.dataset.lockscrollInit = "1"; const mediaItems = section.querySelectorAll(".item-media-scroll"); const contentItems = section.querySelectorAll(".content-block-wrapper"); if (!mediaItems.length || !contentItems.length) return; const total = Math.min(mediaItems.length, contentItems.length); if (total < 2) return; const TRIGGER_RATIO = 0.25; let activeIndex = 0; let ticking = false; let isEnabled = true; // Auto z-index so the stack always works even if you add more items mediaItems.forEach((item, i) => { item.style.zIndex = String(i + 1); }); function setActive(index) { if (index === activeIndex) return; activeIndex = index; contentItems.forEach((item, i) => { item.classList.toggle("is-active", i === index); }); } function getOverlapRatio(el1, el2) { if (!el1 || !el2) return 0; const r1 = el1.getBoundingClientRect(); const r2 = el2.getBoundingClientRect(); const overlap = Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top); if (overlap <= 0) return 0; return overlap / r2.height; } function update() { if (!isEnabled) return; for (let i = 0; i < total - 1; i++) { const overlapRatio = getOverlapRatio(mediaItems[i], mediaItems[i + 1]); if (overlapRatio > TRIGGER_RATIO) { setActive(i + 1); } else if (i === activeIndex - 1) { setActive(i); } } } function requestTick() { if (!isEnabled) return; if (ticking) return; ticking = true; requestAnimationFrame(() => { update(); ticking = false; }); } function onResize() { const shouldEnable = window.innerWidth >= DESKTOP_BREAKPOINT; if (!shouldEnable) { isEnabled = false; window.removeEventListener("scroll", requestTick); window.removeEventListener("resize", onResize); // Allow re-init when coming back to desktop section.dataset.lockscrollInit = "0"; contentItems.forEach((item, i) => item.classList.toggle("is-active", i === 0) ); return; } update(); } // Init state contentItems.forEach((item, i) => item.classList.toggle("is-active", i === 0) ); update(); window.addEventListener("scroll", requestTick, { passive: true }); window.addEventListener("resize", onResize); } function boot() { initLockScrollSection(); } // Run on initial load if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } // Run on bfcache restore AND normal page show window.addEventListener("pageshow", () => { const section = document.querySelector(".scroll-lock-section-wrapper"); if (section) section.dataset.lockscrollInit = "0"; initLockScrollSection(); }); // Webflow-specific safety (re-render / page transitions) if (window.Webflow && Array.isArray(window.Webflow)) { window.Webflow.push(() => { const section = document.querySelector(".scroll-lock-section-wrapper"); if (section) section.dataset.lockscrollInit = "0"; initLockScrollSection(); }); } })(); /* ============================================================================= Sticky block - END ============================================================================= */ document.addEventListener("DOMContentLoaded", () => { const SHARE_BTN_SELECTOR = ".icon-social-box.is-share"; const badgeText = "Copied"; const hideDelay = 2500; const buttons = document.querySelectorAll(SHARE_BTN_SELECTOR); if (!buttons.length) return; function ensureBadge(btn) { let badge = btn.querySelector(".copy-badge"); if (!badge) { badge = document.createElement("div"); badge.className = "copy-badge"; badge.textContent = badgeText; btn.appendChild(badge); } return badge; } async function copyToClipboard(text) { // Modern API if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); return; } // Fallback const ta = document.createElement("textarea"); ta.value = text; ta.setAttribute("readonly", ""); ta.style.position = "fixed"; ta.style.top = "-9999px"; ta.style.left = "-9999px"; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta); } buttons.forEach((btn) => { const badge = ensureBadge(btn); let t = null; btn.addEventListener("click", async (e) => { e.preventDefault(); const url = window.location.href; try { await copyToClipboard(url); // show badge badge.classList.add("is-visible"); clearTimeout(t); t = setTimeout(() => badge.classList.remove("is-visible"), hideDelay); } catch (err) { // If you want, you can show another text here like "Error" console.warn("Copy failed:", err); } }); }); });