Spaces:
Running
Running
| // Mobile menu toggle | |
| const mobileMenuBtn = document.getElementById('mobileMenuBtn'); | |
| const mobileMenu = document.getElementById('mobileMenu'); | |
| if (mobileMenuBtn) { | |
| mobileMenuBtn.addEventListener('click', () => { | |
| const isOpen = !mobileMenu.classList.contains('hidden'); | |
| mobileMenu.classList.toggle('hidden'); | |
| mobileMenuBtn.setAttribute('aria-expanded', String(!isOpen)); | |
| }); | |
| } | |
| // Reveal on scroll | |
| const revealEls = document.querySelectorAll('.reveal'); | |
| const revealObserver = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('visible'); | |
| revealObserver.unobserve(entry.target); | |
| } | |
| }); | |
| }, { threshold: 0.15 }); | |
| revealEls.forEach(el => revealObserver.observe(el)); | |
| // Testimonials slider | |
| const track = document.getElementById('testimonialTrack'); | |
| const slides = track ? Array.from(track.children) : []; | |
| const prevBtn = document.getElementById('prevTestimonial'); | |
| const nextBtn = document.getElementById('nextTestimonial'); | |
| const dotsWrap = document.getElementById('testimonialDots'); | |
| let current = 0; | |
| let autoTimer = null; | |
| function updateSlider(index) { | |
| if (!track) return; | |
| current = (index + slides.length) % slides.length; | |
| const width = track.clientWidth; | |
| track.style.transform = `translateX(-${current * width}px)`; | |
| // Update dots | |
| Array.from(dotsWrap.children).forEach((dot, i) => { | |
| dot.classList.toggle('bg-brand-yellow', i === current); | |
| dot.classList.toggle('bg-gray-300', i !== current); | |
| }); | |
| } | |
| function createDots() { | |
| if (!dotsWrap || slides.length === 0) return; | |
| dotsWrap.innerHTML = ''; | |
| slides.forEach((_, i) => { | |
| const dot = document.createElement('button'); | |
| dot.className = 'h-2 w-2 rounded-full bg-gray-300'; | |
| dot.setAttribute('aria-label', `Go to slide ${i + 1}`); | |
| dot.addEventListener('click', () => { | |
| updateSlider(i); | |
| resetAuto(); | |
| }); | |
| dotsWrap.appendChild(dot); | |
| }); | |
| updateSlider(0); | |
| } | |
| function nextSlide() { | |
| updateSlider(current + 1); | |
| } | |
| function prevSlide() { | |
| updateSlider(current - 1); | |
| } | |
| function startAuto() { | |
| if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; | |
| stopAuto(); | |
| autoTimer = setInterval(nextSlide, 6000); | |
| } | |
| function stopAuto() { | |
| if (autoTimer) clearInterval(autoTimer); | |
| } | |
| function resetAuto() { | |
| stopAuto(); | |
| startAuto(); | |
| } | |
| if (nextBtn && prevBtn) { | |
| nextBtn.addEventListener('click', () => { nextSlide(); resetAuto(); }); | |
| prevBtn.addEventListener('click', () => { prevSlide(); resetAuto(); }); | |
| } | |
| window.addEventListener('resize', () => updateSlider(current)); | |
| createDots(); | |
| startAuto(); | |
| // Case studies modal | |
| const caseModal = document.getElementById('caseModal'); | |
| const caseTitle = document.getElementById('caseTitle'); | |
| const caseBody = document.getElementById('caseBody'); | |
| const closeCase = document.getElementById('closeCase'); | |
| const CASES = { | |
| fintech: { | |
| title: 'Fintech Red Team Engagement', | |
| body: ` | |
| <h4>Objective</h4> | |
| <p>Assess the resilience of critical applications and infrastructure against realistic attacker TTPs.</p> | |
| <h4>Approach</h4> | |
| <ul> | |
| <li>External and internal network penetration testing</li> | |
| <li>Web application and API testing (OWASP Top 10)</li> | |
| <li>Social engineering campaign</li> | |
| <li>Exploit validation in a safe, coordinated manner</li> | |
| </ul> | |
| <h4>Results</h4> | |
| <ul> | |
| <li>Identified a zero‑day chain leading to domain admin</li> | |
| <li>Provided prioritized remediation roadmap</li> | |
| <li>Retest confirmed effective mitigation</li> | |
| </ul> | |
| <p>Post‑engagement, the client adopted continuous automated security testing in CI/CD.</p> | |
| ` | |
| }, | |
| hipaa: { | |
| title: 'HIPAA Compliance Program', | |
| body: ` | |
| <h4>Objective</h4> | |
| <p>Build a HIPAA compliance program aligned to the Security Rule with audit readiness.</p> | |
| <h4>Approach</h4> | |
| <ul> | |
| <li>Gap analysis against HIPAA requirements</li> | |
| <li>Policy, procedure, and control design</li> | |
| <li>Technical safeguard implementation (encryption, IAM, logging)</li> | |
| <li>Risk assessment and workforce training</li> | |
| </ul> | |
| <h4>Results</h4> | |
| <ul> | |
| <li>Passed third‑party audit with zero major findings</li> | |
| <li>Reduced risk exposure by 62% in 90 days</li> | |
| <li>Established continuous compliance monitoring</li> | |
| </ul> | |
| ` | |
| }, | |
| ransomware: { | |
| title: 'Ransomware Containment', | |
| body: ` | |
| <h4>Objective</h4> | |
| <p>Contain active ransomware and restore operations with minimal disruption.</p> | |
| <h4>Approach</h4> | |
| <ul> | |
| <li>Immediate triage and scoping</li> | |
| <li>Network segmentation to limit spread</li> | |
| <li>Forensic preservation and root cause analysis</li> | |
| <li>Recovery from validated backups</li> | |
| </ul> | |
| <h4>Results</h4> | |
| <ul> | |
| <li>Mean time to contain: 14 minutes</li> | |
| <li>No data loss; operations resumed within hours</li> | |
| <li>Hardened controls to prevent recurrence</li> | |
| </ul> | |
| ` | |
| } | |
| }; | |
| document.querySelectorAll('.case-study-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| const key = btn.dataset.case; | |
| const data = CASES[key]; | |
| if (!data) return; | |
| caseTitle.textContent = data.title; | |
| caseBody.innerHTML = data.body; | |
| caseModal.classList.remove('hidden'); | |
| document.body.style.overflow = 'hidden'; | |
| }); | |
| }); | |
| function closeModal() { | |
| caseModal.classList.add('hidden'); | |
| document.body.style.overflow = ''; | |
| } | |
| closeCase?.addEventListener('click', closeModal); | |
| caseModal?.addEventListener('click', (e) => { | |
| if (e.target === caseModal) closeModal(); | |
| }); | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape' && !caseModal.classList.contains('hidden')) closeModal(); | |
| }); | |
| // Back to top button | |
| const backToTop = document.getElementById('backToTop'); | |
| window.addEventListener('scroll', () => { | |
| if (window.scrollY > 600) { | |
| backToTop.classList.remove('hidden'); | |
| } else { | |
| backToTop.classList.add('hidden'); | |
| } | |
| }); | |
| backToTop?.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' })); | |
| // Quote form handling (demo) | |
| const form = document.getElementById('quoteForm'); | |
| const status = document.getElementById('formStatus'); | |
| form?.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const formData = new FormData(form); | |
| const payload = Object.fromEntries(formData.entries()); | |
| // Demo success | |
| status.textContent = 'Submitting...'; | |
| setTimeout(() => { | |
| status.textContent = 'Thanks! We will contact you shortly.'; | |
| console.log('Quote request:', payload); | |
| form.reset(); | |
| }, 800); | |
| }); | |
| // Set current year in footer | |
| document.getElementById('year').textContent = new Date().getFullYear(); |