// ─── Scroll reveal ───────────────────────────────────────── const reveals = document.querySelectorAll('.reveal'); const observer = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); observer.unobserve(e.target); } }); }, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' }); reveals.forEach(el => observer.observe(el)); // ─── Active nav link on scroll ────────────────────────────── const sections = document.querySelectorAll('section[id]'); const navLinks = document.querySelectorAll('.nav-links a'); window.addEventListener('scroll', () => { let current = ''; sections.forEach(s => { if (window.scrollY >= s.offsetTop - 120) current = s.id; }); navLinks.forEach(a => { a.style.color = a.getAttribute('href') === `#${current}` ? 'var(--mauve)' : ''; }); }); // ─── Project filters ────────────────────────────────────────────── const filterChips = document.querySelectorAll('.filter-chip'); const projectCards = document.querySelectorAll('#project-grid .project-card'); const seeMoreButton = document.querySelector('#projects-see-more'); const DEFAULT_VISIBLE_PROJECTS = 8; if (filterChips.length > 0 && projectCards.length > 0) { let selectedFilter = 'all'; let isExpanded = false; const applyFilter = () => { const visibleCards = []; projectCards.forEach(card => { const tech = (card.dataset.tech || '').split(/\s+/).filter(Boolean); const shouldShow = selectedFilter === 'all' || tech.includes(selectedFilter); card.classList.toggle('is-hidden', !shouldShow); if (shouldShow) { card.classList.remove('is-collapsed'); visibleCards.push(card); } }); if (!isExpanded) { visibleCards.slice(DEFAULT_VISIBLE_PROJECTS).forEach(card => { card.classList.add('is-collapsed'); }); } if (seeMoreButton) { const canExpand = visibleCards.length > DEFAULT_VISIBLE_PROJECTS; seeMoreButton.hidden = !canExpand; seeMoreButton.setAttribute('aria-expanded', canExpand && isExpanded ? 'true' : 'false'); seeMoreButton.textContent = isExpanded ? 'See less' : 'See more'; } }; filterChips.forEach(chip => { chip.addEventListener('click', () => { selectedFilter = chip.dataset.filter || 'all'; isExpanded = false; filterChips.forEach(other => { other.classList.toggle('active', other === chip); }); applyFilter(); }); }); if (seeMoreButton) { seeMoreButton.addEventListener('click', () => { isExpanded = !isExpanded; applyFilter(); }); } applyFilter(); }