// ========== SERVICE FILTER ========== (function() { const searchInput = document.getElementById('service-filter-search'); const statusSelect = document.getElementById('service-filter-status'); const countSpan = document.getElementById('service-filter-count'); function updateFilter() { const query = searchInput.value.toLowerCase().trim(); const statusFilter = statusSelect.value; // 'all', 'on', or 'off' const cards = document.querySelectorAll('#cards .card'); let visibleCount = 0; cards.forEach(card => { const name = card.querySelector('.name')?.textContent?.toLowerCase() || ''; const app = card.dataset.app?.toLowerCase() || ''; const status = card.dataset.status || 'off'; // 'on' or 'off' const matchesSearch = !query || name.includes(query) || app.includes(query); const matchesStatus = statusFilter === 'all' || status === statusFilter; if (matchesSearch && matchesStatus) { card.style.display = ''; visibleCount++; } else { card.style.display = 'none'; } }); if (countSpan) { const total = cards.length; countSpan.textContent = `${visibleCount} of ${total} services`; } } // Debounce helper function debounce(fn, delay) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => fn.apply(this, args), delay); }; } searchInput?.addEventListener('input', debounce(updateFilter, 200)); statusSelect?.addEventListener('change', updateFilter); // Initial count on page load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => setTimeout(updateFilter, 500)); } else { setTimeout(updateFilter, 500); } // Expose for external triggers window.refreshServiceFilter = updateFilter; })();