//Andriy code /*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"); } }); }); /* ========================================================= Bottom Bar (FULL) 1) Duplicate panel height synced to Webflow dropdown state 2) Label text synced to current (w--current) link - After first valid current is detected, NEVER fallback to "Menu" (keeps last label during "no-current" gaps while scrolling) 3) Mobile: after anchor navigation, close dropdown by clicking bar_dropdown-toggle ========================================================= */ window.Webflow ||= []; window.Webflow.push(() => { /* ========================== CONFIG ========================== */ const MOBILE_QUERY = "(max-width: 500px)"; const MOBILE_HEIGHT_VH = 60; const ANIMATION_DURATION = 280; const DEFAULT_TEXT = "Menu"; const CLOSE_AFTER_NAV_DELAY_MS = 0; // try 20-50 if you see flicker /* ========================== ELEMENTS ========================== */ const dropdown = document.querySelector(".menu-bottom-bar"); const dropdownToggle = document.querySelector(".bar_dropdown-toggle") || dropdown?.querySelector(".w-dropdown-toggle"); const duplicate = document.querySelector(".list-bottom-bar-dublicate"); const fixedBar = document.querySelector(".fixed-bottom-bar"); const label = document.querySelector(".text-bottom-bar"); const mq = window.matchMedia(MOBILE_QUERY); if (!dropdownToggle || !duplicate || !fixedBar || !label) { console.warn("Bottom bar elements not found"); return; } /* ========================== STATE (label) ========================== */ let hasEverHadCurrent = false; let lastValidLabel = DEFAULT_TEXT; /* ========================== HELPERS ========================== */ function isOpen() { const aria = dropdownToggle.getAttribute("aria-expanded"); if (aria === "true") return true; if (aria === "false") return false; return dropdownToggle.classList.contains("w--open"); } function getOpenHeight() { if (mq.matches) return window.innerHeight * (MOBILE_HEIGHT_VH / 100); return duplicate.scrollHeight; } function openDuplicate() { const height = getOpenHeight(); duplicate.style.transition = `height ${ANIMATION_DURATION}ms ease`; duplicate.style.height = `${height}px`; fixedBar.classList.add("is-on"); } function closeDuplicate() { duplicate.style.transition = `height ${ANIMATION_DURATION}ms ease`; duplicate.style.height = "0px"; fixedBar.classList.remove("is-on"); } function setLabel(text) { label.textContent = text; } function syncFromCurrent() { const current = duplicate.querySelector(".dropdown-link.w--current"); if (current) { const next = current.textContent.trim(); if (next) { hasEverHadCurrent = true; lastValidLabel = next; setLabel(next); return; } } // No current right now: // - Before user has ever reached any section: show DEFAULT ("Menu") // - After first real current: keep last label (NO fallback to Menu) if (!hasEverHadCurrent) { lastValidLabel = DEFAULT_TEXT; setLabel(DEFAULT_TEXT); } else { setLabel(lastValidLabel); } } /* ========================== INITIAL SYNC ========================== */ setLabel(DEFAULT_TEXT); requestAnimationFrame(() => { syncFromCurrent(); if (isOpen()) openDuplicate(); else closeDuplicate(); }); /* ========================== OBSERVE WEBFLOW STATE (aria-expanded) ========================== */ const ariaObserver = new MutationObserver(() => { if (isOpen()) openDuplicate(); else closeDuplicate(); }); ariaObserver.observe(dropdownToggle, { attributes: true, attributeFilter: ["aria-expanded", "class"], }); /* ========================== OBSERVE CURRENT LINK CHANGES (class) ========================== */ const classObserver = new MutationObserver((mutations) => { for (const m of mutations) { if ( m.type === "attributes" && m.attributeName === "class" && m.target instanceof Element && m.target.classList.contains("dropdown-link") ) { syncFromCurrent(); break; } } }); classObserver.observe(duplicate, { subtree: true, attributes: true, attributeFilter: ["class"], }); /* ========================== EXTRA SAFETY: periodic sync while scrolling (covers cases when class changes don't fire reliably) ========================== */ let rafId = null; function scheduleLabelSync() { if (rafId) return; rafId = requestAnimationFrame(() => { rafId = null; syncFromCurrent(); }); } window.addEventListener("scroll", scheduleLabelSync, { passive: true }); function closeAfterNav() { if (!mq.matches) return; if (!isOpen()) return; setTimeout(() => { if (isOpen()) dropdownToggle.click(); }, CLOSE_AFTER_NAV_DELAY_MS); } duplicate.addEventListener("click", (e) => { const link = e.target.closest(".dropdown-link"); if (!link) return; closeAfterNav(); // Also update label instantly on click (better UX) const txt = link.textContent?.trim(); if (txt) { hasEverHadCurrent = true; lastValidLabel = txt; setLabel(txt); } }); window.addEventListener("hashchange", closeAfterNav); mq.addEventListener("change", () => { if (isOpen()) openDuplicate(); }); }); /* ========================== CLICK DD CATEGORIES START ========================== */ /* ========================== CUSTOM DD CATEGORIES ========================== */ document.addEventListener("DOMContentLoaded", () => { const wrapper = document.querySelector(".custom-dd-categories-wrapper"); const list = document.querySelector(".block-list-categories"); const icon = document.querySelector(".icon-custom-dd"); const title = document.querySelector( ".box-title-dd-categories .label-20-semibold" ); const firstSwitch = document.querySelector( ".flex-filtres-left .switch_category" ); const switches = document.querySelectorAll(".switch_category"); if (!wrapper || !list) return; /* ========================== DEFAULT ACTIVE (FIRST) ========================== */ if (firstSwitch) { firstSwitch.classList.add("is-active"); const text = firstSwitch.querySelector(".w-form-label")?.textContent; if (text && title) title.textContent = text; } /* ========================== OPEN / CLOSE DD ========================== */ wrapper.addEventListener("click", (e) => { e.stopPropagation(); list.classList.toggle("is-on"); icon?.classList.toggle("is-rotate"); }); document.body.addEventListener("click", () => { list.classList.remove("is-on"); icon?.classList.remove("is-rotate"); }); list.addEventListener("click", (e) => { e.stopPropagation(); }); /* ========================== SWITCH CATEGORY ========================== */ switches.forEach((switchItem) => { switchItem.addEventListener("click", () => { const label = switchItem.querySelector(".w-form-label"); if (!label) return; const text = label.textContent; if (title) title.textContent = text; list.classList.remove("is-on"); icon?.classList.remove("is-rotate"); }); }); }); /* ========================== CLICK DD CATEGORIES END ========================== */ /* ========================== FAQ - NEW - START ========================== */ $(document).ready(function () { const ACTIVE_CLASS = "is-open"; const BG_CLASS = "is-bg"; const $accordions = $(".faq_accordion-new"); if (!$accordions.length) return; // Accordion that contains [data-open-faq] const $defaultAccordion = $accordions.has("[data-open-faq]").first(); function openAccordion($accordion) { const $answer = $accordion.find(".faq_answer-new"); $accordion.addClass(ACTIVE_CLASS).addClass(BG_CLASS); $answer.css("max-height", $answer.prop("scrollHeight") + "px"); } function closeAccordion($accordion) { const $answer = $accordion.find(".faq_answer-new"); $accordion.removeClass(ACTIVE_CLASS).removeClass(BG_CLASS); $answer.css("max-height", 0); } function refreshOpenHeights() { $accordions.each(function () { const $accordion = $(this); if ($accordion.hasClass(ACTIVE_CLASS)) { const $answer = $accordion.find(".faq_answer-new"); $answer.css("max-height", $answer.prop("scrollHeight") + "px"); } }); } function closeOthers($current) { $accordions.each(function () { const $accordion = $(this); // ❗ skip default accordion if (!$accordion.is($current) && !$accordion.is($defaultAccordion)) { closeAccordion($accordion); } }); } // Initial state $accordions.each(function () { const $accordion = $(this); const $answer = $accordion.find(".faq_answer-new"); $accordion.removeClass(ACTIVE_CLASS).removeClass(BG_CLASS); $answer.css("max-height", 0); }); // Open default accordion on start if ($defaultAccordion.length) { openAccordion($defaultAccordion); } $accordions.each(function () { const $accordion = $(this); const $question = $accordion.find(".faq_question-new"); $question.on("click", function () { const isDefault = $defaultAccordion.length && $accordion.is($defaultAccordion); const isOpen = $accordion.hasClass(ACTIVE_CLASS); // ❗ DEFAULT ACCORDION LOGIC if (isDefault) { if (isOpen) { closeAccordion($accordion); } else { openAccordion($accordion); } return; } // NORMAL ACCORDION if (isOpen) { closeAccordion($accordion); } else { closeOthers($accordion); openAccordion($accordion); } }); }); // Recalculate heights on resize $(window).on("resize", function () { refreshOpenHeights(); }); }); /* ========================== FAQ - NEW - END ========================== */ /* ========================== SLIDER BLOG and SOLUTION - START ========================== */ window.Webflow ||= []; window.Webflow.push(() => { const BREAKPOINT = 991; // slider works only 991px and below const SLIDERS = [ { wrapper: ".block-slider-blog-wrapper", swiper: ".swiper-wrapper.is-blog", }, { wrapper: ".slider-solution-wrapper", swiper: ".swiper-wrapper.is-solution", }, ]; const instances = new Map(); function isMobile() { return window.innerWidth <= BREAKPOINT; } function sliderExists(wrapperSelector, swiperSelector) { return ( document.querySelector(wrapperSelector) && document.querySelector(swiperSelector) ); } function initSlider(config) { if (!sliderExists(config.wrapper, config.swiper)) return; if (!isMobile()) return; if (instances.has(config.wrapper)) return; const wrapper = document.querySelector(config.wrapper); const instance = new Swiper(wrapper, { slidesPerView: "auto", spaceBetween: 8, speed: 800, grabCursor: true, watchOverflow: true, pagination: { el: wrapper.querySelector(".swiper-pagination"), clickable: true, }, navigation: { nextEl: wrapper.querySelector(".swiper-button-next"), prevEl: wrapper.querySelector(".swiper-button-prev"), }, breakpoints: { 0: { slidesPerView: "auto", }, 480: { slidesPerView: "auto", }, 768: { slidesPerView: "auto", }, }, }); instances.set(config.wrapper, instance); } function destroySlider(config) { if (!instances.has(config.wrapper)) return; const instance = instances.get(config.wrapper); instance.destroy(true, true); instances.delete(config.wrapper); } function handleResize() { SLIDERS.forEach((config) => { if (isMobile()) { initSlider(config); } else { destroySlider(config); } }); } handleResize(); window.addEventListener("resize", handleResize); }); /* ========================== SLIDER BLOG and SOLUTION - END ========================== */ /* ========================== TAB - SOLUTION - START ========================== */ document.addEventListener("DOMContentLoaded", () => { const cards = document.querySelectorAll(".card-transformation-wrap"); if (!cards.length) return; cards.forEach((card) => { const switches = card.querySelectorAll(".switch-transform"); const images = card.querySelectorAll(".image-transform"); if (!switches.length || !images.length) return; /* ================================ DEFAULT STATE (respect your HTML) ================================= */ let activeIndex = -1; switches.forEach((btn, index) => { if (btn.classList.contains("is-active")) { activeIndex = index; } }); // If no button has is-active → fallback to first if (activeIndex === -1) { activeIndex = 0; switches[0].classList.add("is-active"); } // Reset images images.forEach((img) => img.classList.remove("is-on")); // Activate correct image if (images[activeIndex]) { images[activeIndex].classList.add("is-on"); } /* ================================ HOVER LOGIC ================================= */ switches.forEach((btn, index) => { btn.addEventListener("mouseenter", () => { switches.forEach((s) => s.classList.remove("is-active")); images.forEach((img) => img.classList.remove("is-on")); btn.classList.add("is-active"); if (images[index]) { images[index].classList.add("is-on"); } }); }); }); }); /* ========================== TAB - SOLUTION - END ========================== */ /* ========================== TAB - WHY US - START ========================== */ document.addEventListener("DOMContentLoaded", () => { const wrappers = document.querySelectorAll(".table-and-switchs-wrapper"); if (!wrappers.length) return; wrappers.forEach((wrapper) => { const switches = wrapper.querySelectorAll(".switch-why-us"); // ❗ беремо тільки колонки БЕЗ is-first const columns = wrapper.querySelectorAll( ".colum-table-why-us:not(.is-first)" ); if (!switches.length || !columns.length) return; /* ================================ DEFAULT STATE (respect HTML) ================================= */ let activeIndex = -1; switches.forEach((btn, index) => { if (btn.classList.contains("is-active")) { activeIndex = index; } }); if (activeIndex === -1) { activeIndex = 0; switches[0].classList.add("is-active"); } columns.forEach((col) => { col.classList.remove("is-visible", "is-on"); }); if (columns[activeIndex]) { columns[activeIndex].classList.add("is-visible"); columns[activeIndex].classList.add("is-on"); } /* ================================ CLICK LOGIC ================================= */ switches.forEach((btn, index) => { btn.addEventListener("click", () => { switches.forEach((s) => s.classList.remove("is-active")); columns.forEach((col) => col.classList.remove("is-visible", "is-on")); btn.classList.add("is-active"); if (columns[index]) { columns[index].classList.add("is-visible"); columns[index].classList.add("is-on"); } }); }); }); }); /* ========================== TAB - WHY US - END ========================== */ /* ========================== SLIDER-IMPACT-START ========================== */ (function () { function initImpactSlider() { const sliderEl = document.querySelector(".slider-testimonial-impact"); if (!sliderEl) return; // prevent double init if (sliderEl.dataset.init === "true") return; sliderEl.dataset.init = "true"; // check Swiper if (typeof Swiper === "undefined") { console.warn("Swiper not found"); return; } const slides = sliderEl.querySelectorAll(".swiper-slide"); const bulletsWrapper = document.querySelector(".swiper-bullet-wrapper"); if (!slides.length || !bulletsWrapper) return; // ========================= // GENERATE BULLETS // ========================= bulletsWrapper.innerHTML = ""; slides.forEach(() => { const bullet = document.createElement("div"); bullet.className = "swiper-bullet"; const inner = document.createElement("div"); inner.className = "dotted-pagination-slider"; bullet.appendChild(inner); bulletsWrapper.appendChild(bullet); }); const bullets = bulletsWrapper.querySelectorAll(".swiper-bullet"); // ========================= // SETTINGS // ========================= const GAP = parseInt(sliderEl.getAttribute("data-gap")) || 24; // ========================= // INIT SWIPER // ========================= const swiper = new Swiper(sliderEl, { slidesPerView: 1, spaceBetween: GAP, speed: 600, autoHeight: true, watchSlidesProgress: true, // 👉 для prev/next класів observer: true, observeParents: true, on: { init: function () { updateBullets(this.activeIndex); }, slideChange: function () { updateBullets(this.activeIndex); }, }, }); // ========================= // BULLETS ACTIVE STATE // ========================= function updateBullets(index) { bullets.forEach((b, i) => { b.classList.toggle("is_active", i === index); }); } // ========================= // CLICK BULLETS // ========================= bullets.forEach((bullet, index) => { bullet.addEventListener("click", () => { swiper.slideTo(index); }); }); // ========================= // WEBFLOW FIX // ========================= setTimeout(() => swiper.update(), 100); } document.addEventListener("DOMContentLoaded", initImpactSlider); })(); /* ========================== SLIDER-IMPACT-END ========================== */ /* ========================== SLIDER-CAPABILITIES-START ========================== */ document.addEventListener("DOMContentLoaded", () => { let swiperInstance = null; const breakpoint = 991; function initSwiper() { const slider = document.querySelector(".slider-capabilities-wrapper"); // Safe guard if (!slider) return; const pagination = slider .closest(".slider-and-pagination-wrapper") ?.querySelector(".swiper-bullet-wrapper.is-slider-main"); if (!pagination) return; // Якщо вже ініціалізований → не дублюємо if (swiperInstance) return; swiperInstance = new Swiper(slider, { slidesPerView: 1, spaceBetween: 16, speed: 500, pagination: { el: pagination, clickable: true, bulletClass: "swiper-bullet", bulletActiveClass: "is_active", renderBullet: function (index, className) { return `