// ===== SCROLL REVEAL — jf-* cards di dalam #assignment-body ===== function initScrollReveal() { const body = document.getElementById('assignment-body'); if (!body) return; const targets = body.querySelectorAll( '.jf-stat-card, .jf-stat-row-card, .jf-prestasi-card, ' + '.jf-sc-card, .jf-karir-card, .jf-alumni-card, ' + '.jf-infra-card, .jf-mitra-card, .jf-tl-item, .jf-kontak-item' ); targets.forEach(el => { el.style.opacity = '0'; el.style.transform = 'translateY(24px)'; el.style.transition = 'opacity 0.55s ease, transform 0.55s ease'; }); const obs = new IntersectionObserver((entries) => { entries.forEach((entry, i) => { if (!entry.isIntersecting) return; setTimeout(() => { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; }, i * 70); obs.unobserve(entry.target); }); }, { threshold: 0.08 }); targets.forEach(el => obs.observe(el)); } // ===== PRESTASI FILTER — tab filter di dalam #assignment-body ===== function initPrestasiFilter() { const body = document.getElementById('assignment-body'); if (!body) return; const tabs = body.querySelectorAll('.jf-tab-btn'); const cards = body.querySelectorAll('#prestasi-grid .jf-prestasi-card'); if (!tabs.length) return; tabs.forEach(tab => { tab.addEventListener('click', () => { tabs.forEach(t => t.classList.remove('jf-tab-active')); tab.classList.add('jf-tab-active'); const filter = tab.dataset.filter; cards.forEach(card => { const show = filter === 'all' || card.dataset.cat === filter; card.style.display = show ? '' : 'none'; if (show) card.style.animation = 'jfFadeIn 0.35s ease forwards'; }); }); }); } // ===== ANIMATED COUNTER — .jf-stat-num di dalam #assignment-body ===== function initCounters() { const body = document.getElementById('assignment-body'); if (!body) return; const obs = new IntersectionObserver((entries) => { entries.forEach(entry => { if (!entry.isIntersecting) return; const el = entry.target; const raw = el.textContent.trim(); const num = parseFloat(raw.replace(/[^0-9.]/g, '')); if (isNaN(num) || num === 0) return; const prefix = raw.match(/^[^0-9]*/)[0]; const suffix = raw.match(/[^0-9.]*$/)[0]; let start = 0; const step = 16; const increment = num / (1400 / step); const timer = setInterval(() => { start = Math.min(start + increment, num); const display = Number.isInteger(num) ? Math.floor(start).toLocaleString('id-ID') : start.toFixed(1); el.textContent = prefix + display + suffix; if (start >= num) clearInterval(timer); }, step); obs.unobserve(el); }); }, { threshold: 0.5 }); body.querySelectorAll('.jf-stat-num').forEach(el => obs.observe(el)); } // ===== INIT ===== initScrollReveal(); initPrestasiFilter(); initCounters();