diff --git a/404.html b/404.html index 0b0d978..49b2e8b 100755 --- a/404.html +++ b/404.html @@ -91,13 +91,13 @@ Oops! The page you're looking for doesn't exist or has been moved. Let's get you back on track.

- + Back to Homepage diff --git a/about.html b/about.html index 56bf5cc..cd0972b 100755 --- a/about.html +++ b/about.html @@ -218,7 +218,7 @@ -
+ diff --git a/api/news.php b/api/news.php index dbb06bd..85f3ae8 100755 --- a/api/news.php +++ b/api/news.php @@ -8,6 +8,11 @@ require_once 'config.php'; $method = $_SERVER['REQUEST_METHOD']; $id = $_GET['id'] ?? null; +// CSRF protection for write operations +if ($method !== 'GET') { + requireSameOriginRequest(); +} + switch ($method) { case 'GET': if ($id) { diff --git a/api/pages.php b/api/pages.php index 8bed5d0..ed4ee21 100755 --- a/api/pages.php +++ b/api/pages.php @@ -11,6 +11,11 @@ $method = $_SERVER['REQUEST_METHOD']; $id = $_GET['id'] ?? null; $type = $_GET['type'] ?? null; // 'global' for global sections +// CSRF protection for write operations +if ($method !== 'GET') { + requireSameOriginRequest(); +} + switch ($method) { case 'GET': if ($type === 'global') { diff --git a/api/portfolio.php b/api/portfolio.php index 920af56..1146b48 100755 --- a/api/portfolio.php +++ b/api/portfolio.php @@ -8,6 +8,11 @@ require_once 'config.php'; $method = $_SERVER['REQUEST_METHOD']; $id = $_GET['id'] ?? null; +// CSRF protection for write operations +if ($method !== 'GET') { + requireSameOriginRequest(); +} + switch ($method) { case 'GET': if ($id) { diff --git a/api/services.php b/api/services.php index 9f39fcf..45dbefb 100755 --- a/api/services.php +++ b/api/services.php @@ -8,6 +8,11 @@ require_once 'config.php'; $method = $_SERVER['REQUEST_METHOD']; $id = $_GET['id'] ?? null; +// CSRF protection for write operations +if ($method !== 'GET') { + requireSameOriginRequest(); +} + switch ($method) { case 'GET': if ($id) { diff --git a/api/team.php b/api/team.php index 1dbf2b9..cd92aea 100755 --- a/api/team.php +++ b/api/team.php @@ -8,6 +8,11 @@ require_once 'config.php'; $method = $_SERVER['REQUEST_METHOD']; $id = $_GET['id'] ?? null; +// CSRF protection for write operations +if ($method !== 'GET') { + requireSameOriginRequest(); +} + switch ($method) { case 'GET': if ($id) { diff --git a/api/testimonials.php b/api/testimonials.php index 586d6ae..6d86d94 100755 --- a/api/testimonials.php +++ b/api/testimonials.php @@ -8,6 +8,11 @@ require_once 'config.php'; $method = $_SERVER['REQUEST_METHOD']; $id = $_GET['id'] ?? null; +// CSRF protection for write operations +if ($method !== 'GET') { + requireSameOriginRequest(); +} + switch ($method) { case 'GET': if ($id) { diff --git a/css/header-fix.css b/css/header-fix.css index b2f256f..ae99978 100755 --- a/css/header-fix.css +++ b/css/header-fix.css @@ -19,7 +19,7 @@ body.premium-ui .header { } body.premium-ui .header.scrolled { - background: rgba(3, 7, 16, 0.97) !important; + background: rgba(14, 30, 52, 0.94) !important; border-bottom: 1px solid rgba(148, 163, 184, 0.1) !important; } diff --git a/css/pages.css b/css/pages.css index 9e74b1a..0abdd23 100755 --- a/css/pages.css +++ b/css/pages.css @@ -53,6 +53,8 @@ position: relative; z-index: 1; text-align: center; + max-width: 900px; + margin: 0 auto; } .breadcrumb { @@ -89,6 +91,7 @@ color: var(--text-white); margin-bottom: 1rem; line-height: 1.2; + text-wrap: balance; } .page-subtitle { @@ -97,6 +100,7 @@ max-width: 600px; margin: 0 auto; line-height: 1.7; + text-wrap: pretty; } /* ======================== @@ -747,7 +751,7 @@ margin-bottom: 2rem; } -.service-features { +.service-content .service-features { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; @@ -1660,6 +1664,16 @@ gap: 1.5rem; } +@media (min-width: 1201px) { + .contact-cards > :nth-child(5) { + grid-column: 2; + } + + .contact-cards > :nth-child(6) { + grid-column: 3; + } +} + .contact-card { background: var(--text-white); padding: 2rem; @@ -2197,7 +2211,7 @@ grid-template-columns: repeat(2, 1fr); } - .service-features, + .service-content .service-features, .service-features-grid { grid-template-columns: 1fr; } diff --git a/css/style.css b/css/style.css index 144c6d0..9c26193 100755 --- a/css/style.css +++ b/css/style.css @@ -6,9 +6,9 @@ /* ==================== CSS Variables ==================== */ :root { /* Primary Colors */ - --primary: #0a1628; - --primary-light: #1a365d; - --primary-dark: #050d17; + --primary: #10223b; + --primary-light: #1f3f69; + --primary-dark: #0b1930; /* Accent Colors */ --accent: #0ea5e9; @@ -55,7 +55,7 @@ --text-muted: #94a3b8; /* Background Colors */ - --bg-dark: #0a1628; + --bg-dark: #122a47; --bg-light: #f8fafc; /* Border Color */ @@ -235,6 +235,7 @@ select { color: var(--gray-900); margin-bottom: var(--spacing-md); line-height: 1.2; + text-wrap: balance; } .section-description { @@ -256,10 +257,13 @@ select { padding: 0.75rem 1.5rem; font-weight: 600; font-size: 0.9375rem; + line-height: 1.2; + min-height: 44px; border-radius: var(--radius-md); transition: var(--transition-base); cursor: pointer; border: 2px solid transparent; + white-space: nowrap; } .btn-primary { @@ -532,6 +536,10 @@ select { gap: var(--spacing-md); } +.nav-actions .btn { + min-width: 168px; +} + .nav-toggle { display: none; flex-direction: column; @@ -641,6 +649,7 @@ select { word-break: normal; overflow-wrap: normal; hyphens: none; + text-wrap: balance; } .hero-title .highlight { @@ -1049,6 +1058,23 @@ select { font-size: 0.75rem; } +.frameworks-grid .service-title { + min-height: 2.8em; +} + +.frameworks-grid .service-description { + min-height: 5.6em; +} + +.frameworks-grid .service-features li { + align-items: flex-start; +} + +.frameworks-grid .service-features li i { + margin-top: 0.35em; + flex-shrink: 0; +} + .service-link { display: inline-flex; align-items: center; @@ -1073,6 +1099,13 @@ select { color: var(--accent-light); } +@media (max-width: 1200px) { + .frameworks-grid .service-title, + .frameworks-grid .service-description { + min-height: 0; + } +} + /* ==================== Why Us Section ==================== */ .why-us { background: #050810; @@ -1712,10 +1745,14 @@ select { .footer-logo { display: flex; align-items: center; + justify-content: center; gap: 0.625rem; margin-bottom: var(--spacing-md); + margin-left: auto; + margin-right: auto; text-decoration: none; line-height: 1; + width: 100%; } .footer-logo img { @@ -1723,6 +1760,7 @@ select { height: 60px; width: auto; max-width: 220px; + margin: 0 auto; } .footer-logo .logo-text { diff --git a/css/ultimate-ui.css b/css/ultimate-ui.css index 22fbfb6..83ec990 100755 --- a/css/ultimate-ui.css +++ b/css/ultimate-ui.css @@ -107,10 +107,11 @@ /* Enhanced Body Background */ body { - background: #050810; + background: #0d2038; background-image: - radial-gradient(ellipse at 0% 0%, rgba(0, 245, 255, 0.06) 0%, transparent 40%), - radial-gradient(ellipse at 100% 0%, rgba(181, 55, 242, 0.06) 0%, transparent 40%); + radial-gradient(ellipse at 0% 0%, rgba(0, 245, 255, 0.1) 0%, transparent 42%), + radial-gradient(ellipse at 100% 0%, rgba(181, 55, 242, 0.09) 0%, transparent 42%), + linear-gradient(180deg, #17335a 0%, #10223d 45%, #0d1d34 100%); /* background-attachment: fixed removed — causes full repaint on every scroll */ position: relative; min-height: 100vh; diff --git a/js/main.js b/js/main.js index 79f9f4a..e0b37f6 100755 --- a/js/main.js +++ b/js/main.js @@ -6,6 +6,22 @@ // API Base URL - adjust based on your hosting setup const API_BASE_URL = '/api'; +// Central icon map: change values here to swap icons site-wide where data-icon-key is used. +const ICON_CLASS_MAP = { + service_it_support: 'fas fa-headset', + service_cybersecurity: 'fas fa-shield-alt', + service_cloud: 'fas fa-cloud', + service_consulting: 'fas fa-chess-king', + framework_iso27001: 'fas fa-certificate', + framework_nist_csf: 'fas fa-sitemap', + framework_soc2: 'fas fa-user-shield', + framework_pci_dss: 'fas fa-credit-card', + framework_hipaa: 'fas fa-heartbeat', + framework_gdpr_ccpa: 'fas fa-scale-balanced', + framework_nist_800: 'fas fa-building-shield', + framework_cobit_telco: 'fas fa-network-wired' +}; + /** * Escape HTML special characters to prevent XSS when inserting into innerHTML. * Use for ALL user/API data interpolated into template literals. @@ -40,6 +56,7 @@ document.addEventListener('DOMContentLoaded', function() { // Initialize all modules initPremiumExperience(); initSiteLinkEnhancements(); + initReplaceableIcons(); optimizeImagesForCLS(); loadPublicSiteSettings(); initPreloader(); @@ -62,6 +79,50 @@ document.addEventListener('DOMContentLoaded', function() { loadTeam(); }); +function sanitizeIconClasses(iconClasses, fallback = 'fas fa-circle') { + const tokens = String(iconClasses || '') + .trim() + .split(/\s+/) + .filter(Boolean) + .filter(token => /^fa[a-z0-9-]+$/i.test(token)); + + if (!tokens.length) { + return fallback.split(/\s+/); + } + + const hasStyleToken = tokens.some(token => /^(fas|far|fab|fal|fat|fa-solid|fa-regular|fa-brands)$/i.test(token)); + if (!hasStyleToken) { + tokens.unshift('fas'); + } + + return tokens; +} + +function initReplaceableIcons(scope = document) { + const iconNodes = scope.querySelectorAll('[data-icon-key], [data-icon]'); + if (!iconNodes.length) return; + + iconNodes.forEach(node => { + const targetIcon = node.matches('i') ? node : node.querySelector('i'); + if (!targetIcon) return; + + const explicitIcon = node.getAttribute('data-icon') || targetIcon.getAttribute('data-icon'); + const iconKey = node.getAttribute('data-icon-key') || targetIcon.getAttribute('data-icon-key'); + const mappedIcon = iconKey ? ICON_CLASS_MAP[iconKey] : ''; + const selected = explicitIcon || mappedIcon; + if (!selected) return; + + Array.from(targetIcon.classList).forEach(cls => { + if (/^fa/i.test(cls)) { + targetIcon.classList.remove(cls); + } + }); + + sanitizeIconClasses(selected).forEach(cls => targetIcon.classList.add(cls)); + targetIcon.setAttribute('aria-hidden', 'true'); + }); +} + function initSiteLinkEnhancements() { // Fallback social URLs: only used for placeholder links (href="#" or "javascript:void(0)"). // Preferred approach: set correct URLs directly in HTML. These are a safety net. @@ -536,6 +597,7 @@ function animateCounter(element, target) { } /* ==================== Testimonials Slider ==================== */ +let _testimonialInterval = null; function initTestimonials() { const testimonials = document.querySelectorAll('.testimonial-card'); const prevBtn = document.querySelector('.testimonial-prev'); @@ -544,8 +606,17 @@ function initTestimonials() { if (!testimonials.length) return; + // Clear any previous interval to prevent stacking + if (_testimonialInterval) { + clearInterval(_testimonialInterval); + _testimonialInterval = null; + } + let currentIndex = 0; + // Clear previous dots before recreating + if (dotsContainer) dotsContainer.innerHTML = ''; + // Create dots testimonials.forEach((_, index) => { const dot = document.createElement('span'); @@ -587,7 +658,7 @@ function initTestimonials() { testimonialContainer.addEventListener('focusin', () => { testimonialPaused = true; }); testimonialContainer.addEventListener('focusout', () => { testimonialPaused = false; }); } - setInterval(() => { + _testimonialInterval = setInterval(() => { if (!testimonialPaused) goToTestimonial(currentIndex + 1); }, 8000); } @@ -951,7 +1022,7 @@ async function loadServices() { return `
-
+

${escapeHTML(service.name || 'Service')}

@@ -969,6 +1040,8 @@ async function loadServices() { servicesGrids.forEach(grid => { grid.innerHTML = servicesMarkup; }); + + initReplaceableIcons(); } else { // No API data returned — static fallback cards already rendered in HTML } @@ -981,9 +1054,15 @@ async function loadServices() { /* ==================== Load Team from API ==================== */ async function loadTeam() { const teamGrid = document.querySelector('[data-team-grid]'); + const teamSection = teamGrid ? teamGrid.closest('.team-section') : null; if (!teamGrid) return; + // Keep hidden until admin-managed content exists + if (teamSection) { + teamSection.hidden = true; + } + try { const response = await fetch(`${API_BASE_URL}/team.php`); const result = await response.json(); @@ -1018,8 +1097,15 @@ async function loadTeam() {
`; }).join(''); + + if (teamSection) { + teamSection.hidden = false; + } + } else { + teamGrid.innerHTML = ''; } } catch (error) { + teamGrid.innerHTML = ''; console.log('Using static team content or API unavailable'); } } diff --git a/services.html b/services.html index 182817b..5c1a8bf 100755 --- a/services.html +++ b/services.html @@ -158,7 +158,7 @@
-
+

IT Support & Maintenance

Proactive managed IT support that keeps your systems running at peak performance. From helpdesk to infrastructure management, we handle it all.

-
+

Cybersecurity Solutions

End-to-end security services to protect your business from evolving threats. Risk assessment, vulnerability management, and incident response.

-
+

Cloud Services

Cloud architecture, migration, and optimization. We design scalable, secure cloud environments tailored to your business needs.

-
+

Strategic IT Consulting

Expert guidance to align technology with your business goals. We deliver roadmaps, vendor selection, and digital transformation strategies.

    @@ -205,6 +205,114 @@
+ +
+
+
+ +

Which Security Framework Fits Your Industry?

+

Frameworks are not just checklists. They help you reduce risk, pass audits, and win client trust. We translate each framework into a practical roadmap your team can execute.

+
+ +
+
+
+

ISO 27001

+

Best for: Organizations in regulated sectors or those selling B2B internationally.

+
    +
  • Information Security Management System (ISMS)
  • +
  • Strong governance and audit readiness
  • +
  • Often required in enterprise procurement
  • +
+
+ +
+
+

NIST Cybersecurity Framework

+

Best for: Critical infrastructure, large organizations, and mature risk programs.

+
    +
  • Identify, Protect, Detect, Respond, Recover
  • +
  • Practical risk-based architecture
  • +
  • Excellent baseline for long-term maturity
  • +
+
+ +
+
+

SOC 2

+

Best for: SaaS, MSPs, cloud service providers, and data-processing companies.

+
    +
  • Trust criteria: Security, Availability, Confidentiality
  • +
  • Frequently requested by enterprise customers
  • +
  • Strong commercial trust signal
  • +
+
+ +
+
+

PCI DSS

+

Best for: Merchants and businesses that store, process, or transmit card data.

+
    +
  • Payment card data protection controls
  • +
  • Network segmentation and monitoring
  • +
  • Mandatory for card-handling environments
  • +
+
+ +
+
+

HIPAA

+

Best for: Healthcare entities and vendors handling protected health information.

+
    +
  • Administrative, technical, and physical safeguards
  • +
  • Data privacy and breach response expectations
  • +
  • Essential for U.S. healthcare-related operations
  • +
+
+ +
+
+

GDPR & CCPA/CPRA

+

Best for: Organizations collecting personal data from EU or California residents.

+
    +
  • Privacy-by-design and data subject rights
  • +
  • Consent, retention, and transparency controls
  • +
  • Reduces legal and reputational exposure
  • +
+
+ +
+
+

NIST SP 800-53 / 800-171

+

Best for: Government suppliers and organizations handling controlled data.

+
    +
  • Structured control catalog for federal ecosystems
  • +
  • Required in many public-sector contracts
  • +
  • Strong alignment with secure architecture practices
  • +
+
+ +
+
+

COBIT / Telecom Frameworks

+

Best for: Telecom, utilities, and governance-heavy environments.

+
    +
  • Governance, control objectives, and accountability
  • +
  • Useful for board-level reporting and assurance
  • +
  • Helps connect cyber risk with business outcomes
  • +
+
+
+ +
+

Not sure which framework applies to your business? We can map your obligations and priorities in one session.

+ Get My Compliance Roadmap +
+ +

Educational guidance only. Regulatory and legal requirements vary by jurisdiction and contract terms.

+
+
+