// Home page specific JS: team carousel + modal document.addEventListener('DOMContentLoaded', () => { // Fetch team data and render fetch('assets/data/team.json') .then(res => res.json()) .then(members => { initTeamSection(members); }) .catch(err => { console.error('Failed to load team data', err); }); function initTeamSection(members) { const carousel = document.getElementById('team-carousel'); const dotsContainer = document.getElementById('carousel-dots'); const prevBtn = document.getElementById('carousel-prev'); const nextBtn = document.getElementById('carousel-next'); // Render cards carousel.innerHTML = members.map((m, i) => `
${m.avatar}

${m.name}

${m.role}

${(m.bio || '').split('.').shift()}.

`).join(''); // Carousel logic const cardsContainer = carousel; // the track let cards = carousel.querySelectorAll('.team-card'); let currentIndex = 0; let cardsPerView = 3; let autoPlayInterval; function updateCardsPerView() { if (window.innerWidth <= 768) cardsPerView = 1; else if (window.innerWidth <= 1024) cardsPerView = 2; else cardsPerView = 3; } function createDots() { dotsContainer.innerHTML = ''; const totalDots = Math.max(1, Math.ceil(cards.length / cardsPerView)); for (let i = 0; i < totalDots; i++) { const dot = document.createElement('div'); dot.classList.add('carousel-dot'); if (i === 0) dot.classList.add('active'); dot.addEventListener('click', () => goToSlide(i)); dotsContainer.appendChild(dot); } } function updateCarousel() { cards = carousel.querySelectorAll('.team-card'); if (cards.length === 0) return; const cardStyle = window.getComputedStyle(cards[0]); const gap = 32; // matches previous implementation const cardWidth = cards[0].offsetWidth + gap; carousel.style.transform = `translateX(-${currentIndex * cardWidth}px)`; const dots = dotsContainer.querySelectorAll('.carousel-dot'); const activeDotIndex = Math.floor(currentIndex / cardsPerView); dots.forEach((dot, i) => dot.classList.toggle('active', i === activeDotIndex)); } function goToSlide(dotIndex) { currentIndex = dotIndex * cardsPerView; if (currentIndex > cards.length - cardsPerView) currentIndex = Math.max(0, cards.length - cardsPerView); updateCarousel(); resetAutoPlay(); } function nextSlide() { currentIndex++; if (currentIndex > cards.length - cardsPerView) currentIndex = 0; updateCarousel(); } function prevSlide() { currentIndex--; if (currentIndex < 0) currentIndex = Math.max(0, cards.length - cardsPerView); updateCarousel(); } function startAutoPlay() { clearInterval(autoPlayInterval); autoPlayInterval = setInterval(nextSlide, 4000); } function resetAutoPlay() { clearInterval(autoPlayInterval); startAutoPlay(); } prevBtn && prevBtn.addEventListener('click', () => { prevSlide(); resetAutoPlay(); }); nextBtn && nextBtn.addEventListener('click', () => { nextSlide(); resetAutoPlay(); }); carousel.addEventListener('mouseenter', () => clearInterval(autoPlayInterval)); carousel.addEventListener('mouseleave', startAutoPlay); updateCardsPerView(); createDots(); startAutoPlay(); window.addEventListener('resize', () => { updateCardsPerView(); createDots(); currentIndex = 0; updateCarousel(); }); // Modal logic const modalOverlay = document.getElementById('team-modal-overlay'); const modalClose = document.getElementById('team-modal-close'); const modalAvatar = document.getElementById('modal-avatar'); const modalName = document.getElementById('modal-name'); const modalRole = document.getElementById('modal-role'); const modalBio = document.getElementById('modal-bio'); const modalStats = document.getElementById('modal-stats'); const modalSkills = document.getElementById('modal-skills'); const modalSocial = document.getElementById('modal-social'); function openMemberModal(index) { const member = members[index]; if (!member) return; modalAvatar.textContent = member.avatar || member.name.charAt(0); modalName.textContent = member.name; modalRole.textContent = member.role; modalBio.textContent = member.bio; modalStats.innerHTML = (member.stats || []).map(stat => `
${stat.value}
${stat.label}
`).join(''); modalSkills.innerHTML = (member.skills || []).map(s => `${s}`).join(''); modalSocial.innerHTML = ` `; if (window.feather && feather.replace) feather.replace(); modalOverlay.classList.add('active'); document.body.style.overflow = 'hidden'; } function closeMemberModal() { modalOverlay.classList.remove('active'); document.body.style.overflow = ''; } // Attach click listeners to generated cards carousel.querySelectorAll('.team-card').forEach(card => { card.addEventListener('click', () => { const idx = parseInt(card.dataset.member, 10); openMemberModal(idx); }); }); modalClose && modalClose.addEventListener('click', closeMemberModal); modalOverlay && modalOverlay.addEventListener('click', (e) => { if (e.target === modalOverlay) closeMemberModal(); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay.classList.contains('active')) closeMemberModal(); }); } });