feat: scroll-triggered section animations via IntersectionObserver + GSAP

Adds a staggered fade-in/rise animation (y:30→0, opacity:0→1, stagger 0.08s,
power2.out, 0.7s) for 5 section grids when they scroll into view:

- .features-grid (The Concierge Standard — includes Free Trial card)
- .faq-grid
- .pricing-grid
- .blog-grid
- .final-cta .container

Uses native IntersectionObserver (threshold 0.12, no extra CDN load). Children
are pre-set to hidden state at page load to prevent flash, then animated once
per section via GSAP. Animate-once guard per section via dataset flag.
This commit is contained in:
2026-08-20 10:32:19 +00:00
parent 9a9319e3a3
commit 3e9a6d5c4b
+36
View File
@@ -623,3 +623,39 @@ var si = 0,
blogState.currentPage = 1;
renderBlog();
});
/* ---- Scroll-triggered section animations ---- */
(function () {
var animated = {};
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
var grid = entry.target;
if (animated[grid.dataset.section]) return;
animated[grid.dataset.section] = true;
var items = grid.children;
gsap.fromTo(
items,
{ y: 30, opacity: 0 },
{
y: 0,
opacity: 1,
duration: 0.7,
ease: 'power2.out',
stagger: 0.08,
overwrite: 'auto'
}
);
});
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
var grids = document.querySelectorAll(
'.features-grid, .faq-grid, .pricing-grid, .blog-grid, .final-cta .container'
);
grids.forEach(function (g) {
g.dataset.section = g.className || 'anon';
/* Pre-set children to hidden state so they start invisible */
gsap.set(g.children, { y: 30, opacity: 0 });
observer.observe(g);
});
})();