/* ========================================================= SAFE GSAP WRAPPERS (prevents "GSAP target not found") ========================================================= */ const safeSet = (targets, vars) => { // Accepts NodeList, Array, single Element, selector string // Runs only if something actually exists if (!targets) return; // If selector string if (typeof targets === "string") { const found = document.querySelectorAll(targets); if (found && found.length) gsap.set(found, vars); return; } // If single element if (targets.nodeType === 1) { gsap.set(targets, vars); return; } // If NodeList / Array-like if (targets.length) { gsap.set(targets, vars); } }; /* ========================================================= HERO animation START ========================================================= */ document.addEventListener("DOMContentLoaded", () => { /* ===================================== HERO SPLIT ===================================== */ document .querySelectorAll("[data-headline-split-appear-hero]") .forEach((el) => { new SplitType(el, { types: "words", lineClass: "word", }); }); const firstElsHero = document.querySelectorAll("[data-first-el-appear-hero]"); const opacityElsHero = document.querySelectorAll( "[data-opacity-el-appear-hero]" ); const linesHero = document.querySelectorAll( "[data-headline-split-appear-hero] .word" ); const secondElsHero = document.querySelectorAll( "[data-second-el-appear-hero]" ); if (firstElsHero.length || linesHero.length || secondElsHero.length) { const tlHero = gsap.timeline(); if (firstElsHero.length) { tlHero.to(firstElsHero, { opacity: 1, duration: 1, ease: "power2.out", }); } if (opacityElsHero.length) { tlHero.to(opacityElsHero, { opacity: 1, duration: 0 }, 0); } if (linesHero.length) { tlHero.to( linesHero, { y: 0, opacity: 1, duration: 1, ease: "power2.out", stagger: 0.2, }, 0.1 ); } if (secondElsHero.length) { tlHero.to( secondElsHero, { y: 0, opacity: 1, duration: 1, ease: "power2.out", stagger: 0.1, }, 0 ); } } /* ===================================== GLOBAL APPEAR FROM TOP ===================================== */ const appearTopEls = document.querySelectorAll("[data-animate-from-top]"); if (appearTopEls.length) { // You already had length checks here, so it's safe gsap.set(appearTopEls, { y: -40, opacity: 0, }); gsap.to(appearTopEls, { y: 0, opacity: 1, duration: 0.9, ease: "power2.out", stagger: 0.15, }); } /* ===================================== LINE DIAGONAL APPEAR ===================================== */ document.querySelectorAll("[data-split-line-appear]").forEach((el) => { new SplitType(el, { types: "lines", lineClass: "line", }); const lines = el.querySelectorAll(".line"); lines.forEach((line) => { const wrapper = document.createElement("div"); wrapper.classList.add("lines-wrap"); line.parentNode.insertBefore(wrapper, line); wrapper.appendChild(line); }); // Explicit initial state (extra safety) gsap.set(lines, { yPercent: 100, skewX: 20, opacity: 0, transformOrigin: "left bottom", }); // Animate gsap.to(lines, { yPercent: 0, skewX: 0, opacity: 1, duration: 1.2, ease: "power4.out", stagger: 0.1, force3D: true, onStart: () => { gsap.set(el, { opacity: 1 }); // show container only when animation starts }, }); }); /* ===================================== WIDTH BG REVEAL ===================================== */ const widthReveal = document.querySelectorAll("[data-width-reveal]"); if (widthReveal.length) { gsap.fromTo( widthReveal, { scaleX: 0, transformOrigin: "left center", }, { scaleX: 1, duration: 1.2, ease: "power4.out", } ); } }); /* ========================================================= HERO animation END ========================================================= */ /* ========================================================= ALL BLOCKS ANIMATION ON SCROLL START ========================================================= */ gsap.registerPlugin(ScrollTrigger); document.querySelectorAll("[data-animation-wrap]").forEach((wrap) => { /* ============================= HEADLINE WORD SPLIT ============================== */ const headlineEls = wrap.querySelectorAll("[data-headline-split-appear]"); if (headlineEls.length) { headlineEls.forEach((el) => { new SplitType(el, { types: "words", wordClass: "word" }); }); ScrollTrigger.refresh(); } /* ============================= SELECTORS ============================== */ const firstEls = wrap.querySelectorAll("[data-first-el-appear]"); const words = wrap.querySelectorAll("[data-headline-split-appear] .word"); const secondEls = wrap.querySelectorAll("[data-second-el-appear]"); const appearTopEls = wrap.querySelectorAll("[data-animate-from-top]"); const widthRevealEls = wrap.querySelectorAll("[data-scroll-width]"); const splitLineEls = wrap.querySelectorAll("[data-scroll-lines]"); const bgRevealEls = wrap.querySelectorAll("[data-scroll-bg-reveal]"); const scaleEls = wrap.querySelectorAll("[data-scroll-scale]"); const centerLineEls = wrap.querySelectorAll("[data-scroll-line-center]"); const clipEls = wrap.querySelectorAll("[data-scroll-clip]"); if ( !firstEls.length && !words.length && !secondEls.length && !appearTopEls.length && !widthRevealEls.length && !splitLineEls.length && !bgRevealEls.length && !scaleEls.length && !centerLineEls.length && !clipEls.length ) return; /* ============================= BASE STATES (SAFE) ============================== */ // These are the lines that caused console warnings when NodeLists were empty. // Now they run only if targets exist. safeSet(appearTopEls, { y: -40, opacity: 0 }); safeSet(widthRevealEls, { scaleX: 0, transformOrigin: "left center" }); safeSet(bgRevealEls, { backgroundSize: "0%" }); // Default scale state scaleEls.forEach((el) => { const direction = el.getAttribute("data-scroll-scale"); let origin = "50% 50%"; // default center switch (direction) { case "right": origin = "100% 100%"; break; case "left": origin = "0% 100%"; break; case "top": origin = "50% 0%"; break; case "bottom": origin = "50% 100%"; break; case "top-left": origin = "0% 0%"; break; case "top-right": origin = "100% 0%"; break; case "bottom-left": origin = "0% 100%"; break; case "bottom-right": origin = "100% 100%"; break; case "center": default: origin = "50% 50%"; } gsap.set(el, { scale: 0.7, transformOrigin: origin, }); }); // Center line reveal base safeSet(centerLineEls, { scaleX: 0, transformOrigin: "50% 50%", }); safeSet(clipEls, { clipPath: "inset(15% round 0rem)", }); /* ============================= TIMELINE ============================== */ // Custom start position from attribute value (e.g. "60%"), fallback to 80% const startPercent = wrap.getAttribute("data-animation-wrap"); const startValue = startPercent ? `top ${startPercent}` : "top 80%"; const tl = gsap.timeline({ scrollTrigger: { trigger: wrap, start: startValue, toggleActions: "play none none none", }, }); /* ----------------- FIRST ELEMENTS ------------------ */ if (firstEls.length) { tl.to(firstEls, { opacity: 1, y: 0, duration: 1, ease: "power2.out", onStart() { firstEls.forEach((el) => el.classList.add("is-animated")); }, }); } /* ----------------- WORDS STAGGER ------------------ */ if (words.length) { tl.to( words, { y: 0, opacity: 1, duration: 1, ease: "power2.out", stagger: 0.1, }, 0.1 ); } /* ----------------- SECOND ELEMENTS ------------------ */ if (secondEls.length) { tl.to( secondEls, { y: 0, opacity: 1, duration: 1, ease: "power2.out", stagger: 0.04, }, 0 ); } /* ----------------- APPEAR FROM TOP ------------------ */ if (appearTopEls.length) { tl.to( appearTopEls, { y: 0, opacity: 1, duration: 0.9, ease: "power2.out", stagger: 0.15, }, 0 ); } /* ----------------- SCALE IMAGE ------------------ */ if (scaleEls.length) { tl.to( scaleEls, { scale: 1, duration: 1, ease: "power3.out", }, 0 ); } /* ----------------- WIDTH REVEAL ------------------ */ if (widthRevealEls.length) { tl.to( widthRevealEls, { scaleX: 1, duration: 1.2, ease: "power4.out", }, 0 ); } /* ----------------- scroll-clip ------------------ */ clipEls.forEach((el) => { const insetValue = el.getAttribute("data-scroll-clip"); const startInset = insetValue ? insetValue : "15%"; gsap.set(el, { clipPath: `inset(${startInset} round 0rem)`, }); tl.to( el, { clipPath: "inset(0% round 0rem)", duration: 1, ease: "power3.out", }, 0 ); }); /* ----------------- LINE CENTER REVEAL ------------------ */ if (centerLineEls.length) { centerLineEls.forEach((el) => { // Get delay from attribute value const delayValue = el.getAttribute("data-scroll-line-center"); // If value exists → use it as number // If empty → fallback to 0 const delay = delayValue ? parseFloat(delayValue) : 0; tl.to( el, { scaleX: 1, duration: 0.8, ease: "power3.out", }, delay ); }); } /* ----------------- SPLIT LINES ------------------ */ if (splitLineEls.length) { splitLineEls.forEach((el) => { new SplitType(el, { types: "lines", lineClass: "line", }); const lines = el.querySelectorAll(".line"); lines.forEach((line) => { const wrapper = document.createElement("div"); wrapper.classList.add("lines-wrap"); line.parentNode.insertBefore(wrapper, line); wrapper.appendChild(line); }); tl.from( el.querySelectorAll(".line"), { yPercent: 100, skewX: 20, transformOrigin: "left bottom", opacity: 0, duration: 1.2, ease: "power4.out", stagger: 0.1, force3D: true, }, 0 ); }); } /* ----------------- BACKGROUND REVEAL ------------------ */ if (bgRevealEls.length) { tl.to( bgRevealEls, { backgroundSize: "100%", duration: 0.6, ease: "power2.out", }, ">-0.5" ); } /* ----------------- ANIME NUMBERS ------------------ */ const numEls = wrap.querySelectorAll("[anime-numbers]"); if (numEls.length) { numEls.forEach((el) => { const rawText = el.textContent.trim(); const match = rawText.match(/-?\d+(\.\d+)?/); if (!match) return; const endStr = match[0]; const endValue = parseFloat(endStr); const decimals = (endStr.split(".")[1] || "").length; const prefix = rawText.slice(0, match.index); const suffix = rawText.slice(match.index + endStr.length); const startValue = endValue * 0.7; const obj = { value: startValue }; tl.to( obj, { value: endValue, duration: 1.5, ease: "power1.out", onUpdate() { const current = decimals ? obj.value.toFixed(decimals) : Math.floor(obj.value); el.textContent = `${prefix}${current}${suffix}`; }, }, 0 ); }); } }); /* ============================================= ALL BLOCKS ANIMATION ON SCROLL END ============================================= */ /* ========================================================= Team Slider (Mobile & Tablet Only ≤ 991px) Safe init + Safe destroy START ========================================================= */ window.Webflow ||= []; window.Webflow.push(() => { const BREAKPOINT = 991; // Required elements const section = document.querySelector(".interactive-block-mob"); const wrapper = document.querySelector(".slider-team-wrapper"); const swiperWrapper = document.querySelector(".swiper-wrapper.is-team"); // ❗ Exit if structure not found if (!section || !wrapper || !swiperWrapper) return; if (typeof Swiper === "undefined") { console.warn("Swiper is not loaded"); return; } let teamSlider = null; function isMobile() { return window.innerWidth <= BREAKPOINT; } function initSlider() { if (teamSlider || !isMobile()) return; teamSlider = new Swiper(wrapper, { slidesPerView: 1.1, spaceBetween: 20, speed: 700, grabCursor: true, watchOverflow: true, breakpoints: { 480: { slidesPerView: 1.2, }, 768: { slidesPerView: 1.4, }, }, pagination: { el: ".swiper-pagination", clickable: true, }, }); } function destroySlider() { if (!teamSlider) return; teamSlider.destroy(true, true); teamSlider = null; } function handleResize() { if (isMobile()) { initSlider(); } else { destroySlider(); } } // Initial check handleResize(); // Resize listener window.addEventListener("resize", handleResize); }); /* ========================================================= Team Slider (Mobile & Tablet Only ≤ 991px) Safe init + Safe destroy END ========================================================= */