document.addEventListener('DOMContentLoaded', function() {
function waitForSplitType(callback) {
if (typeof SplitType !== 'undefined') {
callback();
} else {
setTimeout(() => waitForSplitType(callback), 100);
}
}
waitForSplitType(function() {
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.normalizeScroll({ normalizeScrollX: false });
gsap.config({
nullTargetWarn: false
});
let _scrollTweens = [];
let fadeUpWordSplit = new SplitType('[data-anim="fade-up-word"]:not([data-anim-container] [data-anim="fade-up-word"]), [data-anim="fade-up-word-p"]:not([data-anim-container] [data-anim="fade-up-word-p"])', {
types: 'lines, words'
});
let slideUpCharSplit = new SplitType('[data-anim="slide-up-char"]:not([data-anim-container] [data-anim="slide-up-char"]), [data-anim="slide-up-char-p"]:not([data-anim-container] [data-anim="slide-up-char-p"])', {
types: 'lines, words, chars'
});
let scrollFadeUpWordSplit = new SplitType('[data-anim="scroll-fade-up-word"], [data-anim="scroll-fade-up-word-p"]', {
types: 'lines, words'
});
let scrollSlideUpCharSplit = new SplitType('[data-anim="scroll-slide-up-char"], [data-anim="scroll-slide-up-char-p"]', {
types: 'lines, words, chars'
});
let fadeUpLineSplit = new SplitType('[data-anim="fade-up-line"]', {
types: 'lines'
});
let scrollFadeUpLineSplit = new SplitType('[data-anim="scroll-fade-up-line"]', {
types: 'lines'
});
document.querySelectorAll('[data-anim="fade-up-word"]').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
gsap.set(el.querySelectorAll('.word'), {
y: 50,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
const tl = gsap.timeline({
delay: customDelay
});
tl.to(el.querySelectorAll('.word'), {
y: 0,
autoAlpha: 1,
duration: 1,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.08,
force3D: true,
clearProps: 'willChange'
}, 0);
});
document.querySelectorAll('[data-anim="fade-up-word-p"]').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
const finalDelay = 0.3 + customDelay;
gsap.set(el.querySelectorAll('.word'), {
y: 30,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
const tl = gsap.timeline({
delay: finalDelay
});
tl.to(el.querySelectorAll('.word'), {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.04,
force3D: true,
clearProps: 'willChange'
}, 0);
});
document.querySelectorAll('[data-anim="slide-up-char"]').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
const finalDelay = 0.5 + customDelay;
gsap.set(el.querySelectorAll('.char'), {
y: '120%',
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
const tl = gsap.timeline({
delay: finalDelay
});
tl.to(el.querySelectorAll('.char'), {
y: '0%',
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.02,
force3D: true,
clearProps: 'willChange'
}, 0);
});
document.querySelectorAll('[data-anim="slide-up-char-p"]').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
const finalDelay = 0.8 + customDelay;
gsap.set(el.querySelectorAll('.char'), {
y: '100%',
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
const tl = gsap.timeline({
delay: finalDelay
});
tl.to(el.querySelectorAll('.char'), {
y: '0%',
autoAlpha: 1,
duration: 0.6,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.01,
force3D: true,
clearProps: 'willChange'
}, 0);
});
document.querySelectorAll('[data-anim="scroll-fade-up-word"]').forEach(el => {
const _t = gsap.from(el.querySelectorAll('.word'), {
y: 50,
autoAlpha: 0,
duration: 1,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.08,
force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: {
trigger: el,
start: 'top 80%',
end: 'top 20%',
markers: false,
},
clearProps: 'all'
});
_scrollTweens.push(_t);
});
document.querySelectorAll('[data-anim="scroll-fade-up-word-p"]').forEach(el => {
const _t = gsap.from(el.querySelectorAll('.word'), {
y: 30,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.04,
delay: 0.3,
force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: {
trigger: el,
start: 'top 80%',
end: 'top 20%',
markers: false,
},
clearProps: 'all'
});
_scrollTweens.push(_t);
});
document.querySelectorAll('[data-anim="scroll-slide-up-char"]').forEach(el => {
const _t = gsap.from(el.querySelectorAll('.char'), {
y: '120%',
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.02,
delay: 0.5,
force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: {
trigger: el,
start: 'top 80%',
end: 'top 20%',
markers: false,
},
clearProps: 'all'
});
_scrollTweens.push(_t);
});
document.querySelectorAll('[data-anim="scroll-slide-up-char-p"]').forEach(el => {
const _t = gsap.from(el.querySelectorAll('.char'), {
y: '100%',
autoAlpha: 0,
duration: 0.6,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.01,
delay: 0.8,
force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: {
trigger: el,
start: 'top 80%',
end: 'top 20%',
markers: false,
},
clearProps: 'all'
});
_scrollTweens.push(_t);
});
document.querySelectorAll('[data-anim="fade-up-line"]').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
gsap.set(el.querySelectorAll('.line'), {
y: 50,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
const tl = gsap.timeline({
delay: customDelay
});
tl.to(el.querySelectorAll('.line'), {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.12,
force3D: true,
clearProps: 'willChange'
}, 0);
});
document.querySelectorAll('[data-anim="scroll-fade-up-line"]').forEach(el => {
const _t = gsap.from(el.querySelectorAll('.line'), {
y: 50,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.12,
force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: {
trigger: el,
start: 'top 80%',
end: 'top 20%',
markers: false,
},
clearProps: 'all'
});
_scrollTweens.push(_t);
});
document.querySelectorAll('[data-anim="slide-up"]:not([data-anim-container] [data-anim="slide-up"])').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
gsap.from(el, {
y: 60,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
delay: customDelay,
force3D: true,
scrollTrigger: {
trigger: el,
start: 'top 80%',
markers: false,
},
clearProps: 'all'
});
});
document.querySelectorAll('[data-anim="fade-up"]:not([data-anim-container] [data-anim="fade-up"])').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
gsap.from(el, {
y: 30,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
delay: customDelay,
force3D: true,
scrollTrigger: {
trigger: el,
start: 'top 80%',
markers: false,
},
clearProps: 'all'
});
});
document.querySelectorAll('[data-anim="scale-up"]:not([data-anim-container] [data-anim="scale-up"])').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
gsap.from(el, {
scale: 0.8,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
delay: customDelay,
force3D: true,
scrollTrigger: {
trigger: el,
start: 'top 80%',
markers: false,
},
clearProps: 'all'
});
});
document.querySelectorAll('[data-anim="slide-left"]:not([data-anim-container] [data-anim="slide-left"])').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
gsap.from(el, {
x: -60,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
delay: customDelay,
force3D: true,
scrollTrigger: {
trigger: el,
start: 'top 80%',
markers: false,
},
clearProps: 'all'
});
});
document.querySelectorAll('[data-anim="slide-right"]:not([data-anim-container] [data-anim="slide-right"])').forEach(el => {
const customDelay = el.getAttribute('data-anim-delay') ? parseFloat(el.getAttribute('data-anim-delay')) / 1000 : 0;
gsap.from(el, {
x: 60,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
delay: customDelay,
force3D: true,
scrollTrigger: {
trigger: el,
start: 'top 80%',
markers: false,
},
clearProps: 'all'
});
});
const loadContainers = document.querySelectorAll('[data-anim-container="load"]');
loadContainers.forEach(container => {
const animatedElements = container.querySelectorAll('[data-anim]');
if (animatedElements.length === 0) return;
animatedElements.forEach(el => {
const animType = el.getAttribute('data-anim');
if (animType.includes('fade-up-word')) {
new SplitType(el, {
types: 'lines, words'
});
} else if (animType.includes('slide-up-char')) {
new SplitType(el, {
types: 'lines, words, chars'
});
} else if (animType.includes('fade-up-line')) {
new SplitType(el, {
types: 'lines'
});
}
});
const tl = gsap.timeline();
animatedElements.forEach((el, index) => {
const animType = el.getAttribute('data-anim');
const customDelay = el.getAttribute('data-anim-delay');
const elementDelay = index * 0.4 + (customDelay ? parseFloat(customDelay) / 1000 : 0);
if (animType === 'fade-up-word') {
gsap.set(el.querySelectorAll('.word'), {
y: 50,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el.querySelectorAll('.word'), {
y: 0,
autoAlpha: 1,
duration: 1,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.08,
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'fade-up-word-p') {
gsap.set(el.querySelectorAll('.word'), {
y: 30,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el.querySelectorAll('.word'), {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.04,
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'slide-up-char') {
gsap.set(el.querySelectorAll('.char'), {
y: '120%',
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el.querySelectorAll('.char'), {
y: '0%',
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.02,
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'slide-up-char-p') {
gsap.set(el.querySelectorAll('.char'), {
y: '100%',
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el.querySelectorAll('.char'), {
y: '0%',
autoAlpha: 1,
duration: 0.6,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.01,
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'fade-up-line') {
gsap.set(el.querySelectorAll('.line'), {
y: 50,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el.querySelectorAll('.line'), {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.12,
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'fade-up') {
gsap.set(el, {
y: 30,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el, {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'slide-up') {
gsap.set(el, {
y: 60,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el, {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'scale-up') {
gsap.set(el, {
scale: 0.8,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el, {
scale: 1,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'slide-left') {
gsap.set(el, {
x: -60,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el, {
x: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'willChange'
}, elementDelay);
} else if (animType === 'slide-right') {
gsap.set(el, {
x: 60,
autoAlpha: 0,
force3D: true,
willChange: 'transform, opacity'
});
tl.to(el, {
x: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'willChange'
}, elementDelay);
}
});
});
// ---- RESIZE HANDLER ----
// Must be inside waitForSplitType so split vars + _scrollTweens are in scope
let _splitResizeTimer;
let _lastSplitWidth = window.innerWidth;
function _handleSplitResize() {
const newWidth = window.innerWidth;
if (newWidth === _lastSplitWidth) return;
_lastSplitWidth = newWidth;
// Kill all tracked scroll text tweens + their ScrollTriggers
_scrollTweens.forEach(t => {
if (t && t.scrollTrigger) t.scrollTrigger.kill();
if (t) t.kill();
});
_scrollTweens = [];
// Revert + re-split LOAD animations (already done  naturally visible after re-split)
fadeUpWordSplit.revert();
fadeUpWordSplit = new SplitType('[data-anim="fade-up-word"]:not([data-anim-container] [data-anim="fade-up-word"]), [data-anim="fade-up-word-p"]:not([data-anim-container] [data-anim="fade-up-word-p"])', { types: 'lines, words' });
slideUpCharSplit.revert();
slideUpCharSplit = new SplitType('[data-anim="slide-up-char"]:not([data-anim-container] [data-anim="slide-up-char"]), [data-anim="slide-up-char-p"]:not([data-anim-container] [data-anim="slide-up-char-p"])', { types: 'lines, words, chars' });
fadeUpLineSplit.revert();
fadeUpLineSplit = new SplitType('[data-anim="fade-up-line"]', { types: 'lines' });
// Revert + re-split SCROLL animations, recreate tweens only for not-yet-triggered elements
scrollFadeUpWordSplit.revert();
scrollFadeUpWordSplit = new SplitType('[data-anim="scroll-fade-up-word"], [data-anim="scroll-fade-up-word-p"]', { types: 'lines, words' });
document.querySelectorAll('[data-anim="scroll-fade-up-word"]').forEach(el => {
if (el.hasAttribute('data-anim-done')) return;
const _t = gsap.from(el.querySelectorAll('.word'), {
y: 50, autoAlpha: 0, duration: 1, ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.08, force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: { trigger: el, start: 'top 80%', end: 'top 20%', markers: false },
clearProps: 'all'
});
_scrollTweens.push(_t);
});
document.querySelectorAll('[data-anim="scroll-fade-up-word-p"]').forEach(el => {
if (el.hasAttribute('data-anim-done')) return;
const _t = gsap.from(el.querySelectorAll('.word'), {
y: 30, autoAlpha: 0, duration: 0.8, ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.04, delay: 0.3, force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: { trigger: el, start: 'top 80%', end: 'top 20%', markers: false },
clearProps: 'all'
});
_scrollTweens.push(_t);
});
scrollSlideUpCharSplit.revert();
scrollSlideUpCharSplit = new SplitType('[data-anim="scroll-slide-up-char"], [data-anim="scroll-slide-up-char-p"]', { types: 'lines, words, chars' });
document.querySelectorAll('[data-anim="scroll-slide-up-char"]').forEach(el => {
if (el.hasAttribute('data-anim-done')) return;
const _t = gsap.from(el.querySelectorAll('.char'), {
y: '120%', autoAlpha: 0, duration: 0.8, ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.02, delay: 0.5, force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: { trigger: el, start: 'top 80%', end: 'top 20%', markers: false },
clearProps: 'all'
});
_scrollTweens.push(_t);
});
document.querySelectorAll('[data-anim="scroll-slide-up-char-p"]').forEach(el => {
if (el.hasAttribute('data-anim-done')) return;
const _t = gsap.from(el.querySelectorAll('.char'), {
y: '100%', autoAlpha: 0, duration: 0.6, ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.01, delay: 0.8, force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: { trigger: el, start: 'top 80%', end: 'top 20%', markers: false },
clearProps: 'all'
});
_scrollTweens.push(_t);
});
scrollFadeUpLineSplit.revert();
scrollFadeUpLineSplit = new SplitType('[data-anim="scroll-fade-up-line"]', { types: 'lines' });
document.querySelectorAll('[data-anim="scroll-fade-up-line"]').forEach(el => {
if (el.hasAttribute('data-anim-done')) return;
const _t = gsap.from(el.querySelectorAll('.line'), {
y: 50, autoAlpha: 0, duration: 0.8, ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.12, force3D: true,
onComplete: () => el.setAttribute('data-anim-done', 'true'),
scrollTrigger: { trigger: el, start: 'top 80%', end: 'top 20%', markers: false },
clearProps: 'all'
});
_scrollTweens.push(_t);
});
ScrollTrigger.refresh();
}
window.addEventListener('resize', () => {
clearTimeout(_splitResizeTimer);
_splitResizeTimer = setTimeout(_handleSplitResize, 150);
});
});
window.replayFadeUpWord = function() {
gsap.fromTo('[data-anim="fade-up-word"] .word', {
y: 50,
autoAlpha: 0
}, {
y: 0,
autoAlpha: 1,
duration: 1,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.08,
force3D: true
});
gsap.fromTo('[data-anim="fade-up-word-p"] .word', {
y: 30,
autoAlpha: 0
}, {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.04,
delay: 0.3,
force3D: true
});
};
window.replaySlideUpChar = function() {
gsap.fromTo('[data-anim="slide-up-char"] .char', {
y: '120%',
autoAlpha: 0
}, {
y: '0%',
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.02,
force3D: true
});
gsap.fromTo('[data-anim="slide-up-char-p"] .char', {
y: '100%',
autoAlpha: 0
}, {
y: '0%',
autoAlpha: 1,
duration: 0.6,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.01,
delay: 0.3,
force3D: true
});
};
window.replayScrollFadeUpWord = function() {
gsap.fromTo('[data-anim="scroll-fade-up-word"] .word', {
y: 50,
autoAlpha: 0
}, {
y: 0,
autoAlpha: 1,
duration: 1,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.08,
force3D: true
});
gsap.fromTo('[data-anim="scroll-fade-up-word-p"] .word', {
y: 30,
autoAlpha: 0
}, {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.04,
delay: 0.3,
force3D: true
});
};
window.replayScrollSlideUpChar = function() {
gsap.fromTo('[data-anim="scroll-slide-up-char"] .char', {
y: '120%',
autoAlpha: 0
}, {
y: '0%',
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.02,
force3D: true
});
gsap.fromTo('[data-anim="scroll-slide-up-char-p"] .char', {
y: '100%',
autoAlpha: 0
}, {
y: '0%',
autoAlpha: 1,
duration: 0.6,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.01,
delay: 0.3,
force3D: true
});
};
window.replayFadeUpLine = function() {
gsap.fromTo('[data-anim="fade-up-line"] .line', {
y: 50,
autoAlpha: 0
}, {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.12,
force3D: true
});
};
window.replayScrollFadeUpLine = function() {
gsap.fromTo('[data-anim="scroll-fade-up-line"] .line', {
y: 50,
autoAlpha: 0
}, {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.12,
force3D: true
});
};
const containers = document.querySelectorAll('[data-anim-container="scroll"]');
containers.forEach(container => {
gsap.set(container, {
autoAlpha: 0
});
});
const scrollContainerObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const container = entry.target;
const animatedElements = container.querySelectorAll('[data-anim]');
if (animatedElements.length === 0) return;
animatedElements.forEach(el => {
const animType = el.getAttribute('data-anim');
if (animType.includes('fade-up-word')) {
new SplitType(el, {
types: 'lines, words'
});
} else if (animType.includes('slide-up-char')) {
new SplitType(el, {
types: 'lines, words, chars'
});
} else if (animType.includes('fade-up-line')) {
new SplitType(el, {
types: 'lines'
});
}
});
const tl = gsap.timeline({
onStart: () => {
gsap.set(container, {
autoAlpha: 1
});
}
});
animatedElements.forEach((el, index) => {
const animType = el.getAttribute('data-anim');
const customDelay = el.getAttribute('data-anim-delay');
const elementDelay = index * 0.2 + (customDelay ? parseFloat(customDelay) / 1000 : 0);
if (animType.includes('fade-up-word')) {
tl.from(el.querySelectorAll('.word'), {
y: 50,
autoAlpha: 0,
duration: 1,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.08,
force3D: true,
clearProps: 'all'
}, elementDelay);
} else if (animType.includes('slide-up-char')) {
tl.from(el.querySelectorAll('.char'), {
y: '120%',
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
stagger: 0.02,
force3D: true,
clearProps: 'all'
}, elementDelay);
} else if (animType.includes('fade-up-line')) {
tl.from(el.querySelectorAll('.line'), {
y: 50,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
stagger: 0.12,
force3D: true,
clearProps: 'all'
}, elementDelay);
} else if (animType === 'slide-up') {
tl.from(el, {
y: 60,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'all'
}, elementDelay);
} else if (animType === 'fade-up') {
tl.from(el, {
y: 30,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'all'
}, elementDelay);
} else if (animType === 'scale-up') {
tl.from(el, {
scale: 0.8,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.34, 1, .64, 1)',
force3D: true,
clearProps: 'all'
}, elementDelay);
} else if (animType === 'slide-left') {
tl.from(el, {
x: -60,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'all'
}, elementDelay);
} else if (animType === 'slide-right') {
tl.from(el, {
x: 60,
autoAlpha: 0,
duration: 0.8,
ease: 'cubic-bezier(.27, 1, .48, 1)',
force3D: true,
clearProps: 'all'
}, elementDelay);
}
});
scrollContainerObserver.unobserve(container);
}
});
}, {
threshold: 0.2,
rootMargin: '0px 0px -20% 0px'
});
containers.forEach(container => {
scrollContainerObserver.observe(container);
});
function initScrollDirectionMarquee() {
const marqueeElements = document.querySelectorAll('[data-marquee-scroll]');
marqueeElements.forEach((element) => {
const baseSpeed = parseFloat(element.getAttribute('data-speed')) || 1;
let currentDirection = 1;
const originalContent = element.innerHTML;
element.innerHTML = originalContent + originalContent + originalContent + originalContent;
gsap.delayedCall(0.1, () => {
const contentWidth = element.scrollWidth / 4;
let xPos = 0;
const setX = gsap.quickSetter(element, 'x', 'px');
const wrapX = gsap.utils.wrap(-contentWidth, 0);
const tick = () => {
xPos -= baseSpeed * currentDirection;
xPos = wrapX(xPos);
setX(xPos);
};
gsap.ticker.add(tick);
let lastScrollY = window.pageYOffset;
window.addEventListener('scroll', () => {
const currentScrollY = window.pageYOffset;
const scrollDirection = currentScrollY > lastScrollY ? 1 : -1;
if (scrollDirection !== currentDirection) {
currentDirection = scrollDirection;
}
lastScrollY = currentScrollY;
});
});
});
}
initScrollDirectionMarquee();
function initTiltScaleParallax() {
document.querySelectorAll('[data-tilt-scale]').forEach((element) => {
const container = element.closest('[data-tilt-scale-container]') || element.parentElement;
const tiltDirection = element.getAttribute('data-tilt-direction') || 'rotateY';
const tiltIntensity = parseFloat(element.getAttribute('data-tilt-intensity')) || 18;
const scaleAmount = parseFloat(element.getAttribute('data-scale-amount')) || 1.2;
const isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
const scrubValue = parseFloat(element.getAttribute('data-scrub')) || (isTouchDevice ? 0.5 : 1.2);
const animProps = {
scale: scaleAmount,
ease: 'none'
};
if (tiltDirection === 'rotateX') {
animProps.rotateX = tiltIntensity;
} else if (tiltDirection === 'rotateY') {
animProps.rotateY = tiltIntensity;
} else if (tiltDirection === 'both') {
animProps.rotateX = tiltIntensity * 0.6;
animProps.rotateY = tiltIntensity * 0.8;
}
gsap.fromTo(element, {
rotateX: tiltDirection === 'rotateX' || tiltDirection === 'both' ? -tiltIntensity * 0.5 : 0,
rotateY: tiltDirection === 'rotateY' || tiltDirection === 'both' ? -tiltIntensity * 0.3 : 0,
scale: 1,
force3D: true
}, {
...animProps,
scrollTrigger: {
trigger: container,
start: 'top bottom',
end: 'bottom top',
scrub: scrubValue,
invalidateOnRefresh: true,
}
});
});
}
initTiltScaleParallax();
function initVideoScaleScroll() {
document.querySelectorAll('[data-scale-scroll]').forEach((element) => {
const startScale = parseFloat(element.getAttribute('data-start-scale')) || 0.7;
const endScale = parseFloat(element.getAttribute('data-end-scale')) || 1;
const startOpacity = parseFloat(element.getAttribute('data-start-opacity')) || 0.6;
const endOpacity = parseFloat(element.getAttribute('data-end-opacity')) || 1;
const scrubValue = parseFloat(element.getAttribute('data-scrub')) || 1.5;
gsap.fromTo(element, {
scale: startScale,
opacity: startOpacity,
force3D: true
}, {
scale: endScale,
opacity: endOpacity,
ease: 'power2.out',
scrollTrigger: {
trigger: element,
start: 'top bottom-=100',
end: 'center center',
scrub: scrubValue,
invalidateOnRefresh: true,
}
});
});
}
initVideoScaleScroll();
});
(function () {
var SWIPE_DISTANCE = 50;
var SWIPE_VELOCITY = 0.3;
var AXIS_LOCK_RATIO = 1.2;
var hasPointerEvents = typeof window !== 'undefined' && typeof window.PointerEvent !== 'undefined';
var initialized = false;
function initSwipeStack() {
if (initialized) return;
var stack = document.getElementById('reviewSwipeStackMobile');
if (!stack) return;
var cards = Array.from(stack.querySelectorAll('.review-swipe-card-item'));
if (!cards.length) return;
initialized = true;
var currentCard = 0;
var isAnimating = false;
var startX = 0;
var startY = 0;
var startTime = 0;
var activeTouchId = null;
var mouseDown = false;
var totalCards = cards.length;
var queuedStepDelta = 0;
var navWrap = null;
var prevBtn = null;
var nextBtn = null;
if (stack.nextElementSibling && stack.nextElementSibling.classList.contains('review-swipe-nav')) {
navWrap = stack.nextElementSibling;
} else {
navWrap = document.createElement('div');
navWrap.className = 'review-swipe-nav';
navWrap.innerHTML =
'' +
'';
stack.insertAdjacentElement('afterend', navWrap);
}
prevBtn = navWrap.querySelector('.review-swipe-prev-btn');
nextBtn = navWrap.querySelector('.review-swipe-next-btn');
function updateNavState() {
if (!prevBtn || !nextBtn) return;
prevBtn.disabled = currentCard <= 0;
nextBtn.disabled = currentCard >= totalCards - 1;
}
function navigateWithGuard(delta) {
step(delta);
}
function bindNavButton(button, delta) {
if (!button) return;
function onActivate(event) {
if (event && event.type === 'keydown') {
var key = event.key || event.code;
if (key !== 'Enter' && key !== ' ' && key !== 'Spacebar') return;
}
if (event) {
event.preventDefault();
event.stopPropagation();
}
navigateWithGuard(delta);
}
button.addEventListener('keydown', onActivate);
if (hasPointerEvents) {
button.addEventListener('pointerup', function (event) {
if (event.pointerType === 'mouse' && event.button !== 0) return;
onActivate(event);
});
button.addEventListener('click', function (event) {
// Keep keyboard-triggered click support on pointer-enabled browsers.
if (event.detail !== 0) return;
onActivate(event);
});
return;
}
button.addEventListener('click', onActivate);
}
/* ââ€Âۉâ€Â€ Dynamic sizing ââ€Âۉâ€Â€ */
var STACK_PAD = 48;
function recalcSizes() {
/* Temporarily clear fixed heights so cards can shrink as well as grow */
cards.forEach(function (card) { card.style.height = ''; });
var cardHeight = cards.reduce(function (max, card) {
return Math.max(max, card.scrollHeight);
}, 0);
if (cardHeight < 10) cardHeight = 220; /* fallback if not yet rendered */
cards.forEach(function (card) {
card.style.height = cardHeight + 'px';
});
stack.style.height = (cardHeight + STACK_PAD) + 'px';
}
recalcSizes();
/* Re-measure on resize (debounced 120ms) */
var resizeTimer;
window.addEventListener('resize', function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(recalcSizes, 120);
});
stack.style.touchAction = 'pan-y';
function applyCard(card, y, rotate, opacity, animated, duration) {
/* Clear CSS individual transform props that can conflict with transform shorthand */
card.style.translate = '';
card.style.rotate = '';
card.style.scale = '';
var dur = duration || '0.75s';
card.style.transition = animated
? 'transform ' + dur + ' cubic-bezier(0.16,1,0.3,1), opacity 0.6s ease'
: 'none';
card.style.transform = 'translate3d(0,' + y + '%,0) rotateZ(' + rotate + 'deg)';
card.style.opacity = String(opacity);
}
function paintCards(animated) {
cards.forEach(function (card, index) {
var position = index - currentCard;
var abs = Math.abs(position);
var y = -50, rotate = 0, opacity = 1, z;
if (position < 0) {
y = -50 - (220 + abs * 26);
rotate = abs * 15;
opacity = 0.82;
z = totalCards + 20 + abs;
} else if (position === 0) {
y = -50;
rotate = 0;
opacity = 1;
z = totalCards + 10;
} else {
y = -50;
rotate = position * 2;
opacity = Math.max(0.8, 1 - position * 0.06);
z = totalCards - position;
}
card.style.pointerEvents = position === 0 ? 'auto' : 'none';
card.style.zIndex = String(z);
/* Exiting cards travel a large distance  use a longer duration so they
feel as slow as the incoming card settling into place */
var dur = (animated && position < 0) ? '1.4s' : '0.75s';
applyCard(card, y, rotate, opacity, animated, dur);
});
updateNavState();
}
function step(delta) {
var direction = delta < 0 ? -1 : 1;
if (isAnimating) {
queuedStepDelta = direction;
return;
}
var next = Math.max(0, Math.min(currentCard + direction, totalCards - 1));
if (next === currentCard) {
queuedStepDelta = 0;
return;
}
currentCard = next;
isAnimating = true;
paintCards(true);
setTimeout(function () {
isAnimating = false;
if (queuedStepDelta !== 0) {
var pendingStep = queuedStepDelta;
queuedStepDelta = 0;
step(pendingStep);
}
}, 1400); /* match longest card duration */
}
function startGesture(x, y) {
startX = x; startY = y; startTime = performance.now();
}
function endGesture(x, y) {
var dx = x - startX, dy = y - startY;
var ax = Math.abs(dx), ay = Math.abs(dy);
if (ax < 10) return;
if (ay > ax * AXIS_LOCK_RATIO) return;
var elapsed = Math.max(1, performance.now() - startTime);
var velocity = ax / elapsed;
if (ax < SWIPE_DISTANCE && velocity < SWIPE_VELOCITY) return;
step(dx < 0 ? 1 : -1);
}
/* ââ€Âۉâ€Â€ Touch ââ€Âۉâ€Â€ */
stack.addEventListener('touchstart', function (e) {
if (activeTouchId !== null) return;
var t = e.changedTouches[0];
activeTouchId = t.identifier;
startGesture(t.clientX, t.clientY);
}, { passive: true });
stack.addEventListener('touchend', function (e) {
if (activeTouchId === null) return;
for (var i = 0; i < e.changedTouches.length; i++) {
var t = e.changedTouches[i];
if (t.identifier !== activeTouchId) continue;
endGesture(t.clientX, t.clientY);
activeTouchId = null;
break;
}
}, { passive: true });
stack.addEventListener('touchcancel', function () {
activeTouchId = null;
}, { passive: true });
/* ââ€Âۉâ€Â€ Mouse ââ€Âۉâ€Â€ */
stack.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
mouseDown = true;
startGesture(e.clientX, e.clientY);
});
stack.addEventListener('mouseup', function (e) {
if (!mouseDown) return;
mouseDown = false;
endGesture(e.clientX, e.clientY);
});
stack.addEventListener('mouseleave', function () { mouseDown = false; });
bindNavButton(prevBtn, -1);
bindNavButton(nextBtn, 1);
paintCards(false);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSwipeStack);
} else {
initSwipeStack();
}
window.initReviewSwipeStackMobile = function () {
initSwipeStack();
};
if (!document.getElementById('reviewSwipeStackMobile')) {
var reviewSwipeInitObserver = new MutationObserver(function () {
if (initialized) {
reviewSwipeInitObserver.disconnect();
return;
}
if (document.getElementById('reviewSwipeStackMobile')) {
initSwipeStack();
if (initialized) reviewSwipeInitObserver.disconnect();
}
});
reviewSwipeInitObserver.observe(document.documentElement, {
childList: true,
subtree: true
});
setTimeout(function () {
reviewSwipeInitObserver.disconnect();
}, 15000);
}
})();