/home/bdqbpbxa/dev-subdomains/precisai-dev.goodface.com.ua/wp-content/themes/precisai/main.js
addEventListener('load', (event) => {
const getMobileOperatingSystem = function () {
const n = navigator.userAgent || navigator.vendor || window.opera;
const isMobile = /windows phone/i.test(n) ? "Windows Phone" : /android/i.test(n) ? "Android" : !!/iPad|iPhone|iPod/.test(n) && "iOS"
return isMobile;
};
function playMobileMenu () {
const headerMenu = document.querySelector('.header-menu'),
mobMenuButton = document.querySelector('.burger-menu'),
mobMenuOverlay = document.querySelector('.mobile_menu_overlay'),
internalLink = document.querySelectorAll('.menu-header-menu > li'),
html = document.querySelector('html');
if(document.querySelectorAll('.burger-menu').length) {
mobMenuButton.addEventListener('click', toggleMenu);
mobMenuOverlay.addEventListener('click', toggleMenu);
function toggleMenu() {
mobMenuButton.classList.toggle('--active');
headerMenu.classList.toggle('--open');
html.classList.toggle('scrollLock');
}
internalLink.forEach(function(item) {
item.addEventListener('click', function(e) {
e.stopPropagation();
const width = window.innerWidth;
if(!getMobileOperatingSystem() && width > 1023) return;
setTimeout(function() {
toggleMenu();
}, 100);
})
});
}
}
playMobileMenu();
// Sticky header
let body = document.querySelector('body');
let header = document.querySelector('.header');
const isHeader = document.querySelectorAll('.header').length;
let startPosition = 500;
if (body.clientHeight > 1200 && isHeader) {
headerFixed();
}
let isFrontPage = body.classList.contains('body-front-page');
function headerFixed() {
const sections = document.querySelectorAll('[data-section-bg-theme]');
const headerHeight = header.offsetHeight;
window.addEventListener('scroll', function () {
const scrollPosition = window.scrollY;
if (scrollPosition >= startPosition) {
header.classList.add("active");
if (isFrontPage) {
updateHeaderTheme();
}
} else {
header.classList.remove("active");
if (isFrontPage) {
header.classList.remove('header-inversion');
}
}
});
function updateHeaderTheme() {
let headerInversionApplied = false;
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionBottom = sectionTop + section.offsetHeight;
const headerBottom = window.scrollY + headerHeight;
if (headerBottom >= sectionTop && window.scrollY <= sectionBottom) {
if (section.getAttribute('data-section-bg-theme') === 'light') {
header.classList.add('header-inversion');
headerInversionApplied = true;
}
}
});
if (!headerInversionApplied) {
header.classList.remove('header-inversion');
}
}
}
function showFaq() {
if(document.querySelectorAll('.faq-item').length) {
const faqs = document.querySelectorAll('.faq-item');
activeTab(faqs, '.faq-item__body', true)
}
if(document.querySelectorAll('.appliance-seo-text__list').length) {
const seoTextTabs = document.querySelectorAll('.appliance-seo-text__list');
activeTab(seoTextTabs, '.appliance-seo-text__text', false)
}
function activeTab(elems, elemsTextSelector, showFirstTab) {
elems.forEach(function(elem) {
elem.addEventListener('click', function(e) {
const elemBody = elem.querySelector(elemsTextSelector);
elem.classList.toggle('--show');
if (elemBody.style.maxHeight) {
elemBody.style.maxHeight = null;
} else {
elemBody.style.maxHeight = elemBody.scrollHeight + "px";
}
});
});
}
}
showFaq();
let isPopupOpen = false;
function popup({ popupSelector, overlaySelector, closeBtnSelector, openBtnSelector = null, autoOpen = false, autoOpenDelay = false, sourceButtonContent = false, contentContainerSelector = false }) {
const popup = document.querySelector(popupSelector),
closeBtn = popup.querySelector(closeBtnSelector),
overlay = document.querySelector(overlaySelector),
body = document.querySelector('body'),
header = document.querySelector('.header');
if (!popup || !overlay || !closeBtn) return;
let scrollWidthCompensation = calcScroll();
function showPopup(e) {
e.preventDefault();
isPopupOpen = true;
if(sourceButtonContent) {
insertContent(e);
}
body.classList.add('scroll-lock');
overlay.classList.add('--show');
popup.classList.add('--show');
body.style.marginRight = `${scrollWidthCompensation}px`;
header.style.paddingRight = `${scrollWidthCompensation}px`;
}
function closePopup() {
isPopupOpen = false;
body.classList.remove('scroll-lock');
overlay.classList.remove('--show');
popup.classList.remove('--show');
body.style.marginRight = '0px';
header.style.paddingRight = '0px';
}
function insertContent(e) {
const target = e.target; // Элемент, на который кликнули
const parentCard = target.closest(sourceButtonContent); // Используем класс из sourceButtonContent
const popupContentContainer = document.querySelector(contentContainerSelector); // Контейнер попапа
if (parentCard && popupContentContainer) {
// Копируем содержимое карточки
const clonedContent = parentCard.cloneNode(true);
// Очищаем контейнер попапа перед вставкой (опционально)
popupContentContainer.innerHTML = '';
// Вставляем копию карточки в попап
popupContentContainer.appendChild(clonedContent);
}
}
function calcScroll() {
let div = document.createElement('div');
div.style.width = '50px';
div.style.height = '50px';
div.style.overflowY = 'scroll';
div.style.visibility = 'hidden';
document.body.appendChild(div);
let scrollWidth = div.offsetWidth - div.clientWidth;
div.remove();
return scrollWidth;
}
closeBtn.addEventListener('click', () => {
closePopup();
});
overlay.addEventListener('click', () => {
closePopup();
});
// Если openBtnSelector указан, добавляем обработчики для открытия попапа
if (openBtnSelector) {
const openBtns = document.querySelectorAll(openBtnSelector);
openBtns.forEach(button => {
button.addEventListener('click', (e) => {
showPopup(e);
});
});
}
if(autoOpenDelay) {
const popupStrWithoutDot = removeLeadingDot(popupSelector);
let isPopupOpenedBefore = localStorage.getItem(`${popupStrWithoutDot}-submit`) === 'true';
setTimeout(() => {
if(!isPopupOpen && !isPopupOpenedBefore) showPopup();
}, autoOpenDelay);
}
function removeLeadingDot(str) {
if (str.startsWith('.')) {
return str.slice(1);
}
return str;
}
if (autoOpen) {
showPopup();
}
return showPopup;
}
if (document.querySelectorAll('.popup-join').length) {
const openPopup = popup({
popupSelector: '.popup-join',
overlaySelector: '.popup-join__overlay',
closeBtnSelector: '.popup-join__close',
openBtnSelector: '.show-form-popup-btn'
});
}
if (document.querySelectorAll('.popup-precis-advisory-board').length && document.querySelectorAll('.precis-advisory-board-item__read-more').length) {
const openAdvisoryPopup = popup({
popupSelector: '.popup-precis-advisory-board',
overlaySelector: '.popup-precis-advisory-board__overlay',
closeBtnSelector: '.popup-precis-advisory-board__close',
openBtnSelector: '.precis-advisory-board-item__read-more',
sourceButtonContent: '.precis-advisory-board-item',
contentContainerSelector: '.popup-precis-advisory-board__content',
});
}
function animNumFromTo() {
function number_to(elem)
{
const element = elem;
const from = parseFloat(element.dataset.numberFrom) ? parseFloat(element.dataset.numberFrom) : 0;
const to = parseFloat(element.dataset.numberTo) ? parseFloat(element.dataset.numberTo) : 0;
const duration = parseFloat(element.dataset.numberSpeed) ? parseFloat(element.dataset.numberSpeed) : 1000;
const start = new Date().getTime();
setTimeout(function() {
const now = (new Date().getTime()) - start;
const progress = now / duration;
let result = Math.floor((to - from) * progress + from);
if(to < 10) {
element.innerHTML = progress < 1 ? '0' + result : '0' + to;
} else {
element.innerHTML = progress < 1 ? result : to;
}
if (progress < 1) setTimeout(arguments.callee, 10);
}, 10);
}
const elem = [
document.querySelector('.why-precis-ai-better-time'),
];
let isLeaving = true;
const config = {
root: null, // avoiding 'root' or setting it to 'null' sets it to default value: viewport
rootMargin: '0px',
threshold: 0.5
};
let observer = new IntersectionObserver(function(elem) {
if (elem[0].isIntersecting) {
if(!isLeaving) return;
let digit = document.querySelectorAll('.why-precis-ai-better-time__item');
digit.forEach(function(elem){
number_to(elem);
})
isLeaving = false;
}
}, config);
if(document.querySelector('.why-precis-ai-better-time')) {
observer.observe(elem[0]);
}
}
animNumFromTo();
function subscribeForm(selector, stateBtnName) {
if(!document.querySelectorAll(`${selector}`).length) return;
const parentSelector = document.querySelector(`${selector}`);
const wpcf7Elm = parentSelector.querySelector( '.wpcf7');
const form = parentSelector.querySelector( '.wpcf7-form');
const formButton = parentSelector.querySelector('input[type=submit]');
const output = parentSelector.querySelector('.wpcf7-response-output');
formButton.addEventListener('click', function(e) {
wpcf7Elm.addEventListener( 'wpcf7mailsent', function( event ) {
console.log('2323');
formButton.value = stateBtnName.uploadBtnName;
setTimeout(() => {
formButton.value = stateBtnName.staticBtnName;
output.innerHTML = '';
form.classList.remove('sent');
form.classList.add('init');
}, 5000);
}, false );
})
}
function initSubscribeForm() {
const increaseEfficiencyBtnName = {
'staticBtnName': 'Subscribe',
'uploadBtnName': 'Subscribed',
};
const popupBtnName = {
'staticBtnName': 'Send',
'uploadBtnName': 'Sent successfully!',
};
// ======================= PDF DOWNLOAD (INDEX + MODAL) START =======================
;(function() {
function triggerProxyDownload(srcUrl) {
const iframeId = 'pdf-proxy-download-iframe';
let iframe = document.getElementById(iframeId);
if (!iframe) {
iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.id = iframeId;
document.body.appendChild(iframe);
}
const proxy = '/index.php?precis_download_pdf=1&pdf=' + encodeURIComponent(srcUrl);
iframe.src = proxy;
}
// INDEX FORM (on page) -> download + success + reset
(function() {
const form = document.querySelector('.sidebar-contact-form');
if (!form) return;
const container = document.querySelector('.archive-page-sidebar__item.with-form.mb-32');
if (!container) return;
// Слушаем успешную отправку формы от WPCF7
document.addEventListener('wpcf7mailsent', function(event) {
// Проверяем, что это НЕ модальная форма
const isModalForm = event.target.closest('.modal-pdf');
const pdfField = event.target.querySelector('input[name="pdf_url"]');
if (pdfField && pdfField.value && !isModalForm) {
console.log('INDEX form submitted successfully!');
triggerProxyDownload(pdfField.value);
container.classList.add('success');
setTimeout(() => {
container.classList.remove('success');
const nameField = event.target.querySelector('input[name="text-full-name"]');
const emailField = event.target.querySelector('input[name="work-email"]');
const checkbox = event.target.querySelector('input[type="checkbox"]');
if (nameField) nameField.value = '';
if (emailField) emailField.value = '';
if (checkbox) checkbox.checked = false;
}, 10000);
}
});
})();
// MODAL FORM -> download + success + reset + auto-hide after 10s
(function() {
const form = document.querySelector('.modal-pdf .sidebar-contact-form');
if (!form) return;
const modalContent = document.querySelector('.modal-pdf-content');
if (!modalContent) return;
// Слушаем успешную отправку формы от WPCF7
document.addEventListener('wpcf7mailsent', function(event) {
// Проверяем, что это модальная форма
const isModalForm = event.target.closest('.modal-pdf');
const pdfField = event.target.querySelector('input[name="pdf_url"]');
if (pdfField && pdfField.value && isModalForm) {
console.log('MODAL form submitted successfully!');
triggerProxyDownload(pdfField.value);
modalContent.classList.add('success');
// Автоматически скрываем success через 10 секунд и восстанавливаем форму
setTimeout(() => {
modalContent.classList.remove('success');
// Очищаем поля формы
const nameField = event.target.querySelector('input[name="text-full-name"]');
const emailField = event.target.querySelector('input[name="work-email"]');
const checkbox = event.target.querySelector('input[type="checkbox"]');
if (nameField) nameField.value = '';
if (emailField) emailField.value = '';
if (checkbox) checkbox.checked = false;
}, 10000);
}
});
})();
})();
// ======================== PDF DOWNLOAD (INDEX + MODAL) END ========================
subscribeForm('.increase-efficiency', increaseEfficiencyBtnName);
subscribeForm('.popup-form .drop-us-line-drop-us', popupBtnName);
}
initSubscribeForm();
function showCurrMenuItemOnHomePage() {
const body = document.querySelector('body');
if(!document.querySelectorAll('.menu-header-menu a').length
|| !getMobileOperatingSystem()
|| !body.classList.contains('body-front-page')) return;
const links = document.querySelectorAll('.menu-header-menu a');
const sections = document.querySelectorAll('.menu-item-observer');
function updateActiveLink(observerItem) {
links.forEach((link) => {
const linkTarget = removeHashAndSlash(link.getAttribute('href'));
if (linkTarget === observerItem) {
link.classList.add('--active');
} else {
link.classList.remove('--active');
}
});
}
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
updateActiveLink(entry.target.getAttribute('data-observer-menu-id'));
} else {
removeAllMenuItemActive();
}
});
}, { threshold: 0.4 });
sections.forEach(section => observer.observe(section));
function removeHashAndSlash(inputString) {
return inputString.replace(/[\/#]/g, '');
}
function removeAllMenuItemActive() {
links.forEach(function (link) {
link.classList.remove('--active');
});
}
}
showCurrMenuItemOnHomePage();
function showVideo() {
if (!document.querySelectorAll('.video-section-inner').length) return;
const videoButton = document.querySelector('.video-section-inner');
let video = null;
let videoPlaying = false; // Флаг состояния воспроизведения
// Проверяем, если это мобильное устройство
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// Если мобильное устройство, создаем видео сразу
// if (isMobile) {
// createVideo(true); // Передаем true для автозапуска
// }
videoButton.addEventListener('click', function(event) {
// Проверяем, был ли клик по .hero-video__content или его дочерним элементам
if (event.target.closest('.hero-video__content')) {
return; // Не запускаем видео при клике по контенту
}
if (!video) {
createVideo(false); // Создаем видео при первом клике
} else {
toggleVideoPlayback(); // Управляем воспроизведением после создания
}
});
function createVideo(autoPlay = false) {
const videoUrl = videoButton.getAttribute('data-file-url');
// Создаем видео-элемент
video = document.createElement('video');
video.classList.add('video-section-video');
video.setAttribute('src', videoUrl);
video.setAttribute('controls', 'controls');
video.setAttribute('muted', ''); // Отключение звука для автоматического воспроизведения
video.setAttribute('autoplay', ''); // Отключение звука для автоматического воспроизведения
video.setAttribute('playsinline', ''); // Для мобильных устройств
video.setAttribute('width', '100%'); // Устанавливаем ширину на 100%
video.setAttribute('height', 'auto'); // Устанавливаем высоту автоматически
// Заменяем изображение на видео
const videoImage = document.querySelector('.video-section-image');
videoButton.replaceChild(video, videoImage);
// Убираем кнопку воспроизведения
const playButton = document.querySelector('.video-section-content');
if (playButton) {
playButton.style.display = 'none';
}
// Запускаем воспроизведение сразу после создания, если это возможно
if (autoPlay) {
video.play().catch(function(error) {
console.error('Ошибка воспроизведения видео:', error);
});
videoPlaying = true; // Устанавливаем флаг на воспроизведение
}
}
function toggleVideoPlayback() {
videoPlaying = !videoPlaying; // Переключаем флаг состояния
if (videoPlaying) {
video.pause(); // Ставим видео на паузу
} else {
video.play().catch(function(error) {
console.error('Ошибка воспроизведения видео:', error);
}); // Запускаем воспроизведение
}
}
}
showVideo();
function initCalendlyOnFormSubmit() {
document.addEventListener('wpcf7mailsent', function(event) {
const tryFormId = 1779;
if (event.detail.contactFormId === tryFormId) {
const formInputs = event.detail.inputs;
let email = '';
formInputs.forEach(function(input) {
if (input.name === "try-form-email") {
email = input.value;
}
});
if (email) {
Calendly.initPopupWidget({
url: 'https://calendly.com/precisai/demo',
prefill: { email: email }
});
}
}
}, false);
}
initCalendlyOnFormSubmit();
function adjustElementPadding(selector) {
const element = document.querySelector(selector);
if (element.scrollHeight > element.clientHeight) {
element.style.paddingLeft = '14px';
element.style.left = '-14px';
} else {
element.style.paddingLeft = '0';
element.style.left = '0';
}
}
if(document.querySelector('.archive-glossary-term-toc')) {
adjustElementPadding('.archive-glossary-term-toc');
}
class GlossaryFilter {
constructor(inputSelector, postSelector, letterSelector) {
this.input = document.querySelector(inputSelector);
this.posts = document.querySelectorAll(postSelector);
this.letters = document.querySelectorAll(letterSelector);
if (!this.input) return;
this.init();
}
init() {
this.input.addEventListener('input', () => this.filterPosts());
}
filterPosts() {
const query = this.input.value.trim().toLowerCase();
// Сначала скрываем все посты
this.posts.forEach(post => {
const titleEl = post.querySelector('.archive-glossary-term__term-title');
const titleText = titleEl ? titleEl.textContent.toLowerCase() : '';
const matches = titleText.includes(query);
post.style.display = matches || query === '' ? '' : 'none';
});
// Затем обрабатываем буквы: показываем, только если под ними остались видимые посты
this.letters.forEach(letter => {
const nextSiblings = this.getFollowingPosts(letter);
const hasVisiblePost = nextSiblings.some(post => post.style.display !== 'none');
letter.style.display = hasVisiblePost ? '' : 'none';
});
}
// Метод: получает все посты после буквы до следующей буквы или до конца
getFollowingPosts(letterElement) {
const result = [];
let next = letterElement.nextElementSibling;
while (next && !next.classList.contains('glossary-letter')) {
if (next.classList.contains('archive-glossary-term__post')) {
result.push(next);
}
next = next.nextElementSibling;
}
return result;
}
}
if(document.querySelector('[data-js-glossary-search-input]') && document.querySelector('.archive-glossary-term__post')) {
new GlossaryFilter('[data-js-glossary-search-input]', '.archive-glossary-term__post', '.glossary-letter');
}
(() => {
const modal = document.querySelector('.modal-pdf');
const closeBtn = document.querySelector('.modal-pdf-close');
const overlay = document.querySelector('.modal-pdf-overlay');
const checkbox = document.querySelector('.checkbox-input');
if (!modal) return;
let isOpen = false;
// Обробник руху миші
const onMouseMove = (e) => {
const disabledInSession = sessionStorage.getItem('modalPdfDisabled') === 'true';
if (disabledInSession) return; // якщо відключено — не відкривати
if (!isOpen && e.clientY <= 10) {
modal.classList.add('show');
isOpen = true;
}
};
document.addEventListener('mousemove', onMouseMove);
// Функція закриття модалки
const closeModal = () => {
modal.classList.remove('show');
isOpen = false;
// Якщо чекбокс активний — забороняємо відкриття в поточній сесії
if (checkbox && checkbox.checked) {
sessionStorage.setItem('modalPdfDisabled', 'true');
}
// Восстанавливаем форму при закрытии модалки
const modalForm = document.querySelector('.modal-pdf .sidebar-contact-form');
const modalContent = document.querySelector('.modal-pdf-content');
if (modalForm && modalContent) {
// Убираем success состояние
modalContent.classList.remove('success');
// Очищаем поля формы
const nameField = modalForm.querySelector('input[name="text-full-name"]');
const emailField = modalForm.querySelector('input[name="work-email"]');
const checkboxField = modalForm.querySelector('input[type="checkbox"]');
if (nameField) nameField.value = '';
if (emailField) emailField.value = '';
if (checkboxField) checkboxField.checked = false;
}
};
if (closeBtn) closeBtn.addEventListener('click', closeModal);
if (overlay) overlay.addEventListener('click', closeModal);
})();
(() => {
const forQr = document.querySelector('.landing-hero .imgs .for-qr');
const btn = forQr?.querySelector('.btn');
if (!forQr || !btn) return;
// Клік на кнопку
btn.addEventListener('click', (e) => {
e.stopPropagation(); // щоб клік не полетів далі на document
forQr.classList.toggle('active');
});
// Клік поза межами .for-qr — знімаємо active
document.addEventListener('click', (e) => {
if (!forQr.contains(e.target)) {
forQr.classList.remove('active');
}
});
})();
(() => {
const forQr = document.querySelector('.landing-engine .imgs .for-qr');
const btn = forQr?.querySelector('.btn');
if (!forQr || !btn) return;
// Клік на кнопку
btn.addEventListener('click', (e) => {
e.stopPropagation(); // щоб клік не полетів далі на document
forQr.classList.toggle('active');
});
// Клік поза межами .for-qr — знімаємо active
document.addEventListener('click', (e) => {
if (!forQr.contains(e.target)) {
forQr.classList.remove('active');
}
});
})();
(() => {
const forQr = document.querySelector('.landing-perks .imgs .for-qr');
const btn = forQr?.querySelector('.btn');
if (!forQr || !btn) return;
// Клік на кнопку
btn.addEventListener('click', (e) => {
e.stopPropagation(); // щоб клік не полетів далі на document
forQr.classList.toggle('active');
});
// Клік поза межами .for-qr — знімаємо active
document.addEventListener('click', (e) => {
if (!forQr.contains(e.target)) {
forQr.classList.remove('active');
}
});
})();
AOS.init({
// Global settings:
disable: false, // accepts following values: 'phone', 'tablet', 'mobile', boolean, expression or function
startEvent: 'DOMContentLoaded', // name of the event dispatched on the document, that AOS should initialize on
initClassName: 'aos-init', // class applied after initialization
animatedClassName: 'aos-animate', // class applied on animation
useClassNames: false, // if true, will add content of `data-aos` as classes on scroll
disableMutationObserver: false, // disables automatic mutations' detections (advanced)
debounceDelay: 50, // the delay on debounce used while resizing window (advanced)
throttleDelay: 99, // the delay on throttle used while scrolling the page (advanced)
// Settings that can be overridden on per-element basis, by `data-aos-*` attributes:
offset: 20, // offset (in px) from the original trigger point
delay: 0, // values from 0 to 3000, with step 50ms
duration: 400, // values from 0 to 3000, with step 50ms
easing: 'ease', // default easing for AOS animations
once: true, // whether animation should happen only once - while scrolling down
mirror: false, // whether elements should animate out while scrolling past them
anchorPlacement: 'top-bottom', // defines which position of the element regarding to window should trigger the animation
});
(() => {
const headings = document.querySelectorAll('.animate-words');
headings.forEach(heading => {
// очищуємо попередні .word, якщо були
heading.querySelectorAll('span.word').forEach(span => {
const children = Array.from(span.childNodes);
span.replaceWith(...children);
});
// розбиваємо текст і <i> на слова
const nodes = Array.from(heading.childNodes);
const frag = document.createDocumentFragment();
nodes.forEach(node => {
if (node.nodeType === Node.TEXT_NODE) {
const parts = node.nodeValue.split(/(\s+)/);
parts.forEach(part => {
if (!part) return;
if (/^\s+$/.test(part)) {
frag.appendChild(document.createTextNode(part));
} else {
const span = document.createElement('span');
span.className = 'word';
span.textContent = part;
frag.appendChild(span);
}
});
} else if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'I') {
const innerParts = node.textContent.split(/(\s+)/);
node.textContent = '';
innerParts.forEach(part => {
if (!part) return;
if (/^\s+$/.test(part)) {
node.appendChild(document.createTextNode(part));
} else {
const span = document.createElement('span');
span.className = 'word';
span.textContent = part;
node.appendChild(span);
}
});
frag.appendChild(node);
} else {
frag.appendChild(node.cloneNode(true));
}
});
heading.innerHTML = '';
heading.appendChild(frag);
// спостерігаємо за появою у вʼюпорті
const wordSpans = heading.querySelectorAll('.word');
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
heading.classList.add('active'); // 👈 додаємо клас для opacity:1
// коли активний — анімуємо слова
wordSpans.forEach((el, i) => {
setTimeout(() => el.classList.add('visible'), i * 50);
});
obs.unobserve(entry.target); // лише один раз
}
});
}, { threshold: 0.3 });
observer.observe(heading);
});
})();
})