/* ========================================== DISABLE DEV MODES ========================================== */ document.addEventListener("DOMContentLoaded", () => { const devModes = document.querySelectorAll(".dev-edite-mode"); // Exit if elements not found if (!devModes.length) return; devModes.forEach((el) => { // Remove is-on if (el.classList.contains("is-on")) { el.classList.remove("is-on"); } // Remove is-active if (el.classList.contains("is-active")) { el.classList.remove("is-active"); } }); }); /* ========================== TAB CODE START ========================== */ document.addEventListener("DOMContentLoaded", () => { const items = document.querySelectorAll(".item-switch-menu-what-we-do"); const progressLine = document.querySelector(".line-progress-active"); if (!items.length) return; const isDesktop = window.innerWidth > 991; /* ======================================== SCROLL TO GLOBAL ANCHOR ======================================== */ function scrollToAnchor() { const anchor = document.querySelector("[ankor]"); if (!anchor) return; const OFFSET = 80; // під header (зміни якщо треба) // 🔥 Webflow-safe scroll requestAnimationFrame(() => { requestAnimationFrame(() => { const top = anchor.getBoundingClientRect().top + window.pageYOffset - OFFSET; window.scrollTo({ top, behavior: "smooth", }); }); }); } /* ===================================================== DESKTOP (TABS) ===================================================== */ if (isDesktop) { if (!progressLine) return; const STEP = 56 + 4; let activeIndex = [...items].findIndex((item) => item.querySelector(".switch-menu")?.classList.contains("is-on") ); if (activeIndex === -1) { const firstItem = items[0]; const firstButton = firstItem.querySelector(".switch-menu"); const firstContent = firstItem.querySelector( ".tab-content-what-we-do-wrapper" ); if (firstButton && firstContent) { firstButton.classList.add("is-on"); firstContent.classList.add("is-on"); activeIndex = 0; } } if (activeIndex >= 0) { progressLine.style.transform = `translateY(${activeIndex * STEP}px)`; } items.forEach((item, index) => { const button = item.querySelector(".switch-menu"); const content = item.querySelector(".tab-content-what-we-do-wrapper"); if (!button || !content) return; button.addEventListener("click", () => { items.forEach((el) => { el.querySelector(".switch-menu")?.classList.remove("is-on"); el.querySelector(".tab-content-what-we-do-wrapper")?.classList.remove( "is-on" ); }); button.classList.add("is-on"); content.classList.add("is-on"); progressLine.style.transform = `translateY(${index * STEP}px)`; }); }); } else { /* ===================================================== MOBILE / TABLET (ACCORDION) ===================================================== */ if (typeof window.jQuery === "undefined") return; const $ = window.jQuery; items.forEach((item, index) => { const button = item.querySelector(".switch-menu"); const content = item.querySelector(".tab-content-what-we-do-wrapper"); const arrow = item.querySelector(".arrow--dd-tab-mobe"); if (!button || !content) return; const $content = $(content); /* INIT */ $content.css({ height: "0px", overflow: "hidden", }); /* FIRST OPEN */ if (index === 0) { const fullHeight = content.scrollHeight; $content.addClass("is-open").css("height", fullHeight + "px"); if (arrow) { arrow.style.transform = "rotate(180deg)"; } } /* CLICK */ button.addEventListener("click", () => { const isOpen = $content.hasClass("is-open"); /* CLOSE ALL */ items.forEach((el) => { const c = el.querySelector(".tab-content-what-we-do-wrapper"); const a = el.querySelector(".arrow--dd-tab-mobe"); if (!c) return; $(c) .removeClass("is-open") .stop(true, true) .animate({ height: 0 }, 300); if (a) a.style.transform = "rotate(0deg)"; }); /* OPEN CURRENT */ if (!isOpen) { const fullHeight = content.scrollHeight; $content .addClass("is-open") .stop(true, true) .animate({ height: fullHeight }, 300); if (arrow) { arrow.style.transform = "rotate(180deg)"; } /* 🔥 СКРОЛ ДО ANCHOR */ setTimeout(() => { // force reflow (щоб Webflow не лагав) content.offsetHeight; scrollToAnchor(); }, 350); } }); }); } }); /* ========================== TAB CODE END ========================== */ /* ========================== HERO-LINE-PROCESS-START ========================== */ document.addEventListener("DOMContentLoaded", () => { const wrapper = document.querySelector(".right-content-timeline-process"); const line = document.querySelector(".active-line-process"); const lineTrack = document.querySelector(".line-process"); const items = document.querySelectorAll(".item-timeline-process"); if (!wrapper || !line || !lineTrack || !items.length) return; function updateTimeline() { const wrapperRect = wrapper.getBoundingClientRect(); const lineTrackRect = lineTrack.getBoundingClientRect(); const windowHeight = window.innerHeight; const startPoint = windowHeight * 0.6; let progress = (startPoint - wrapperRect.top) / wrapperRect.height; progress = Math.max(0, Math.min(progress, 1)); const translateY = (1 - progress) * -100; line.style.transform = `translateY(${translateY}%)`; const activeHeight = lineTrackRect.height * progress; items.forEach((item) => { const bullet = item.querySelector(".bullet-active-timeline"); const button = item.querySelector(".button-team"); const iconWrap = item.querySelector(".box-icon-team-wrap"); // 👈 NEW if (!bullet) return; const bulletRect = bullet.getBoundingClientRect(); const bulletCenter = bulletRect.top - lineTrackRect.top + bulletRect.height / 2; if (activeHeight >= bulletCenter) { bullet.classList.add("is-active-b"); if (button) button.classList.add("is-on-b"); if (iconWrap) iconWrap.classList.add("is-color"); // 👈 NEW } else { bullet.classList.remove("is-active-b"); if (button) button.classList.remove("is-on-b"); if (iconWrap) iconWrap.classList.remove("is-color"); // 👈 NEW } }); } window.addEventListener("scroll", updateTimeline, { passive: true }); window.addEventListener("resize", updateTimeline); updateTimeline(); }); /* ========================== HERO-LINE-PROCESS-END ========================== */ /* ========================== CARD-CS-LINE-PROCESS-START ========================== */ document.addEventListener("DOMContentLoaded", () => { const wrapper = document.querySelector(".right-content-timeline-process-cs"); const line = document.querySelector(".active-line-process-cs"); const lineTrack = document.querySelector(".line-process-cs"); const items = document.querySelectorAll(".item-timeline-process"); if (!wrapper || !line || !lineTrack || !items.length) return; function updateTimeline() { const wrapperRect = wrapper.getBoundingClientRect(); const lineTrackRect = lineTrack.getBoundingClientRect(); const windowHeight = window.innerHeight; const startPoint = windowHeight * 0.6; let progress = (startPoint - wrapperRect.top) / wrapperRect.height; progress = Math.max(0, Math.min(progress, 1)); const translateY = (1 - progress) * -100; line.style.transform = `translateY(${translateY}%)`; const activeHeight = lineTrackRect.height * progress; items.forEach((item) => { const bullet = item.querySelector(".bullet-active-timeline-cs"); const button = item.querySelector(".button-timeline-cs"); if (!bullet) return; const bulletRect = bullet.getBoundingClientRect(); const bulletCenter = bulletRect.top - lineTrackRect.top + bulletRect.height / 2; if (activeHeight >= bulletCenter) { bullet.classList.add("is-active-b"); if (button) button.classList.add("is-on-b"); } else { bullet.classList.remove("is-active-b"); if (button) button.classList.remove("is-on-b"); } }); } window.addEventListener("scroll", updateTimeline, { passive: true }); window.addEventListener("resize", updateTimeline); updateTimeline(); }); /* ========================== CARD-CS-LINE-PROCESS-END ========================== */ /* ========================== HERO-SLIDER-FORM-START ========================== */ document.addEventListener("DOMContentLoaded", () => { const slider = document.querySelector(".slider-with-form"); const slides = document.querySelectorAll(".swiper-slide.is-form"); const bulletsWrapper = document.querySelector(".swiper-bullet-wrapper"); if (!slider || !slides.length || !bulletsWrapper) return; bulletsWrapper.innerHTML = ""; slides.forEach((_, index) => { const bullet = document.createElement("div"); bullet.classList.add("swiper-bullet"); if (index === 0) bullet.classList.add("is_active_hero"); bulletsWrapper.appendChild(bullet); }); const bullets = bulletsWrapper.querySelectorAll(".swiper-bullet"); const swiper = new Swiper(slider, { slidesPerView: 1, spaceBetween: 0, speed: 600, loop: true, effect: "slide", grabCursor: true, autoplay: { delay: 5000, disableOnInteraction: false, pauseOnMouseEnter: true, }, on: { init: function () { updateBullets(this.realIndex); }, slideChange: function () { updateBullets(this.realIndex); }, }, }); function updateBullets(activeIndex) { bullets.forEach((bullet, index) => { bullet.classList.toggle("is_active_hero", index === activeIndex); }); } bullets.forEach((bullet, index) => { bullet.addEventListener("click", () => { swiper.slideToLoop(index); swiper.autoplay.start(); }); }); }); /* ========================== HERO-SLIDER-FORM-END ========================== */ document.addEventListener("DOMContentLoaded", () => { const list = document.querySelector(".stacked-card-team"); const cards = Array.from( list?.querySelectorAll(".item-stacked-card-team") || [] ); if (!list || !cards.length) return; const CONFIG = { triggerLineVh: 0.5, scaleStep: 0.008, yStep: 5, stickyTop: 144, // 9rem }; let tops = []; let ticking = false; // INIT cards.forEach((card, index) => { card.style.position = "sticky"; card.style.top = "9rem"; card.style.transformOrigin = "50% 0%"; card.style.willChange = "transform"; card.style.zIndex = index + 1; }); function measure() { tops = cards.map((card) => { const rect = card.getBoundingClientRect(); return rect.top + window.scrollY; }); update(); } function getActiveFloat() { if (tops.length <= 1) return 0; const trigger = window.scrollY + window.innerHeight * CONFIG.triggerLineVh; if (trigger <= tops[0]) return 0; if (trigger >= tops[tops.length - 1]) return tops.length - 1; for (let i = 0; i < tops.length - 1; i++) { const start = tops[i]; const end = tops[i + 1]; if (trigger >= start && trigger <= end) { const span = Math.max(end - start, 1); return i + (trigger - start) / span; } } return tops.length - 1; } function update() { ticking = false; const activeFloat = getActiveFloat(); cards.forEach((card, index) => { const rect = card.getBoundingClientRect(); // 🔥 is-active тільки коли реально sticky if (rect.top <= CONFIG.stickyTop + 1) { card.classList.add("is-active"); } else { card.classList.remove("is-active"); } // 🔥 КЛЮЧ: обмежуємо distance const rawDistance = activeFloat - index; const distance = Math.min( Math.max(rawDistance, 0), cards.length - 1 - index ); // ще не дійшла if (distance <= 0) { card.style.transform = "translate3d(0,0,0) scale(1)"; return; } // 🔥 рівні + плавність const level = Math.floor(distance); const progress = Math.min(distance - level, 1); // SCALE const scaleFrom = 1 - level * CONFIG.scaleStep; const scaleTo = 1 - (level + 1) * CONFIG.scaleStep; const scale = scaleFrom + (scaleTo - scaleFrom) * progress; // MOVE (вверх) const yFrom = -(level * CONFIG.yStep); const yTo = -((level + 1) * CONFIG.yStep); const y = yFrom + (yTo - yFrom) * progress; card.style.transform = `translate3d(0, ${y}px, 0) scale(${scale})`; }); } function requestUpdate() { if (ticking) return; ticking = true; requestAnimationFrame(update); } window.addEventListener("scroll", requestUpdate, { passive: true }); window.addEventListener("resize", measure); if (window.ResizeObserver) { const ro = new ResizeObserver(measure); cards.forEach((card) => ro.observe(card)); } measure(); });