File: /var/www/viitorx.stgviitor.com/wp-content/themes/viitorx/js/section-reveal.js
/**
* Scroll-triggered reveal for [data-section-reveal].
* Prefer ScrollTrigger when present — it stays aligned with Lenis + scrollerProxy on marketing pages.
*/
(function () {
'use strict';
var nodes = document.querySelectorAll('[data-section-reveal]');
if (!nodes.length) {
return;
}
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); }
function runCounters(container) {
container.querySelectorAll('.js-count-stat').forEach(function (item) {
var end = parseFloat(item.getAttribute('data-count-to'));
var suf = item.getAttribute('data-count-suffix') || '';
var el = item.querySelector('.stat-count-display');
if (!el || !Number.isFinite(end)) return;
if (reduced) { el.textContent = Math.round(end) + suf; return; }
var duration = 2200;
var start = performance.now();
(function frame(now) {
var u = Math.min(1, (now - start) / duration);
el.textContent = Math.round(easeOutCubic(u) * end) + suf;
if (u < 1) requestAnimationFrame(frame);
})(start);
});
}
function reveal(el) {
el.classList.add('section-reveal--visible');
runCounters(el);
}
/** Above-the-fold triggers must not stay opacity:0 waiting for ScrollTrigger / IO. */
function isOverlappingViewport(el) {
var rect = el.getBoundingClientRect();
var vh = window.innerHeight || document.documentElement.clientHeight;
var vw = window.innerWidth || document.documentElement.clientWidth;
var margin = 160;
return rect.bottom > -margin && rect.top < vh + margin && rect.right > -margin && rect.left < vw + margin;
}
function revealIfAlreadyInView(list) {
list.forEach(function (el) {
if (!el.classList.contains('section-reveal--visible') && isOverlappingViewport(el)) {
reveal(el);
}
});
}
revealIfAlreadyInView(nodes);
function initIntersectionObserver() {
if (!('IntersectionObserver' in window)) {
nodes.forEach(reveal);
return;
}
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) {
return;
}
reveal(entry.target);
io.unobserve(entry.target);
});
},
{
root: null,
rootMargin: '0px 0px 18% 0px',
threshold: 0,
}
);
nodes.forEach(function (el) {
io.observe(el);
});
}
if (typeof ScrollTrigger !== 'undefined') {
nodes.forEach(function (el) {
ScrollTrigger.create({
trigger: el,
start: 'top 92%',
once: true,
onEnter: function () {
reveal(el);
},
});
});
requestAnimationFrame(function () {
if (typeof ScrollTrigger !== 'undefined' && ScrollTrigger.refresh) {
ScrollTrigger.refresh();
}
revealIfAlreadyInView(nodes);
});
return;
}
initIntersectionObserver();
})();