/** * About Page Animator * Cycles an is-active highlight through feature rows in .features-stage. * Respects prefers-reduced-motion and pauses via IntersectionObserver. * Mirrors the patterns and conventions of solutions-animator.js. */ (function () { 'use strict'; if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; var CYCLE_MS = 2000; function initFeatures(stage) { var items = stage.querySelectorAll('.feat-item'); if (!items.length) return; var state = { idx: 0, timer: null }; function advance() { items[state.idx].classList.remove('is-active'); state.idx = (state.idx + 1) % items.length; items[state.idx].classList.add('is-active'); } function startTimer() { if (state.timer) return; state.timer = setInterval(advance, CYCLE_MS); } function stopTimer() { clearInterval(state.timer); state.timer = null; } items[0].classList.add('is-active'); if ('IntersectionObserver' in window) { new IntersectionObserver(function (entries) { for (var i = 0; i < entries.length; i++) { entries[i].isIntersecting ? startTimer() : stopTimer(); } }, { rootMargin: '200px', threshold: 0.05 }).observe(stage); } else { startTimer(); } } function boot() { var stages = document.querySelectorAll('.features-stage'); if (!stages.length) return; for (var i = 0; i < stages.length; i++) { if (stages[i]._featAnim) continue; stages[i]._featAnim = true; initFeatures(stages[i]); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', boot); } else { boot(); } })();