/* ═══════════════════════════════════════════════════════════════ УНИВЕРСАЛЬНЫЙ INTERSECTION OBSERVER Вставить в Zero Block → JS или в блок «HTML-код» Tilda. Принцип: вешается на .animate-on-scroll При попадании в viewport → добавляет .is-visible При prefers-reduced-motion → классы добавляются мгновенно ════════════════════════════════════════════════════════════ */ (function () { 'use strict'; var prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; /* ── УНИВЕРСАЛЬНЫЙ SCROLL-OBSERVER ── */ var scrollEls = document.querySelectorAll('.animate-on-scroll'); if (prefersReduced) { /* Мгновенно показываем всё без анимации */ scrollEls.forEach(function (el) { el.style.opacity = '1'; el.style.transform = 'none'; el.style.transition = 'none'; }); } else if ('IntersectionObserver' in window) { var observer = new IntersectionObserver( function (entries, obs) { entries.forEach(function (entry) { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); obs.unobserve(entry.target); } }); }, { threshold: 0.15, /* 15% элемента в viewport */ rootMargin: '0px 0px -40px 0px' /* Небольшой отступ снизу — элемент появляется чуть позже нижнего края */ } ); scrollEls.forEach(function (el) { observer.observe(el); }); } else { /* Fallback для старых браузеров */ scrollEls.forEach(function (el) { el.classList.add('is-visible'); }); } /* ── FAQ-АККОРДЕОН ── */ document.querySelectorAll('.faq-trigger').forEach(function (btn) { btn.addEventListener('click', function () { var item = btn.closest('.faq-item'); var body = item.querySelector('.faq-body'); var isOpen = item.classList.contains('is-open'); /* Закрываем все */ document.querySelectorAll('.faq-item.is-open').forEach(function (openItem) { var openBody = openItem.querySelector('.faq-body'); openItem.classList.remove('is-open'); openBody.style.height = '0'; }); /* Открываем текущий (если был закрыт) */ if (!isOpen) { item.classList.add('is-open'); body.style.height = body.scrollHeight + 'px'; /* После transition сбрасываем до 'auto' — корректно работает при ресайзе окна */ body.addEventListener('transitionend', function onEnd() { if (item.classList.contains('is-open')) { body.style.height = 'auto'; } body.removeEventListener('transitionend', onEnd); }); } }); }); /* ── COUNT-UP ДЛЯ STAT-ЦИФР ── Применять к элементам: 0 Zero Block или HTML-блок Tilda. JS обязателен. */ function easeOut(t) { return 1 - Math.pow(1 - t, 3); /* Cubic ease-out */ } function animateCount(el) { var target = parseInt(el.dataset.target, 10); var duration = 1200; /* --duration-cinematic */ var start = null; function step(timestamp) { if (!start) start = timestamp; var progress = Math.min((timestamp - start) / duration, 1); el.textContent = Math.floor(easeOut(progress) * target); if (progress < 1) { requestAnimationFrame(step); } else { el.textContent = target; /* Точное финальное значение */ } } requestAnimationFrame(step); } if (!prefersReduced) { var countEls = document.querySelectorAll('.count-up'); if ('IntersectionObserver' in window) { var countObserver = new IntersectionObserver( function (entries, obs) { entries.forEach(function (entry) { if (entry.isIntersecting) { animateCount(entry.target); obs.unobserve(entry.target); } }); }, { threshold: 0.5 } ); countEls.forEach(function (el) { countObserver.observe(el); }); } } else { /* При reduced-motion: сразу показать финальное значение */ document.querySelectorAll('.count-up').forEach(function (el) { el.textContent = el.dataset.target; }); } /* ── STICKY CTA (мобильный) ── */ var stickyCta = document.querySelector('.sticky-cta'); var heroSection = document.querySelector('.section--hero'); var finalCta = document.querySelector('.section--final-cta'); if (stickyCta && heroSection) { var stickyObserver = new IntersectionObserver( function (entries) { entries.forEach(function (entry) { /* Показываем когда Hero ушёл из viewport */ if (entry.target === heroSection) { if (!entry.isIntersecting) { stickyCta.classList.add('is-visible'); } else { stickyCta.classList.remove('is-visible'); } } /* Скрываем когда финальный CTA в viewport */ if (finalCta && entry.target === finalCta) { if (entry.isIntersecting) { stickyCta.classList.remove('is-visible'); } } }); }, { threshold: 0 } ); stickyObserver.observe(heroSection); if (finalCta) stickyObserver.observe(finalCta); } }());
Made on
Tilda