File: /var/www/viitorx.stgviitor.com/wp-content/themes/viitorx/js/offerings-animate.js
/**
* Offerings page — GSAP scroll-driven animations.
* Replaces the flat CSS section-reveal for offering/visual/contact sections.
* Runs after front-page-animations.js (GSAP + Lenis bridge already initialised).
*
* Pattern: gsap.set() initial states on load → ScrollTrigger.create({ onEnter })
* → gsap.to() inside onEnter. No lenis.resize() during scroll, no extra
* ScrollTrigger.refresh() — front-page-animations.js handles both.
*/
(function () {
'use strict';
if (!document.body.classList.contains('offerings-page')) return;
if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') return;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
gsap.registerPlugin(ScrollTrigger);
/* ── HELPERS ──────────────────────────────────────────────── */
/** Collect all matrix slot elements sorted in reading order (row → column). */
function getMatrixCellsOrdered(matrix) {
if (!matrix) return [];
var cells = [];
matrix.querySelectorAll('[class*="offering-matrix__slot--"]').forEach(function (el) {
var cls = el.className;
var row = 0, col = 0;
var hMatch = cls.match(/slot--head-c(\d)/);
var rMatch = cls.match(/slot--r(\d+)-c(\d)/);
if (hMatch) { row = 0; col = parseInt(hMatch[1], 10); }
else if (rMatch) { row = parseInt(rMatch[1], 10); col = parseInt(rMatch[2], 10); }
cells.push({ el: el, row: row, col: col });
});
cells.sort(function (a, b) { return a.row !== b.row ? a.row - b.row : a.col - b.col; });
return cells.map(function (c) { return c.el; });
}
/* ── OFFERING CONTENT SECTIONS ─────────────────────────────── */
document.querySelectorAll('.offering[data-section-reveal]').forEach(function (section) {
var headerEls = gsap.utils.toArray(section.querySelectorAll('.offering__header > *'));
var allCells = getMatrixCellsOrdered(section.querySelector('.offering-matrix'));
/* Set invisible initial states synchronously so elements never flash. */
if (headerEls.length) gsap.set(headerEls, { opacity: 0, y: 44 });
if (allCells.length) gsap.set(allCells, { opacity: 0, y: 16 });
ScrollTrigger.create({
trigger: section,
start: 'top 82%',
once: true,
onEnter: function () {
var tl = gsap.timeline();
var cellOffset = headerEls.length ? 0.28 : 0;
if (headerEls.length) {
tl.to(headerEls, {
opacity: 1,
y: 0,
duration: 0.92,
stagger: 0.13,
ease: 'power3.out',
}, 0);
}
/* Single unified wave: all cells in reading order (row → col). */
if (allCells.length) {
tl.to(allCells, {
opacity: 1,
y: 0,
duration: 0.62,
stagger: 0.038,
ease: 'power3.out',
}, cellOffset);
}
},
});
});
/* ── FULL-WIDTH VISUAL IMAGES ───────────────────────────────── */
document.querySelectorAll('.visual[data-section-reveal]').forEach(function (section) {
var img = section.querySelector('.visual__img');
if (!img) return;
gsap.set(img, { opacity: 0, scale: 1.045 });
ScrollTrigger.create({
trigger: section,
start: 'top 88%',
once: true,
onEnter: function () {
gsap.to(img, {
opacity: 1,
scale: 1,
duration: 1.4,
ease: 'power2.out',
});
},
});
});
/* ── CONTACT SECTION: left slides from left, right from right ─ */
var contactSection = document.querySelector('.contact[data-section-reveal]');
if (contactSection) {
var leftItems = gsap.utils.toArray(contactSection.querySelectorAll('.contact__left > *'));
var rightItems = gsap.utils.toArray(contactSection.querySelectorAll('.contact__right > *'));
if (leftItems.length) gsap.set(leftItems, { opacity: 0, x: -44 });
if (rightItems.length) gsap.set(rightItems, { opacity: 0, x: 44 });
ScrollTrigger.create({
trigger: contactSection,
start: 'top 82%',
once: true,
onEnter: function () {
var tl = gsap.timeline();
if (leftItems.length) {
tl.to(leftItems, {
opacity: 1,
x: 0,
duration: 0.9,
stagger: 0.11,
ease: 'power3.out',
}, 0);
}
if (rightItems.length) {
tl.to(rightItems, {
opacity: 1,
x: 0,
duration: 0.9,
stagger: 0.11,
ease: 'power3.out',
}, 0.2);
}
},
});
}
})();