/* Navbar logo swap styles Start */ (function () { // Styles are injected immediately so both logos never show at once const css = ` .navbar_logo-link { display: grid; } .navbar_logo-link .navbar_logo_white, .navbar_logo-link .navbar_logo_black { grid-area: 1 / 1; transition: opacity 0.3s ease; } /* Default state (top of page): white logo */ .navbar_component .navbar_logo_black { opacity: 0; pointer-events: none; } /* Scrolled state: black logo */ .navbar_component.is-scroll_nav .navbar_logo_white { opacity: 0; pointer-events: none; } .navbar_component.is-scroll_nav .navbar_logo_black { opacity: 1; pointer-events: auto; } `; const style = document.createElement("style"); style.setAttribute("data-navbar-logo-swap", ""); style.textContent = css; document.head.appendChild(style); })(); /* Navbar logo swap styles END */ /* Navbar after scroll Start */ document.addEventListener("DOMContentLoaded", function () { const navbar = document.querySelector(".navbar_component"); if (!navbar) return; const isWhite = navbar.closest(".white-navbar") !== null; // If wrapper .white-navbar exists - add class immediately and disable scroll logic if (isWhite) { navbar.classList.add("is-scroll_nav"); return; } // Otherwise - add/remove class on scroll window.addEventListener("scroll", function () { if (window.scrollY > 800) { navbar.classList.add("is-scroll_nav"); } else { navbar.classList.remove("is-scroll_nav"); } }); }); /* Navbar after scroll END */ gsap.registerPlugin(ScrollTrigger); // 1. For each container with attribute data-animation-wrap document.querySelectorAll("[data-animation-wrap]").forEach((wrap) => { // 1.1 Split all headlines inside this wrap into words const headlineEls = wrap.querySelectorAll("[data-headline-split-appear]"); if (headlineEls.length) { headlineEls.forEach((el) => { // Here we change types to "words" and use wordClass new SplitType(el, { types: "words", wordClass: "word" }); }); // After updating the DOM (adding span.word), refresh ScrollTrigger positions ScrollTrigger.refresh(); } // 2. Collect element groups for animation const firstEls = wrap.querySelectorAll("[data-first-el-appear]"); // Animate .word instead of .line const words = wrap.querySelectorAll("[data-headline-split-appear] .word"); const secondEls = wrap.querySelectorAll("[data-second-el-appear]"); // 3. If there's nothing to animate - skip if (!firstEls.length && !words.length && !secondEls.length) return; // 4. Create timeline with ScrollTrigger for this wrap const tl = gsap.timeline({ scrollTrigger: { trigger: wrap, start: "top 80%", toggleActions: "play none none none", }, }); // 5. Animate the first group if (firstEls.length) { tl.from(firstEls, { opacity: 0, duration: 1, ease: "power2.out", }); } // 6. Animate words with stagger if (words.length) { tl.from( words, { y: 20, opacity: 0, duration: 1, ease: "power2.out", stagger: 0.1, }, 0.1 // start 0.1s after TL start ); } // 7. Animate second group if (secondEls.length) { tl.from( secondEls, { y: 20, opacity: 0, duration: 1, ease: "power2.out", stagger: 0.1, }, 0.4 ); } // 8. Number animation 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; el.textContent = decimals ? `${prefix}${startValue.toFixed(decimals)}${suffix}` : `${prefix}${Math.floor(startValue)}${suffix}`; const obj = { value: startValue }; tl.to( obj, { value: endValue, duration: 2.5, ease: "power1.out", onUpdate() { const current = decimals ? obj.value.toFixed(decimals) : Math.floor(obj.value); el.textContent = `${prefix}${current}${suffix}`; }, }, 0 ); }); } }); // Animate all elements with the same trigger attribute gsap.utils.toArray("[data-samefirts-el-appear]").forEach((el) => { gsap.from(el, { scrollTrigger: { trigger: el, // bind to the element itself start: "top 80%", // when the top of the element reaches 80% of viewport toggleActions: "play none none none", }, opacity: 0, duration: 1, ease: "power2.out", }); }); /* Tab navbars Start */ // For each .sub-nav-tabs block document.querySelectorAll(".sub-nav-tabs").forEach((wrapper) => { // Find only tabs and images inside this wrapper const tabs = wrapper.querySelectorAll(".tab-link"); const images = wrapper.querySelectorAll(".menu-link-image"); tabs.forEach((tab) => { tab.addEventListener("mouseenter", () => { const idx = tab.getAttribute("link-image-ixndex"); // Hide all images inside this wrapper images.forEach((img) => img.classList.remove("is-active")); // Show the one with the same index const target = wrapper.querySelector( `.menu-link-image[link-image-ixndex="${idx}"]` ); if (target) target.classList.add("is-active"); }); tab.addEventListener("mouseleave", () => { const idx = tab.getAttribute("link-image-ixndex"); const target = wrapper.querySelector( `.menu-link-image[link-image-ixndex="${idx}"]` ); if (target) target.classList.remove("is-active"); }); }); }); /* Tab navbars End */ /* Open-close faq items code START */ document.addEventListener("DOMContentLoaded", function () { // If there are no FAQ items on the page - do nothing if (!document.querySelector('[js-faq-collapse="true"]')) return; // Otherwise attach click handler document.addEventListener("click", function (event) { const element = event.target.closest('[js-faq-collapse="true"]'); if (element) { if (!element.classList.contains("open")) { // Close all opened items except the clicked one 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-close faq items code END */