Compare commits

..

No commits in common. "lighthouse" and "main" have entirely different histories.

55 changed files with 645 additions and 3104 deletions

View File

@ -1,12 +0,0 @@
{
"permissions": {
"allow": [
"Bash(npm run build:*)",
"Bash(npm install:*)",
"Bash(ls:*)",
"Bash(node scripts/optimize-images.js:*)",
"Bash(node scripts/optimize-images.cjs:*)",
"Skill(frontend-design)"
]
}
}

View File

@ -3,9 +3,9 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>IT Support Corpus Christi ⚡ 24/7 Managed Services | Bay Area Affiliates</title>
<meta name="description" content="Stop IT headaches! 25+ years experience, 2-hour response time, 24/7 monitoring. Managed IT services in Corpus Christi & Coastal Bend. Free assessment. Call (361) 765-8400" />
<meta name="keywords" content="managed IT services corpus christi, IT support coastal bend, 24/7 IT monitoring, business computer solutions, Windows 11 migration, VPN setup, network security corpus christi, same-day IT support" />
<title>Managed IT Services Corpus Christi | Bay Area Affiliates</title>
<meta name="description" content="Secure, tailored IT support—Corpus Christi's trusted experts for 25+ years. Call today for a free assessment." />
<meta name="keywords" content="managed IT services corpus christi, IT support coastal bend, business computer solutions, Portland IT services, computer repair corpus christi" />
<meta name="author" content="Bay Area Affiliates, Inc." />
<meta property="og:title" content="Corpus Christi Managed IT Experts. Reliable, Secure, Local." />
@ -211,33 +211,10 @@
}
</script>
<!-- Performance Optimization -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="dns-prefetch" href="https://fonts.gstatic.com" />
<!-- Non-blocking font loading with font-display: swap -->
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap" media="print" onload="this.media='all'" />
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap" /></noscript>
<!-- Critical asset preloads -->
<link rel="preload" href="/logo_bayarea.svg" as="image" />
<link rel="preload" href="/serverroom.webp" as="image" type="image/webp" />
<!-- Canonical URL -->
<link rel="canonical" href="https://bayarea-cc.com/" />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/logo_bayarea.svg" />
<link rel="apple-touch-icon" sizes="180x180" href="/logo_bayarea.svg" />
<!-- PWA / Mobile -->
<meta name="theme-color" content="#3366ff" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="manifest" href="/manifest.json" />
<link rel="icon" type="image/png" href="/logo_bayarea.svg" />
<!-- Note: For production, consider converting logo_bayarea.svg to favicon.ico for better browser support -->
</head>
<body>

682
package-lock.json generated
View File

@ -42,7 +42,6 @@
"cmdk": "^1.1.1",
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.24.12",
"gsap": "^3.13.0",
"input-otp": "^1.4.2",
"lucide-react": "^0.462.0",
@ -74,13 +73,10 @@
"globals": "^15.15.0",
"lovable-tagger": "^1.1.9",
"postcss": "^8.5.6",
"sharp": "^0.34.5",
"tailwindcss": "^3.4.17",
"terser": "^5.44.1",
"typescript": "^5.8.3",
"typescript-eslint": "^8.38.0",
"vite": "^5.4.19",
"web-vitals": "^5.1.0"
"vite": "^5.4.19"
}
},
"node_modules/@alloc/quick-lru": {
@ -154,17 +150,6 @@
"node": ">=6.9.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
"integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
@ -874,496 +859,6 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@img/colour": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@img/sharp-darwin-arm64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-darwin-x64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-x64": "1.2.4"
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
"cpu": [
"arm"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-ppc64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-riscv64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-s390x": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
"cpu": [
"s390x"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-linux-arm": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
"cpu": [
"arm"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm": "1.2.4"
}
},
"node_modules/@img/sharp-linux-arm64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-linux-ppc64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-ppc64": "1.2.4"
}
},
"node_modules/@img/sharp-linux-riscv64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-riscv64": "1.2.4"
}
},
"node_modules/@img/sharp-linux-s390x": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-s390x": "1.2.4"
}
},
"node_modules/@img/sharp-linux-x64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-x64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
}
},
"node_modules/@img/sharp-wasm32": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
"cpu": [
"wasm32"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
"@emnapi/runtime": "^1.7.0"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-arm64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-ia32": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-x64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@ -1413,17 +908,6 @@
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
@ -4027,13 +3511,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@ -4391,16 +3868,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/detect-node-es": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
@ -4895,33 +4362,6 @@
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/framer-motion": {
"version": "12.24.12",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.24.12.tgz",
"integrity": "sha512-W+tBOI1SDGNMH4D4mADY95qYd16Drke2Tj9zlGlwTGSCi6yy8wbMmPY1mvirfcTK8HBeuuCd2PflHdN/zbL4ew==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.24.11",
"motion-utils": "^12.24.10",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -5434,21 +4874,6 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/motion-dom": {
"version": "12.24.11",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.11.tgz",
"integrity": "sha512-DlWOmsXMJrV8lzZyd+LKjG2CXULUs++bkq8GZ2Sr0R0RRhs30K2wtY+LKiTjhmJU3W61HK+rB0GLz6XmPvTA1A==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.24.10"
}
},
"node_modules/motion-utils": {
"version": "12.24.10",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.24.10.tgz",
"integrity": "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -6258,9 +5683,9 @@
}
},
"node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"bin": {
@ -6270,51 +5695,6 @@
"node": ">=10"
}
},
"node_modules/sharp": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@img/colour": "^1.0.0",
"detect-libc": "^2.1.2",
"semver": "^7.7.3"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-darwin-arm64": "0.34.5",
"@img/sharp-darwin-x64": "0.34.5",
"@img/sharp-libvips-darwin-arm64": "1.2.4",
"@img/sharp-libvips-darwin-x64": "1.2.4",
"@img/sharp-libvips-linux-arm": "1.2.4",
"@img/sharp-libvips-linux-arm64": "1.2.4",
"@img/sharp-libvips-linux-ppc64": "1.2.4",
"@img/sharp-libvips-linux-riscv64": "1.2.4",
"@img/sharp-libvips-linux-s390x": "1.2.4",
"@img/sharp-libvips-linux-x64": "1.2.4",
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
"@img/sharp-linux-arm": "0.34.5",
"@img/sharp-linux-arm64": "0.34.5",
"@img/sharp-linux-ppc64": "0.34.5",
"@img/sharp-linux-riscv64": "0.34.5",
"@img/sharp-linux-s390x": "0.34.5",
"@img/sharp-linux-x64": "0.34.5",
"@img/sharp-linuxmusl-arm64": "0.34.5",
"@img/sharp-linuxmusl-x64": "0.34.5",
"@img/sharp-wasm32": "0.34.5",
"@img/sharp-win32-arm64": "0.34.5",
"@img/sharp-win32-ia32": "0.34.5",
"@img/sharp-win32-x64": "0.34.5"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@ -6358,16 +5738,6 @@
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -6377,17 +5747,6 @@
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@ -6600,32 +5959,6 @@
"tailwindcss": ">=3.0.0 || insiders"
}
},
"node_modules/terser": {
"version": "5.44.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
},
"node_modules/terser/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@ -7372,13 +6705,6 @@
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/web-vitals": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.1.0.tgz",
"integrity": "sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@ -45,7 +45,6 @@
"cmdk": "^1.1.1",
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.24.12",
"gsap": "^3.13.0",
"input-otp": "^1.4.2",
"lucide-react": "^0.462.0",
@ -77,12 +76,9 @@
"globals": "^15.15.0",
"lovable-tagger": "^1.1.9",
"postcss": "^8.5.6",
"sharp": "^0.34.5",
"tailwindcss": "^3.4.17",
"terser": "^5.44.1",
"typescript": "^5.8.3",
"typescript-eslint": "^8.38.0",
"vite": "^5.4.19",
"web-vitals": "^5.1.0"
"vite": "^5.4.19"
}
}

View File

@ -1,33 +0,0 @@
# Security Headers for all routes
/*
# XSS Protection
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
# Referrer Policy
Referrer-Policy: strict-origin-when-cross-origin
# Permissions Policy
Permissions-Policy: geolocation=(self), microphone=(), camera=(), payment=()
# Strict Transport Security (HSTS)
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# Content Security Policy
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https: blob:; connect-src 'self' https://www.google-analytics.com https://*.googletagmanager.com; frame-ancestors 'none';
# Cache Control for static assets
Cache-Control: public, max-age=31536000, immutable
# Cache Control for HTML
/*.html
Cache-Control: public, max-age=0, must-revalidate
# Cache Control for service worker
/sw.js
Cache-Control: public, max-age=0, must-revalidate
# Cache Control for manifest
/manifest.json
Cache-Control: public, max-age=86400, must-revalidate

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

View File

@ -1,22 +0,0 @@
{
"name": "Bay Area Affiliates - Managed IT Services",
"short_name": "Bay Area IT",
"description": "Professional managed IT services in Corpus Christi & Coastal Bend",
"start_url": "/",
"display": "standalone",
"background_color": "#030303",
"theme_color": "#3366ff",
"orientation": "portrait-primary",
"icons": [
{
"src": "/logo_bayarea.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any maskable"
}
],
"categories": ["business", "productivity", "utilities"],
"scope": "/",
"lang": "en-US",
"dir": "ltr"
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

View File

@ -1,113 +0,0 @@
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
const PUBLIC_DIR = path.join(__dirname, '../public');
const WEBP_QUALITY = 80;
const AVIF_QUALITY = 80;
const PNG_QUALITY = 80;
async function optimizeImages() {
console.log('Starting image optimization...\n');
// Get all PNG files in public directory
const files = fs.readdirSync(PUBLIC_DIR)
.filter(file => file.endsWith('.png'))
.map(file => path.join(PUBLIC_DIR, file));
if (files.length === 0) {
console.log('No PNG files found in public directory');
return;
}
console.log(`Found ${files.length} PNG files to optimize\n`);
let totalOriginalSize = 0;
let totalWebpSize = 0;
let totalAvifSize = 0;
let totalCompressedPngSize = 0;
for (const filePath of files) {
const filename = path.basename(filePath);
const filenameWithoutExt = path.basename(filePath, '.png');
const originalSize = fs.statSync(filePath).size;
totalOriginalSize += originalSize;
console.log(`Processing: ${filename} (${(originalSize / 1024 / 1024).toFixed(2)} MB)`);
try {
// Generate WebP version
const webpPath = path.join(PUBLIC_DIR, `${filenameWithoutExt}.webp`);
await sharp(filePath)
.resize(1920, null, { withoutEnlargement: true, fit: 'inside' })
.webp({ quality: WEBP_QUALITY })
.toFile(webpPath);
const webpSize = fs.statSync(webpPath).size;
totalWebpSize += webpSize;
console.log(` ✓ WebP: ${(webpSize / 1024).toFixed(2)} KB (${((1 - webpSize / originalSize) * 100).toFixed(1)}% reduction)`);
// Generate AVIF version
const avifPath = path.join(PUBLIC_DIR, `${filenameWithoutExt}.avif`);
await sharp(filePath)
.resize(1920, null, { withoutEnlargement: true, fit: 'inside' })
.avif({ quality: AVIF_QUALITY })
.toFile(avifPath);
const avifSize = fs.statSync(avifPath).size;
totalAvifSize += avifSize;
console.log(` ✓ AVIF: ${(avifSize / 1024).toFixed(2)} KB (${((1 - avifSize / originalSize) * 100).toFixed(1)}% reduction)`);
// Compress original PNG
const compressedPngPath = path.join(PUBLIC_DIR, `${filenameWithoutExt}_compressed.png`);
await sharp(filePath)
.resize(1920, null, { withoutEnlargement: true, fit: 'inside' })
.png({
quality: PNG_QUALITY,
compressionLevel: 9,
palette: true
})
.toFile(compressedPngPath);
const compressedPngSize = fs.statSync(compressedPngPath).size;
totalCompressedPngSize += compressedPngSize;
console.log(` ✓ Compressed PNG: ${(compressedPngSize / 1024).toFixed(2)} KB (${((1 - compressedPngSize / originalSize) * 100).toFixed(1)}% reduction)`);
// Replace original with compressed version
fs.unlinkSync(filePath);
fs.renameSync(compressedPngPath, filePath);
console.log(` ✓ Original PNG replaced with compressed version\n`);
} catch (error) {
console.error(` ✗ Error processing ${filename}:`, error.message);
}
}
// Summary
console.log('\n' + '='.repeat(60));
console.log('OPTIMIZATION SUMMARY');
console.log('='.repeat(60));
console.log(`Total original PNG size: ${(totalOriginalSize / 1024 / 1024).toFixed(2)} MB`);
console.log(`Total WebP size: ${(totalWebpSize / 1024 / 1024).toFixed(2)} MB (${((1 - totalWebpSize / totalOriginalSize) * 100).toFixed(1)}% reduction)`);
console.log(`Total AVIF size: ${(totalAvifSize / 1024 / 1024).toFixed(2)} MB (${((1 - totalAvifSize / totalOriginalSize) * 100).toFixed(1)}% reduction)`);
console.log(`Total compressed PNG size: ${(totalCompressedPngSize / 1024 / 1024).toFixed(2)} MB (${((1 - totalCompressedPngSize / totalOriginalSize) * 100).toFixed(1)}% reduction)`);
console.log('\nAverage file sizes:');
console.log(` WebP: ${(totalWebpSize / files.length / 1024).toFixed(2)} KB`);
console.log(` AVIF: ${(totalAvifSize / files.length / 1024).toFixed(2)} KB`);
console.log(` Compressed PNG: ${(totalCompressedPngSize / files.length / 1024).toFixed(2)} KB`);
console.log('='.repeat(60));
// Check if targets are met
const avgWebpSize = totalWebpSize / files.length / 1024;
const avgAvifSize = totalAvifSize / files.length / 1024;
const totalSizeAfter = (totalWebpSize + totalAvifSize + totalCompressedPngSize) / 1024 / 1024;
console.log('\nTarget Achievement:');
console.log(` WebP avg < 200KB: ${avgWebpSize < 200 ? '✓ PASS' : '✗ FAIL'} (${avgWebpSize.toFixed(2)} KB)`);
console.log(` AVIF avg < 150KB: ${avgAvifSize < 150 ? '✓ PASS' : '✗ FAIL'} (${avgAvifSize.toFixed(2)} KB)`);
console.log(` Total size < 2MB: ${totalSizeAfter < 2 ? '✓ PASS' : '✗ FAIL'} (${totalSizeAfter.toFixed(2)} MB)`);
}
optimizeImages()
.then(() => console.log('\nOptimization complete!'))
.catch(error => console.error('Optimization failed:', error));

View File

@ -2,12 +2,8 @@ import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
import { lazy, Suspense, useEffect } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { reportWebVitals, observePerformance } from "@/utils/reportWebVitals";
import { pageTransition } from "@/utils/animations";
import ExitIntentPopup from "@/components/ExitIntentPopup";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { lazy, Suspense } from "react";
// Eager load critical pages for better initial performance
import Index from "./pages/Index";
@ -30,101 +26,42 @@ const NetworkAttachedStorage = lazy(() => import("./pages/NetworkAttachedStorage
const queryClient = new QueryClient();
// Loading fallback component with animation
// Loading fallback component
const PageLoader = () => (
<motion.div
className="min-h-screen flex items-center justify-center bg-background-deep"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<div className="min-h-screen flex items-center justify-center bg-background-deep">
<div className="text-center">
<motion.div
className="w-16 h-16 border-4 border-neon/30 border-t-neon rounded-full mx-auto mb-4"
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
/>
<motion.p
className="text-foreground-muted"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
>
Loading...
</motion.p>
<div className="w-16 h-16 border-4 border-neon/30 border-t-neon rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-foreground-muted">Loading...</p>
</div>
</motion.div>
</div>
);
// Animated page wrapper
const AnimatedPage = ({ children }: { children: React.ReactNode }) => {
return (
<motion.div
initial="initial"
animate="animate"
exit="exit"
variants={{
initial: pageTransition.initial,
animate: pageTransition.animate,
exit: pageTransition.exit
}}
transition={pageTransition.transition}
>
{children}
</motion.div>
);
};
const AppContent = () => {
const location = useLocation();
useEffect(() => {
// Initialize Web Vitals monitoring
reportWebVitals();
observePerformance();
}, []);
// Scroll to top on route change
useEffect(() => {
window.scrollTo(0, 0);
}, [location.pathname]);
return (
<>
<ExitIntentPopup />
<AnimatePresence mode="wait">
<Suspense fallback={<PageLoader />}>
<Routes location={location} key={location.pathname}>
<Route path="/" element={<AnimatedPage><Index /></AnimatedPage>} />
<Route path="/services" element={<AnimatedPage><Services /></AnimatedPage>} />
<Route path="/about" element={<AnimatedPage><About /></AnimatedPage>} />
<Route path="/blog" element={<AnimatedPage><Blog /></AnimatedPage>} />
<Route path="/blog/:slug" element={<AnimatedPage><BlogPost /></AnimatedPage>} />
<Route path="/contact" element={<AnimatedPage><Contact /></AnimatedPage>} />
<Route path="/windows-11-transition" element={<AnimatedPage><Windows11Transition /></AnimatedPage>} />
<Route path="/vpn-setup" element={<AnimatedPage><VpnSetup /></AnimatedPage>} />
<Route path="/web-services" element={<AnimatedPage><WebServices /></AnimatedPage>} />
<Route path="/performance-upgrades" element={<AnimatedPage><PerformanceUpgrades /></AnimatedPage>} />
<Route path="/printer-scanner-installation" element={<AnimatedPage><PrinterScannerInstallation /></AnimatedPage>} />
<Route path="/desktop-hardware" element={<AnimatedPage><DesktopHardware /></AnimatedPage>} />
<Route path="/network-infrastructure" element={<AnimatedPage><NetworkInfrastructure /></AnimatedPage>} />
<Route path="/network-attached-storage" element={<AnimatedPage><NetworkAttachedStorage /></AnimatedPage>} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<AnimatedPage><NotFound /></AnimatedPage>} />
</Routes>
</Suspense>
</AnimatePresence>
</>
);
};
const App = () => (
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<Toaster />
<Sonner />
<BrowserRouter>
<AppContent />
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Index />} />
<Route path="/services" element={<Services />} />
<Route path="/about" element={<About />} />
<Route path="/blog" element={<Blog />} />
<Route path="/blog/:slug" element={<BlogPost />} />
<Route path="/contact" element={<Contact />} />
<Route path="/windows-11-transition" element={<Windows11Transition />} />
<Route path="/vpn-setup" element={<VpnSetup />} />
<Route path="/web-services" element={<WebServices />} />
<Route path="/performance-upgrades" element={<PerformanceUpgrades />} />
<Route path="/printer-scanner-installation" element={<PrinterScannerInstallation />} />
<Route path="/desktop-hardware" element={<DesktopHardware />} />
<Route path="/network-infrastructure" element={<NetworkInfrastructure />} />
<Route path="/network-attached-storage" element={<NetworkAttachedStorage />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</Suspense>
</BrowserRouter>
</TooltipProvider>
</QueryClientProvider>

View File

@ -1,134 +0,0 @@
import { useEffect, useState } from 'react';
import { X, Download } from 'lucide-react';
import { Link } from 'react-router-dom';
const ExitIntentPopup = () => {
const [isVisible, setIsVisible] = useState(false);
const [hasShown, setHasShown] = useState(false);
useEffect(() => {
// Check if popup was already shown in this session
const shown = sessionStorage.getItem('exitIntentShown');
if (shown) {
setHasShown(true);
return;
}
const handleMouseLeave = (e: MouseEvent) => {
// Only trigger if mouse is leaving from top of viewport
if (e.clientY <= 0 && !hasShown) {
setIsVisible(true);
setHasShown(true);
sessionStorage.setItem('exitIntentShown', 'true');
}
};
// Add delay before activating to avoid false triggers
const timer = setTimeout(() => {
document.addEventListener('mouseleave', handleMouseLeave);
}, 3000);
return () => {
clearTimeout(timer);
document.removeEventListener('mouseleave', handleMouseLeave);
};
}, [hasShown]);
const handleClose = () => {
setIsVisible(false);
};
if (!isVisible) return null;
return (
<>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 animate-in fade-in duration-300"
onClick={handleClose}
/>
{/* Popup */}
<div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50 animate-in zoom-in duration-300">
<div className="card-dark p-8 max-w-lg w-[90vw] relative">
{/* Close button */}
<button
onClick={handleClose}
className="absolute top-4 right-4 text-foreground-muted hover:text-foreground transition-colors"
aria-label="Close popup"
>
<X className="w-6 h-6" />
</button>
{/* Content */}
<div className="text-center">
<div className="w-16 h-16 bg-neon/20 rounded-full flex items-center justify-center mx-auto mb-4">
<Download className="w-8 h-8 text-neon" />
</div>
<h2 className="font-heading font-bold text-2xl sm:text-3xl text-foreground mb-4">
Wait! Don't leave empty-handed
</h2>
<p className="text-foreground-muted mb-6 text-lg">
Download our <span className="text-neon font-semibold">free Windows 11 Migration Checklist</span>
a $299 value guide to help you prepare for the upgrade.
</p>
{/* Key benefits */}
<ul className="text-left space-y-2 mb-6 text-sm">
<li className="flex items-start gap-2">
<svg className="w-5 h-5 text-green-400 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
<span>Hardware compatibility check (avoid costly mistakes)</span>
</li>
<li className="flex items-start gap-2">
<svg className="w-5 h-5 text-green-400 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
<span>Step-by-step migration timeline</span>
</li>
<li className="flex items-start gap-2">
<svg className="w-5 h-5 text-green-400 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
<span>Security & compliance considerations</span>
</li>
<li className="flex items-start gap-2">
<svg className="w-5 h-5 text-green-400 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
<span>Rollback plan (just in case)</span>
</li>
</ul>
{/* CTA */}
<Link
to="/contact?lead=windows11-checklist"
onClick={handleClose}
className="btn-neon w-full flex items-center justify-center space-x-2 mb-3"
>
<Download className="w-5 h-5" />
<span>Download Free Checklist</span>
</Link>
<p className="text-xs text-foreground-muted">
No spam. No credit card required. Instant access.
</p>
{/* Close link */}
<button
onClick={handleClose}
className="text-sm text-foreground-muted hover:text-foreground mt-4 underline"
>
No thanks, I'll figure it out myself
</button>
</div>
</div>
</div>
</>
);
};
export default ExitIntentPopup;

View File

@ -20,7 +20,7 @@ const Footer = () => {
];
return (
<footer role="contentinfo" className="bg-background-deep border-t border-border">
<footer className="bg-background-deep border-t border-border">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-12">
{/* Company info */}

View File

@ -1,8 +1,6 @@
import { useState, useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Menu, X } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { navVariants, staggerContainer, staggerItem, buttonHover, buttonTap } from '@/utils/animations';
const Navigation = () => {
const [isOpen, setIsOpen] = useState(false);
@ -29,173 +27,87 @@ const Navigation = () => {
const isActive = (path: string) => location.pathname === path;
return (
<>
{/* Skip link for accessibility */}
<a href="#main-content" className="skip-link">
Skip to main content
</a>
<motion.nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${isScrolled
? 'bg-background-deep/80 backdrop-blur-md border-b border-neon/30 shadow-[0_0_20px_rgba(0,0,0,0.5)]'
: 'bg-transparent border-b border-transparent'
}`}
initial="hidden"
animate="visible"
variants={navVariants}
role="navigation"
aria-label="Main navigation"
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-14 md:h-16">
{/* Logo with animation */}
<Link to="/" className="flex items-center space-x-3">
<motion.div
className="w-12 h-12 flex items-center justify-center overflow-visible"
whileHover={{ rotate: 360 }}
transition={{ duration: 0.6, ease: "easeInOut" }}
>
<img
src="/logo_bayarea.svg"
alt="Bay Area Affiliates Logo"
className="w-10 h-10 opacity-90"
/>
</motion.div>
<span className="font-heading font-bold text-lg text-white">
Bay Area Affiliates
</span>
</Link>
{/* Desktop Navigation with animations */}
<div className="hidden md:flex items-center space-x-8">
{navItems.map((item, index) => (
<motion.div
key={item.name}
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1, duration: 0.5 }}
>
<Link
to={item.path}
className={`font-medium transition-all duration-200 relative group px-2 py-1 ${isActive(item.path)
? 'text-neon'
: 'text-white hover:text-neon'
}`}
>
{item.name}
{/* Animated underline */}
<motion.span
className="absolute -bottom-1 left-0 h-0.5 bg-neon rounded-full"
initial={{ width: isActive(item.path) ? '100%' : 0 }}
whileHover={{ width: '100%', boxShadow: '0 0 8px rgba(51, 102, 255, 0.6)' }}
transition={{ duration: 0.3, ease: "easeOut" }}
/>
{/* Glow effect on hover */}
<motion.span
className="absolute inset-0 bg-neon/5 rounded-lg -z-10"
initial={{ opacity: 0, scale: 0.8 }}
whileHover={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.2 }}
/>
</Link>
</motion.div>
))}
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.5, duration: 0.5 }}
whileHover={buttonHover}
whileTap={buttonTap}
>
<Link
to="/contact"
className="btn-neon ml-4"
>
Get Started
</Link>
</motion.div>
<nav className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${isScrolled
? 'bg-white/5 backdrop-blur-2xl border-b border-white/20 shadow-2xl shadow-black/20'
: 'bg-transparent'
}`}>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<Link to="/" className="flex items-center space-x-3">
<div className="w-12 h-12 flex items-center justify-center overflow-visible">
<img
src="/logo_bayarea.svg"
alt="Bay Area Affiliates Logo"
className="w-10 h-10 opacity-90 hover:opacity-100 transition-opacity duration-300"
/>
</div>
<span className="font-heading font-bold text-lg text-white">
Bay Area Affiliates
</span>
</Link>
{/* Mobile menu button with animation */}
<motion.button
onClick={() => setIsOpen(!isOpen)}
className="md:hidden text-white hover:text-neon transition-colors"
aria-label="Toggle navigation menu"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
{/* Desktop Navigation */}
<div className="hidden md:flex items-center space-x-8">
{navItems.map((item) => (
<Link
key={item.name}
to={item.path}
className={`font-medium transition-colors duration-200 ${isActive(item.path)
? 'text-neon'
: 'text-white hover:text-neon'
}`}
>
{item.name}
</Link>
))}
<Link
to="/contact"
className="btn-neon ml-4"
>
<AnimatePresence mode="wait">
{isOpen ? (
<motion.div
key="close"
initial={{ rotate: -90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: 90, opacity: 0 }}
transition={{ duration: 0.2 }}
>
<X size={24} />
</motion.div>
) : (
<motion.div
key="menu"
initial={{ rotate: 90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: -90, opacity: 0 }}
transition={{ duration: 0.2 }}
>
<Menu size={24} />
</motion.div>
)}
</AnimatePresence>
</motion.button>
Get Started
</Link>
</div>
{/* Mobile Navigation with smooth animations */}
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
className="md:hidden bg-white/5 backdrop-blur-2xl border-t border-white/20 overflow-hidden"
>
<motion.div
className="px-2 pt-2 pb-3 space-y-1"
variants={staggerContainer}
initial="hidden"
animate="visible"
>
{navItems.map((item) => (
<motion.div key={item.name} variants={staggerItem}>
<Link
to={item.path}
onClick={() => setIsOpen(false)}
className={`block px-3 py-2 rounded-md text-base font-medium transition-colors ${isActive(item.path)
? 'text-neon bg-neon/10'
: 'text-white hover:text-neon hover:bg-neon/5'
}`}
>
{item.name}
</Link>
</motion.div>
))}
<motion.div variants={staggerItem}>
<Link
to="/contact"
onClick={() => setIsOpen(false)}
className="block w-full text-center btn-neon mt-4"
>
Get Started
</Link>
</motion.div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
{/* Mobile menu button */}
<button
onClick={() => setIsOpen(!isOpen)}
className="md:hidden text-white hover:text-neon transition-colors"
aria-label="Toggle navigation menu"
>
{isOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
</motion.nav>
</>
{/* Mobile Navigation */}
{isOpen && (
<div className="md:hidden bg-white/5 backdrop-blur-2xl border-t border-white/20">
<div className="px-2 pt-2 pb-3 space-y-1">
{navItems.map((item) => (
<Link
key={item.name}
to={item.path}
onClick={() => setIsOpen(false)}
className={`block px-3 py-2 rounded-md text-base font-medium transition-colors ${isActive(item.path)
? 'text-neon bg-neon/10'
: 'text-white hover:text-neon hover:bg-neon/5'
}`}
>
{item.name}
</Link>
))}
<Link
to="/contact"
onClick={() => setIsOpen(false)}
className="block w-full text-center btn-neon mt-4"
>
Get Started
</Link>
</div>
</div>
)}
</div>
</nav>
);
};

View File

@ -1,50 +1,47 @@
import { ReactNode } from 'react';
import { motion, useInView } from 'framer-motion';
import React, { useRef } from 'react';
import { ReactNode, useLayoutEffect, useRef } from 'react';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
interface ScrollRevealProps {
children: React.ReactNode;
width?: 'fit-content' | '100%';
gsap.registerPlugin(ScrollTrigger);
type ScrollRevealProps = {
children: ReactNode;
delay?: number;
duration?: number;
direction?: 'up' | 'down' | 'left' | 'right';
className?: string;
}
};
const ScrollReveal: React.FC<ScrollRevealProps> = ({
children,
width = 'fit-content',
delay = 0,
duration = 0.5,
direction = 'up',
className = ""
}) => {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: "-50px 0px" }); // Trigger slightly before element is fully in view
const ScrollReveal = ({ children, delay = 0, className = '' }: ScrollRevealProps) => {
const elementRef = useRef<HTMLDivElement>(null);
const getDirectionOffset = () => {
switch (direction) {
case 'up': return { y: 75 };
case 'down': return { y: -75 };
case 'left': return { x: 75 };
case 'right': return { x: -75 };
default: return { y: 75 };
}
};
useLayoutEffect(() => {
const element = elementRef.current;
if (!element) return;
const ctx = gsap.context(() => {
gsap.fromTo(
element,
{ autoAlpha: 0, y: 32 },
{
autoAlpha: 1,
y: 0,
duration: 0.8,
ease: 'power3.out',
delay: delay / 1000,
scrollTrigger: {
trigger: element,
start: 'top 85%',
once: true,
},
}
);
}, element);
return () => ctx.revert();
}, [delay]);
return (
<div ref={ref} style={{ position: 'relative', width, overflow: 'hidden' }} className={className}>
<motion.div
variants={{
hidden: { opacity: 0, ...getDirectionOffset() },
visible: { opacity: 1, x: 0, y: 0 },
}}
initial="hidden"
animate={isInView ? "visible" : "hidden"}
transition={{ duration, delay, ease: "easeOut" }}
>
{children}
</motion.div>
<div ref={elementRef} className={className}>
{children}
</div>
);
};

View File

@ -1,244 +0,0 @@
import { motion } from 'framer-motion';
import { useMemo } from 'react';
const BackgroundAnimations = () => {
// Particles with better visibility (increased opacity and size)
const particles = useMemo(() => {
return Array.from({ length: 18 }, (_, i) => ({
id: i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 4 + 2, // Larger particles (2-6px instead of 1-3px)
duration: Math.random() * 10 + 6,
delay: Math.random() * 4,
opacity: Math.random() * 0.4 + 0.5, // Much higher opacity (0.5-0.9)
}));
}, []);
// Circuit nodes for tech effect
const circuitNodes = useMemo(() => {
return Array.from({ length: 8 }, (_, i) => ({
id: i,
x: Math.random() * 80 + 10,
y: Math.random() * 80 + 10,
pulseDelay: Math.random() * 2,
}));
}, []);
// Connection lines between nodes
const connectionLines = useMemo(() => {
const lines = [];
for (let i = 0; i < circuitNodes.length - 1; i++) {
if (i + 1 < circuitNodes.length) {
lines.push({
id: i,
x1: circuitNodes[i].x,
y1: circuitNodes[i].y,
x2: circuitNodes[i + 1].x,
y2: circuitNodes[i + 1].y,
});
}
}
return lines;
}, [circuitNodes]);
return (
<div className="absolute inset-0 overflow-hidden pointer-events-none" style={{ willChange: 'transform' }}>
{/* Animated Grid Background */}
<motion.div
className="absolute inset-0"
style={{
backgroundImage: `
linear-gradient(to right, rgba(51, 102, 255, 0.08) 1px, transparent 1px),
linear-gradient(to bottom, rgba(51, 102, 255, 0.08) 1px, transparent 1px)
`,
backgroundSize: '60px 60px',
willChange: 'transform',
}}
animate={{
opacity: [0.3, 0.5, 0.3],
}}
transition={{
duration: 4,
repeat: Infinity,
ease: "easeInOut",
}}
/>
{/* SVG Container for Lines and Nodes */}
<svg className="absolute inset-0 w-full h-full" style={{ willChange: 'transform' }}>
{/* Animated Connection Lines */}
{connectionLines.map((line) => (
<motion.line
key={line.id}
x1={`${line.x1}%`}
y1={`${line.y1}%`}
x2={`${line.x2}%`}
y2={`${line.y2}%`}
stroke="rgba(51, 102, 255, 0.4)"
strokeWidth="1.5"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: [0.3, 0.7, 0.3] }}
transition={{
pathLength: { duration: 2, delay: line.id * 0.3 },
opacity: { duration: 3, repeat: Infinity, ease: "easeInOut" },
}}
/>
))}
{/* Vertical Data Streams */}
{[...Array(6)].map((_, i) => (
<motion.line
key={`vertical-${i}`}
x1={`${10 + i * 18}%`}
y1="0%"
x2={`${10 + i * 18}%`}
y2="100%"
stroke="rgba(51, 102, 255, 0.3)"
strokeWidth="1.5"
strokeDasharray="8 16"
initial={{ strokeDashoffset: 0 }}
animate={{ strokeDashoffset: 100 }}
transition={{
duration: 3,
repeat: Infinity,
ease: "linear",
delay: i * 0.4,
}}
/>
))}
{/* Circuit Nodes with pulse ring */}
{circuitNodes.map((node) => (
<g key={node.id}>
{/* Pulse ring */}
<motion.circle
cx={`${node.x}%`}
cy={`${node.y}%`}
r="6"
fill="none"
stroke="#3366ff"
strokeWidth="1"
animate={{
r: [6, 12, 6],
opacity: [0.8, 0, 0.8],
}}
transition={{
duration: 2.5,
repeat: Infinity,
delay: node.pulseDelay,
ease: "easeOut",
}}
/>
{/* Core node */}
<motion.circle
cx={`${node.x}%`}
cy={`${node.y}%`}
r="4"
fill="#3366ff"
animate={{
opacity: [0.7, 1, 0.7],
r: [3, 4.5, 3],
}}
transition={{
duration: 2,
repeat: Infinity,
delay: node.pulseDelay,
ease: "easeInOut",
}}
/>
</g>
))}
</svg>
{/* Floating Data Particles - highly visible */}
{particles.map((particle) => (
<motion.div
key={particle.id}
className="absolute rounded-full bg-neon"
style={{
width: `${particle.size}px`,
height: `${particle.size}px`,
left: `${particle.x}%`,
top: `${particle.y}%`,
boxShadow: '0 0 12px rgba(51, 102, 255, 0.9), 0 0 24px rgba(51, 102, 255, 0.5)',
willChange: 'transform, opacity',
}}
animate={{
y: [0, -500],
opacity: [0, particle.opacity, particle.opacity, 0],
}}
transition={{
duration: particle.duration,
repeat: Infinity,
delay: particle.delay,
ease: "linear",
}}
/>
))}
{/* Scanline Effect */}
<motion.div
className="absolute inset-0 pointer-events-none"
style={{
background: `repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(51, 102, 255, 0.04) 2px,
rgba(51, 102, 255, 0.04) 4px
)`,
}}
animate={{
opacity: [0.3, 0.5, 0.3],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut",
}}
/>
{/* Radial Glows */}
{[
{ x: 20, y: 30, size: 300 },
{ x: 80, y: 70, size: 350 },
{ x: 50, y: 50, size: 400 },
].map((pos, i) => (
<motion.div
key={`glow-${i}`}
className="absolute rounded-full pointer-events-none"
style={{
left: `${pos.x}%`,
top: `${pos.y}%`,
width: `${pos.size}px`,
height: `${pos.size}px`,
background: 'radial-gradient(circle, rgba(51, 102, 255, 0.25) 0%, transparent 70%)',
transform: 'translate(-50%, -50%)',
willChange: 'transform',
}}
animate={{
scale: [1, 1.3, 1],
opacity: [0.5, 0.8, 0.5],
}}
transition={{
duration: 5,
repeat: Infinity,
delay: i * 1.5,
ease: "easeInOut",
}}
/>
))}
{/* Corner Accent Glows */}
<div className="absolute top-0 left-0 w-96 h-96 bg-gradient-to-br from-neon/20 to-transparent rounded-full opacity-60 pointer-events-none" />
<div className="absolute bottom-0 right-0 w-[500px] h-[500px] bg-gradient-to-tl from-neon/20 to-transparent rounded-full opacity-60 pointer-events-none" />
</div>
);
};
export default BackgroundAnimations;

View File

@ -1,8 +1,6 @@
import { ArrowRight, Clock, DollarSign, Phone } from 'lucide-react';
import { Link } from 'react-router-dom';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { fadeInUp, staggerContainer, staggerItem, buttonHover, buttonTap } from '@/utils/animations';
import ScrollReveal from '../ScrollReveal';
const CTASection = () => {
const faqs = [
@ -23,13 +21,6 @@ const CTASection = () => {
}
];
const headerRef = useRef(null);
const ctaRef = useRef(null);
const faqRef = useRef(null);
const isHeaderInView = useInView(headerRef, { once: true, margin: "-100px" });
const isCtaInView = useInView(ctaRef, { once: true, margin: "-100px" });
const isFaqInView = useInView(faqRef, { once: true, margin: "-100px" });
return (
<section className="py-24 bg-background-deep relative overflow-hidden">
{/* Background decoration */}
@ -38,141 +29,88 @@ const CTASection = () => {
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto text-center">
<motion.div
ref={headerRef}
initial="hidden"
animate={isHeaderInView ? "visible" : "hidden"}
variants={fadeInUp}
>
<ScrollReveal>
<h2 className="font-heading font-bold text-4xl sm:text-5xl lg:text-6xl text-foreground mb-6">
Ready for <motion.span
className="text-neon text-glow"
animate={{
textShadow: [
"0 0 20px rgba(51, 102, 255, 0.5)",
"0 0 40px rgba(51, 102, 255, 0.8)",
"0 0 20px rgba(51, 102, 255, 0.5)"
]
}}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
>
reliable IT?
</motion.span>
Ready for <span className="text-neon text-glow">reliable IT?</span>
</h2>
<p className="text-xl text-foreground-muted mb-12 leading-relaxed">
Join 150+ Coastal Bend businesses that trust us with their technology.
Join 150+ Coastal Bend businesses that trust us with their technology.
Get started with a free 20-minute assessment.
</p>
</motion.div>
</ScrollReveal>
{/* Primary CTAs */}
<motion.div
ref={ctaRef}
initial={{ opacity: 0, y: 30 }}
animate={isCtaInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
transition={{ delay: 0.2, duration: 0.6 }}
className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-16"
>
<motion.div whileHover={buttonHover} whileTap={buttonTap}>
<ScrollReveal delay={200}>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-16">
<Link
to="/contact"
className="btn-neon group flex items-center space-x-2 text-lg px-8 py-4"
>
<span>Book a 20-minute assessment</span>
<motion.div
animate={{ x: [0, 3, 0] }}
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
>
<ArrowRight className="w-5 h-5" />
</motion.div>
<ArrowRight className="w-5 h-5 transition-transform group-hover:translate-x-1" />
</Link>
</motion.div>
<motion.div whileHover={buttonHover} whileTap={buttonTap}>
<Link
to="/contact"
className="btn-ghost group flex items-center space-x-2 text-lg px-8 py-4"
>
<span>Send a message</span>
</Link>
</motion.div>
</motion.div>
</div>
</ScrollReveal>
{/* Micro FAQ */}
<motion.div
ref={faqRef}
initial="hidden"
animate={isFaqInView ? "visible" : "hidden"}
variants={staggerContainer}
className="grid grid-cols-1 md:grid-cols-3 gap-8"
>
{faqs.map((faq, index) => {
const Icon = faq.icon;
return (
<motion.div
key={faq.question}
variants={staggerItem}
className="text-left"
>
<motion.div
className="flex items-start space-x-3"
whileHover={{ x: 5 }}
transition={{ duration: 0.2 }}
>
<motion.div
className="w-8 h-8 bg-neon/20 rounded-lg flex items-center justify-center flex-shrink-0 mt-1"
whileHover={{ rotate: 360, scale: 1.1 }}
transition={{ duration: 0.5 }}
>
<Icon className="w-4 h-4 text-neon" />
</motion.div>
<div>
<h3 className="font-semibold text-foreground mb-2">
{faq.question}
</h3>
<p className="text-sm text-foreground-muted">
{faq.answer}
</p>
<ScrollReveal delay={400}>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{faqs.map((faq, index) => {
const Icon = faq.icon;
return (
<div key={faq.question} className="text-left">
<div className="flex items-start space-x-3">
<div className="w-8 h-8 bg-neon/20 rounded-lg flex items-center justify-center flex-shrink-0 mt-1">
<Icon className="w-4 h-4 text-neon" />
</div>
<div>
<h3 className="font-semibold text-foreground mb-2">
{faq.question}
</h3>
<p className="text-sm text-foreground-muted">
{faq.answer}
</p>
</div>
</div>
</motion.div>
</motion.div>
);
})}
</motion.div>
</div>
);
})}
</div>
</ScrollReveal>
{/* Contact info */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isFaqInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
transition={{ delay: 0.6, duration: 0.6 }}
className="mt-16 pt-8 border-t border-border/30"
>
<p className="text-sm text-foreground-muted mb-4">
Ready to talk? We're here to help.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center text-sm">
<motion.a
href="tel:+1-361-555-0123"
className="text-neon hover:text-neon/80 transition-colors flex items-center"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Phone className="w-4 h-4 mr-2" />
(361) 555-0123
</motion.a>
<span className="hidden sm:block text-border">|</span>
<motion.a
href="mailto:info@bayareaaffiliates.com"
className="text-neon hover:text-neon/80 transition-colors"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
info@bayareaaffiliates.com
</motion.a>
<ScrollReveal delay={600}>
<div className="mt-16 pt-8 border-t border-border/30">
<p className="text-sm text-foreground-muted mb-4">
Ready to talk? We're here to help.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center text-sm">
<a
href="tel:+1-361-555-0123"
className="text-neon hover:text-neon/80 transition-colors flex items-center"
>
<Phone className="w-4 h-4 mr-2" />
(361) 555-0123
</a>
<span className="hidden sm:block text-border">|</span>
<a
href="mailto:info@bayareaaffiliates.com"
className="text-neon hover:text-neon/80 transition-colors"
>
info@bayareaaffiliates.com
</a>
</div>
</div>
</motion.div>
</ScrollReveal>
</div>
</div>
</section>

View File

@ -1,165 +1,87 @@
import { ArrowRight, Play } from 'lucide-react';
import { Link } from 'react-router-dom';
import { motion, useScroll, useTransform } from 'framer-motion';
import { heroVariants, heroItemVariants, buttonHover, buttonTap, easing } from '@/utils/animations';
import BackgroundAnimations from './BackgroundAnimations';
import { useEffect, useRef } from 'react';
import heroNetwork from '@/assets/hero-network.jpg';
const HeroSection = () => {
const { scrollY } = useScroll();
const imageRef = useRef<HTMLImageElement>(null);
// Smooth parallax effect with Framer Motion
const y = useTransform(scrollY, [0, 500], [0, 150]);
const opacity = useTransform(scrollY, [0, 300], [1, 0]);
useEffect(() => {
const handleScroll = () => {
if (imageRef.current) {
const scrolled = window.pageYOffset;
const parallax = scrolled * 0.5;
imageRef.current.style.transform = `translateY(${parallax}px) scale(1.1)`;
}
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<section className="section-pin">
<div className="relative h-full flex items-center justify-center overflow-hidden">
{/* Background image with smooth parallax */}
<motion.div
className="absolute inset-0 overflow-hidden"
style={{ y }}
>
<picture>
<source type="image/avif" srcSet="/serverroom.avif" />
<source type="image/webp" srcSet="/serverroom.webp" />
<motion.img
src="/serverroom.png"
alt="Modern IT infrastructure with network connections and server equipment"
className="w-full h-[110%] object-cover"
initial={{ scale: 1.1, opacity: 0 }}
animate={{ scale: 1.1, opacity: 1 }}
transition={{ duration: 1.2, ease: easing.elegant }}
loading="eager"
fetchPriority="high"
/>
</picture>
{/* Background image with parallax */}
<div className="absolute inset-0 overflow-hidden">
<img
ref={imageRef}
src="/serverroom.png"
alt="Modern IT infrastructure with network connections"
className="w-full h-[110%] object-cover will-change-transform"
style={{ transform: 'translateY(0px) scale(1.1)' }}
/>
{/* Dark overlay */}
<div className="absolute inset-0 bg-black/35"></div>
</div>
{/* Animated background effects */}
<BackgroundAnimations />
{/* Darker overlay for better text contrast */}
<div className="absolute inset-0 bg-gradient-to-b from-black/60 via-black/50 to-black/60"></div>
</motion.div>
{/* Main content with staggered animations */}
<motion.div
className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center"
variants={heroVariants}
initial="hidden"
animate="visible"
style={{ opacity }}
>
{/* Main content */}
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<div className="max-w-4xl mx-auto">
{/* Badge */}
<motion.div
variants={heroItemVariants}
className="inline-flex items-center px-4 py-2 rounded-full bg-neon/20 border border-neon/40 text-neon text-sm font-medium mb-8 drop-shadow-[0_0_10px_rgba(51,102,255,0.5)]"
>
<motion.span
className="w-2 h-2 bg-neon rounded-full mr-2"
animate={{
scale: [1, 1.2, 1],
opacity: [1, 0.7, 1],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut",
}}
/>
<div className="inline-flex items-center px-4 py-2 rounded-full bg-neon/20 border border-neon/40 text-neon text-sm font-medium mb-8 drop-shadow-[0_0_10px_rgba(51,102,255,0.5)]">
<span className="w-2 h-2 bg-neon rounded-full mr-2 animate-glow-pulse"></span>
Serving the Coastal Bend since 2010
</motion.div>
</div>
{/* Main headline */}
<motion.h1
variants={heroItemVariants}
className="font-heading font-bold text-5xl sm:text-6xl lg:text-7xl text-white mb-6 text-balance drop-shadow-[0_0_20px_rgba(255,255,255,0.3)]"
>
<h1 className="font-heading font-bold text-5xl sm:text-6xl lg:text-7xl text-white mb-6 text-balance drop-shadow-[0_0_20px_rgba(255,255,255,0.3)]">
Modern IT that keeps your{' '}
<motion.span
className="text-neon text-glow drop-shadow-[0_0_30px_rgba(51,102,255,0.8)]"
animate={{
textShadow: [
'0 0 20px rgba(51,102,255,0.5)',
'0 0 30px rgba(51,102,255,0.8)',
'0 0 20px rgba(51,102,255,0.5)',
],
}}
transition={{
duration: 3,
repeat: Infinity,
ease: "easeInOut",
}}
>
business moving
</motion.span>
</motion.h1>
<span className="text-neon text-glow drop-shadow-[0_0_30px_rgba(51,102,255,0.8)]">business moving</span>
</h1>
{/* Subheadline */}
<motion.p
variants={heroItemVariants}
className="text-xl sm:text-2xl text-white/95 mb-12 max-w-3xl mx-auto leading-relaxed drop-shadow-[0_0_15px_rgba(255,255,255,0.2)]"
>
<p className="text-xl sm:text-2xl text-white/95 mb-12 max-w-3xl mx-auto leading-relaxed drop-shadow-[0_0_15px_rgba(255,255,255,0.2)]">
From fast devices to secure remote access and resilient networks we design,
run and protect your tech so you can focus on growth.
</motion.p>
</p>
{/* CTA buttons with hover animations */}
<motion.div
variants={heroItemVariants}
className="flex flex-col sm:flex-row gap-4 justify-center items-center"
>
<motion.div
whileHover={buttonHover}
whileTap={buttonTap}
{/* CTA buttons */}
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<Link
to="/contact"
className="btn-neon group flex items-center space-x-2"
>
<Link
to="/contact"
className="btn-neon group flex items-center space-x-2"
>
<span>Get in touch</span>
<motion.div
animate={{ x: [0, 3, 0] }}
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
>
<ArrowRight className="w-5 h-5" />
</motion.div>
</Link>
</motion.div>
<span>Get in touch</span>
<ArrowRight className="w-5 h-5 transition-transform group-hover:translate-x-1" />
</Link>
<motion.div
whileHover={buttonHover}
whileTap={buttonTap}
>
<Link
to="/services"
className="btn-ghost group flex items-center space-x-2"
>
<Play className="w-5 h-5" />
<span>See our services</span>
</Link>
</motion.div>
</motion.div>
<button className="btn-ghost group flex items-center space-x-2">
<Play className="w-5 h-5 transition-transform group-hover:scale-110" />
<span>See our system</span>
</button>
</div>
</div>
</motion.div>
</div>
{/* Animated scroll indicator */}
<motion.div
className="absolute bottom-8 left-1/2 transform -translate-x-1/2"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 1.5, duration: 0.8 }}
>
{/* Scroll indicator */}
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2">
<div className="w-6 h-10 border-2 border-neon/60 rounded-full flex justify-center drop-shadow-[0_0_10px_rgba(51,102,255,0.3)]">
<motion.div
className="w-1 h-3 bg-neon rounded-full mt-2"
animate={{ y: [0, 12, 0] }}
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
/>
<div className="w-1 h-3 bg-neon rounded-full mt-2 animate-bounce"></div>
</div>
</motion.div>
</div>
</div>
</section>
);

View File

@ -1,20 +1,10 @@
import { useEffect, useRef, useState } from 'react';
import { Search, Shield, Cog, Zap } from 'lucide-react';
import { motion, useInView, useScroll, useTransform } from 'framer-motion';
import { fadeInUp, scaleIn } from '@/utils/animations';
import ScrollReveal from '../ScrollReveal';
const ProcessTimeline = () => {
const [activeStep, setActiveStep] = useState(0);
const sectionRef = useRef<HTMLDivElement>(null);
const headerRef = useRef(null);
const isHeaderInView = useInView(headerRef, { once: true, margin: "-100px" });
// Smooth scroll-based timeline progress
const { scrollYProgress } = useScroll({
target: sectionRef,
offset: ["start end", "end start"]
});
const timelineProgress = useTransform(scrollYProgress, [0.2, 0.8], [0, 100]);
const steps = [
{
@ -73,12 +63,7 @@ const ProcessTimeline = () => {
</div>
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
ref={headerRef}
initial="hidden"
animate={isHeaderInView ? "visible" : "hidden"}
variants={fadeInUp}
>
<ScrollReveal>
<div className="text-center mb-20">
<h2 className="font-heading font-bold text-4xl sm:text-5xl text-foreground mb-6">
How we <span className="text-neon">transform</span> your IT
@ -87,14 +72,14 @@ const ProcessTimeline = () => {
Our proven four-phase methodology ensures systematic improvement and lasting results.
</p>
</div>
</motion.div>
</ScrollReveal>
<div className="relative">
{/* Timeline line */}
<div className="absolute left-8 lg:left-1/2 lg:transform lg:-translate-x-1/2 top-0 bottom-0 w-px bg-border">
<motion.div
className="absolute top-0 left-0 w-full bg-neon"
style={{ height: useTransform(timelineProgress, (value) => `${value}%`) }}
<div
className="absolute top-0 left-0 w-full bg-neon transition-all duration-500 ease-out"
style={{ height: `${(activeStep + 1) * 25}%` }}
/>
</div>
@ -104,35 +89,17 @@ const ProcessTimeline = () => {
const Icon = step.icon;
const isActive = index <= activeStep;
const isEven = index % 2 === 0;
const stepRef = useRef(null);
const isInView = useInView(stepRef, { once: true, margin: "-150px" });
return (
<motion.div
key={step.title}
ref={stepRef}
initial="hidden"
animate={isInView ? "visible" : "hidden"}
variants={fadeInUp}
transition={{ delay: index * 0.1 }}
>
<ScrollReveal key={step.title} delay={index * 100}>
<div className={`relative flex flex-col lg:flex-row items-center ${
isEven ? '' : 'lg:flex-row-reverse'
}`}>
{/* Step content */}
<motion.div
className={`flex-1 ${isEven ? 'lg:pr-16' : 'lg:pl-16'} ${
isEven ? 'lg:text-right' : 'lg:text-left'
} text-center lg:text-left`}
initial={{ opacity: 0, x: isEven ? -40 : 40 }}
animate={isInView ? { opacity: 1, x: 0 } : { opacity: 0, x: isEven ? -40 : 40 }}
transition={{ delay: index * 0.1 + 0.2, duration: 0.6 }}
>
<motion.div
className="card-dark p-8 max-w-lg mx-auto lg:mx-0"
whileHover={{ y: -5, boxShadow: "0 0 30px rgba(51, 102, 255, 0.3)" }}
transition={{ duration: 0.3 }}
>
<div className={`flex-1 ${isEven ? 'lg:pr-16' : 'lg:pl-16'} ${
isEven ? 'lg:text-right' : 'lg:text-left'
} text-center lg:text-left`}>
<div className="card-dark p-8 max-w-lg mx-auto lg:mx-0">
<div className="mb-4">
<span className="text-sm font-medium text-neon uppercase tracking-wider">
Step {index + 1}
@ -147,34 +114,24 @@ const ProcessTimeline = () => {
<p className="text-sm text-foreground-muted">
{step.details}
</p>
</motion.div>
</motion.div>
</div>
</div>
{/* Timeline dot */}
<div className="relative z-10 my-8 lg:my-0">
<motion.div
className={`w-16 h-16 rounded-full border-4 flex items-center justify-center ${
isActive
? 'border-neon bg-neon text-neon-foreground shadow-neon'
: 'border-border bg-background text-foreground-muted'
}`}
initial={{ scale: 0, rotate: -180 }}
animate={isInView ? { scale: 1, rotate: 0 } : { scale: 0, rotate: -180 }}
transition={{
delay: index * 0.1 + 0.3,
duration: 0.6,
type: "spring",
stiffness: 200
}}
>
<div className={`w-16 h-16 rounded-full border-4 flex items-center justify-center transition-all duration-500 ${
isActive
? 'border-neon bg-neon text-neon-foreground shadow-neon'
: 'border-border bg-background text-foreground-muted'
}`}>
<Icon className="w-6 h-6" />
</motion.div>
</div>
</div>
{/* Spacer for layout */}
<div className="flex-1 hidden lg:block"></div>
</div>
</motion.div>
</ScrollReveal>
);
})}
</div>

View File

@ -1,8 +1,6 @@
import { MapPin, Star, Users } from 'lucide-react';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import ScrollReveal from '../ScrollReveal';
import CountUpNumber from '../CountUpNumber';
import { fadeInUp, scaleIn, staggerContainer, staggerItem } from '@/utils/animations';
const ProofSection = () => {
const stats = [
@ -19,13 +17,6 @@ const ProofSection = () => {
company: "Coastal Medical Group"
};
const headerRef = useRef(null);
const statsRef = useRef(null);
const testimonialRef = useRef(null);
const isHeaderInView = useInView(headerRef, { once: true, margin: "-100px" });
const isStatsInView = useInView(statsRef, { once: true, margin: "-100px" });
const isTestimonialInView = useInView(testimonialRef, { once: true, margin: "-100px" });
return (
<section className="py-24 bg-background relative overflow-hidden">
{/* Background decoration */}
@ -33,27 +24,12 @@ const ProofSection = () => {
<div className="absolute top-0 left-1/2 transform -translate-x-1/2 w-96 h-96 bg-neon/5 rounded-full blur-3xl"></div>
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
ref={headerRef}
initial="hidden"
animate={isHeaderInView ? "visible" : "hidden"}
variants={fadeInUp}
>
<ScrollReveal>
<div className="text-center mb-16">
<motion.div
className="inline-flex items-center px-4 py-2 rounded-full bg-neon/10 border border-neon/30 text-neon text-sm font-medium mb-8"
animate={{
boxShadow: [
"0 0 20px rgba(51, 102, 255, 0.2)",
"0 0 30px rgba(51, 102, 255, 0.4)",
"0 0 20px rgba(51, 102, 255, 0.2)"
]
}}
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
>
<div className="inline-flex items-center px-4 py-2 rounded-full bg-neon/10 border border-neon/30 text-neon text-sm font-medium mb-8">
<MapPin className="w-4 h-4 mr-2" />
Proudly serving the Coastal Bend
</motion.div>
</div>
<h2 className="font-heading font-bold text-4xl sm:text-5xl text-foreground mb-6">
Local expertise for{' '}
@ -66,92 +42,48 @@ const ProofSection = () => {
tailored solutions that work in the real world.
</p>
</div>
</motion.div>
</ScrollReveal>
{/* Stats */}
<motion.div
ref={statsRef}
initial="hidden"
animate={isStatsInView ? "visible" : "hidden"}
variants={staggerContainer}
className="grid grid-cols-2 lg:grid-cols-4 gap-8 mb-20"
>
{stats.map((stat, index) => (
<motion.div
key={stat.label}
variants={staggerItem}
className="text-center"
>
<div className="font-heading font-bold text-4xl lg:text-5xl text-neon mb-2">
<CountUpNumber
value={stat.value}
duration={2000 + index * 200}
className="inline-block"
/>
<ScrollReveal delay={200}>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 mb-20">
{stats.map((stat, index) => (
<div key={stat.label} className="text-center">
<div className="font-heading font-bold text-4xl lg:text-5xl text-neon mb-2">
<CountUpNumber
value={stat.value}
duration={2000 + index * 200}
className="inline-block"
/>
</div>
<div className="text-foreground-muted text-sm lg:text-base">
{stat.label}
</div>
</div>
<motion.div
className="text-foreground-muted text-sm lg:text-base"
initial={{ opacity: 0, y: 10 }}
animate={isStatsInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ delay: index * 0.1 + 0.3, duration: 0.5 }}
>
{stat.label}
</motion.div>
</motion.div>
))}
</motion.div>
))}
</div>
</ScrollReveal>
{/* Testimonial */}
<motion.div
ref={testimonialRef}
initial="hidden"
animate={isTestimonialInView ? "visible" : "hidden"}
variants={scaleIn}
>
<motion.div
className="card-dark p-8 lg:p-12 max-w-4xl mx-auto"
whileHover={{ y: -5, boxShadow: "0 0 40px rgba(51, 102, 255, 0.3)" }}
transition={{ duration: 0.3 }}
>
<ScrollReveal delay={400}>
<div className="card-dark p-8 lg:p-12 max-w-4xl mx-auto">
<div className="flex flex-col lg:flex-row items-start gap-8">
{/* Quote */}
<div className="flex-1">
<motion.div
className="flex items-center mb-6"
initial="hidden"
animate={isTestimonialInView ? "visible" : "hidden"}
variants={staggerContainer}
>
<div className="flex items-center mb-6">
{[...Array(5)].map((_, i) => (
<motion.div
key={i}
variants={staggerItem}
whileHover={{ scale: 1.2, rotate: 360 }}
transition={{ duration: 0.4 }}
>
<Star className="w-5 h-5 text-neon fill-current" />
</motion.div>
<Star key={i} className="w-5 h-5 text-neon fill-current" />
))}
</motion.div>
</div>
<blockquote className="text-lg lg:text-xl text-foreground leading-relaxed mb-6">
"{testimonial.quote}"
</blockquote>
<div className="flex items-center">
<motion.div
className="w-12 h-12 bg-neon/20 rounded-full flex items-center justify-center mr-4"
animate={{
boxShadow: [
"0 0 10px rgba(51, 102, 255, 0.3)",
"0 0 20px rgba(51, 102, 255, 0.5)",
"0 0 10px rgba(51, 102, 255, 0.3)"
]
}}
transition={{ duration: 2, repeat: Infinity }}
>
<div className="w-12 h-12 bg-neon/20 rounded-full flex items-center justify-center mr-4">
<Users className="w-6 h-6 text-neon" />
</motion.div>
</div>
<div>
<div className="font-semibold text-foreground">{testimonial.author}</div>
<div className="text-foreground-muted text-sm">
@ -161,43 +93,29 @@ const ProofSection = () => {
</div>
</div>
</div>
</motion.div>
</motion.div>
</div>
</ScrollReveal>
{/* Service area */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isTestimonialInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
transition={{ delay: 0.4, duration: 0.6 }}
className="mt-16 text-center"
>
<h3 className="font-heading font-semibold text-xl text-foreground mb-6">
Serving businesses throughout the Coastal Bend
</h3>
<ScrollReveal delay={600}>
<div className="mt-16 text-center">
<h3 className="font-heading font-semibold text-xl text-foreground mb-6">
Serving businesses throughout the Coastal Bend
</h3>
<motion.div
className="flex flex-wrap justify-center items-center gap-6 text-foreground-muted"
initial="hidden"
animate={isTestimonialInView ? "visible" : "hidden"}
variants={staggerContainer}
>
{[
'Corpus Christi', 'Portland', 'Ingleside', 'Aransas Pass',
'Rockport', 'Fulton', 'Sinton', 'Mathis'
].map((city) => (
<motion.span
key={city}
className="flex items-center text-sm"
variants={staggerItem}
whileHover={{ scale: 1.1, color: "rgba(51, 102, 255, 1)" }}
transition={{ duration: 0.2 }}
>
<MapPin className="w-3 h-3 mr-1 text-neon" />
{city}
</motion.span>
))}
</motion.div>
</motion.div>
<div className="flex flex-wrap justify-center items-center gap-6 text-foreground-muted">
{[
'Corpus Christi', 'Portland', 'Ingleside', 'Aransas Pass',
'Rockport', 'Fulton', 'Sinton', 'Mathis'
].map((city) => (
<span key={city} className="flex items-center text-sm">
<MapPin className="w-3 h-3 mr-1 text-neon" />
{city}
</span>
))}
</div>
</div>
</ScrollReveal>
</div>
</section>
);

View File

@ -1,8 +1,6 @@
import { Monitor, Wifi, Cloud, Shield, Database, Settings } from 'lucide-react';
import { Link } from 'react-router-dom';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { fadeInUp, staggerContainer, staggerItem, cardHover, glowHover } from '@/utils/animations';
import ScrollReveal from '../ScrollReveal';
const ServicesOverview = () => {
const services = [
@ -44,11 +42,6 @@ const ServicesOverview = () => {
}
];
const headerRef = useRef(null);
const gridRef = useRef(null);
const isHeaderInView = useInView(headerRef, { once: true, margin: "-100px" });
const isGridInView = useInView(gridRef, { once: true, margin: "-100px" });
return (
<section className="py-24 bg-background-deep relative overflow-hidden">
{/* Background decoration */}
@ -57,12 +50,7 @@ const ServicesOverview = () => {
<div className="absolute bottom-1/4 left-0 w-96 h-96 bg-neon/5 rounded-full blur-3xl"></div>
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
ref={headerRef}
initial="hidden"
animate={isHeaderInView ? "visible" : "hidden"}
variants={fadeInUp}
>
<ScrollReveal>
<div className="text-center mb-16">
<h2 className="font-heading font-bold text-4xl sm:text-5xl text-foreground mb-6">
Complete IT solutions for{' '}
@ -72,110 +60,66 @@ const ServicesOverview = () => {
From desktop support to enterprise infrastructure we've got your technology covered.
</p>
</div>
</motion.div>
</ScrollReveal>
<motion.div
ref={gridRef}
initial="hidden"
animate={isGridInView ? "visible" : "hidden"}
variants={staggerContainer}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{services.map((service, index) => {
const Icon = service.icon;
return (
<motion.div key={service.title} variants={staggerItem} className="h-full">
<motion.div
className="card-dark p-8 h-full relative overflow-hidden group border border-white/5 hover:border-neon/50 transition-colors duration-500"
whileHover={{
y: -10,
scale: 1.02,
boxShadow: "0 20px 40px -10px rgba(51, 102, 255, 0.3)"
}}
transition={{ duration: 0.4, ease: "easeOut" }}
>
{/* Subtle gradient background nice touch */}
<div className="absolute inset-0 bg-gradient-to-br from-neon/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<ScrollReveal key={service.title} delay={index * 100}>
<div className="card-dark p-8 group hover:shadow-neon transition-all duration-500 hover:-translate-y-1">
{/* Icon */}
<motion.div
className="w-12 h-12 bg-neon/10 rounded-xl flex items-center justify-center mb-6 relative z-10 group-hover:bg-neon/20 transition-colors duration-300"
whileHover={{ rotate: 360, scale: 1.1 }}
transition={{ duration: 0.6 }}
>
<Icon className="w-6 h-6 text-neon drop-shadow-[0_0_8px_rgba(51,102,255,0.6)]" />
</motion.div>
<div className="w-12 h-12 bg-neon/20 rounded-xl flex items-center justify-center mb-6 group-hover:bg-neon/30 transition-colors">
<Icon className="w-6 h-6 text-neon" />
</div>
{/* Content */}
<div className="relative z-10">
<h3 className="font-heading font-bold text-xl text-foreground mb-4 group-hover:text-neon transition-colors duration-300">
{service.title}
</h3>
<h3 className="font-heading font-bold text-xl text-foreground mb-4">
{service.title}
</h3>
<p className="text-foreground-muted mb-6 leading-relaxed">
{service.description}
</p>
<p className="text-foreground-muted mb-6 leading-relaxed">
{service.description}
</p>
{/* Features */}
<ul className="space-y-2 mb-6">
{service.features.map((feature) => (
<li key={feature} className="flex items-center text-sm text-foreground-muted">
<div className="w-1.5 h-1.5 bg-neon rounded-full mr-3"></div>
{feature}
</li>
))}
</ul>
{/* Features */}
<ul className="space-y-2 mb-6">
{service.features.map((feature) => (
<li key={feature} className="flex items-center text-sm text-foreground-muted">
<motion.div
className="w-1.5 h-1.5 bg-neon rounded-full mr-3 shadow-[0_0_5px_rgba(51,102,255,0.8)]"
animate={{
scale: [1, 1.5, 1],
opacity: [0.7, 1, 0.7]
}}
transition={{
duration: 2,
repeat: Infinity,
delay: index * 0.2
}}
/>
{feature}
</li>
))}
</ul>
{/* CTA */}
<Link
to="/services"
className="inline-flex items-center text-neon font-medium group-hover:translate-x-2 transition-transform duration-300"
>
Learn more
<motion.svg
className="w-4 h-4 ml-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
animate={{ x: [0, 3, 0] }}
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" />
</motion.svg>
</Link>
</div>
</motion.div>
</motion.div>
{/* CTA */}
<Link
to="/services"
className="inline-flex items-center text-neon font-medium hover:text-neon/80 transition-colors group-hover:underline"
>
Learn more
<svg className="w-4 h-4 ml-1 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" />
</svg>
</Link>
</div>
</ScrollReveal>
);
})}
</motion.div>
</div>
{/* Bottom CTA */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isGridInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
transition={{ delay: 0.8, duration: 0.6 }}
className="text-center mt-16"
>
<Link
to="/services"
className="btn-ghost text-lg px-12 py-4"
>
View all services
</Link>
</motion.div>
<ScrollReveal delay={600}>
<div className="text-center mt-16">
<Link
to="/services"
className="btn-ghost text-lg px-12 py-4"
>
View all services
</Link>
</div>
</ScrollReveal>
</div>
</section>
);

View File

@ -1,54 +0,0 @@
import { motion } from 'framer-motion';
const TrustedBy = () => {
// Placeholder logos - in a real app these would be SVGs
const companies = [
{ name: "TechCorp", color: "bg-blue-400" },
{ name: "SecureNet", color: "bg-indigo-400" },
{ name: "CloudSystems", color: "bg-cyan-400" },
{ name: "FutureData", color: "bg-sky-400" },
{ name: "CyberGuard", color: "bg-teal-400" },
{ name: "NetWorks", color: "bg-emerald-400" },
];
// duplicate for seamless loop
const logos = [...companies, ...companies, ...companies];
return (
<section className="py-10 border-b border-white/5 bg-background-deep relative overflow-hidden">
<p className="text-center text-sm font-medium text-foreground-muted mb-8 tracking-wider uppercase relative z-10 px-4">
Trusted by innovative businesses in Corpus Christi
</p>
<div className="relative flex overflow-hidden mask-linear-fade">
{/* Gradient Masks for smooth fade out at edges */}
<div className="absolute left-0 top-0 bottom-0 w-24 bg-gradient-to-r from-background-deep to-transparent z-10"></div>
<div className="absolute right-0 top-0 bottom-0 w-24 bg-gradient-to-l from-background-deep to-transparent z-10"></div>
<motion.div
className="flex space-x-16 items-center"
animate={{ x: ["0%", "-50%"] }}
transition={{
duration: 30,
repeat: Infinity,
ease: "linear",
repeatType: "loop"
}}
>
{logos.map((company, index) => (
<div key={index} className="flex items-center space-x-2 opacity-50 grayscale hover:grayscale-0 hover:opacity-100 transition-all duration-300">
{/* Fake Logo Icon */}
<div className={`w-8 h-8 rounded-lg ${company.color}/20 flex items-center justify-center`}>
<div className={`w-4 h-4 rounded-full ${company.color}`}></div>
</div>
{/* Fake Logo Text */}
<span className="text-xl font-bold text-white tracking-tight">{company.name}</span>
</div>
))}
</motion.div>
</div>
</section>
);
};
export default TrustedBy;

View File

@ -1,58 +1,6 @@
import { Shield, Zap, Users, ArrowRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import { motion, useInView, useMotionValue, useSpring, useTransform } from 'framer-motion';
import { useRef, MouseEvent } from 'react';
import { fadeInUp, slideInLeft, slideInRight, buttonHover, buttonTap, cardHover } from '@/utils/animations';
// Optimized 3D Tilt Card Component with reduced effect
const TiltCard = ({ children, className = '' }: { children: React.ReactNode; className?: string }) => {
const cardRef = useRef<HTMLDivElement>(null);
const mouseX = useMotionValue(0);
const mouseY = useMotionValue(0);
// Reduced rotation range for subtler effect (10 -> 5 degrees)
const rotateX = useSpring(useTransform(mouseY, [-0.5, 0.5], [5, -5]), {
stiffness: 200,
damping: 25,
});
const rotateY = useSpring(useTransform(mouseX, [-0.5, 0.5], [-5, 5]), {
stiffness: 200,
damping: 25,
});
const handleMouseMove = (e: MouseEvent<HTMLDivElement>) => {
if (!cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
mouseX.set((e.clientX - centerX) / (rect.width / 2));
mouseY.set((e.clientY - centerY) / (rect.height / 2));
};
const handleMouseLeave = () => {
mouseX.set(0);
mouseY.set(0);
};
return (
<motion.div
ref={cardRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={{
rotateX,
rotateY,
transformStyle: 'preserve-3d',
willChange: 'transform',
}}
className={className}
whileHover={{ scale: 1.01 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
);
};
import ScrollReveal from '../ScrollReveal';
const ValuePillars = () => {
const pillars = [
@ -79,21 +27,13 @@ const ValuePillars = () => {
},
];
const headerRef = useRef(null);
const isHeaderInView = useInView(headerRef, { once: true, margin: "-100px" });
return (
<section className="py-24 bg-background-deep relative overflow-hidden">
{/* Background decoration */}
<div className="absolute inset-0 grid-overlay opacity-20"></div>
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
ref={headerRef}
initial="hidden"
animate={isHeaderInView ? "visible" : "hidden"}
variants={fadeInUp}
>
<ScrollReveal>
<div className="text-center mb-16">
<h2 className="font-heading font-bold text-4xl sm:text-5xl text-foreground mb-6">
Why teams choose us for{' '}
@ -103,96 +43,58 @@ const ValuePillars = () => {
We handle the complexity so you can focus on what you do best.
</p>
</div>
</motion.div>
</ScrollReveal>
<div className="space-y-24">
{pillars.map((pillar, index) => {
const Icon = pillar.icon;
const isReverse = index % 2 === 1;
const itemRef = useRef(null);
const isInView = useInView(itemRef, { once: true, margin: "-100px" });
return (
<motion.div
key={pillar.number}
ref={itemRef}
initial="hidden"
animate={isInView ? "visible" : "hidden"}
variants={isReverse ? slideInRight : slideInLeft}
transition={{ delay: 0.2 }}
>
<ScrollReveal key={pillar.number} delay={index * 200}>
<div className={`flex flex-col ${isReverse ? 'lg:flex-row-reverse' : 'lg:flex-row'} items-center gap-12 lg:gap-16`}>
{/* Content */}
<div className="flex-1 space-y-6">
<div className="flex items-center space-x-4">
<motion.span
className="text-6xl font-heading font-bold text-neon/30"
initial={{ opacity: 0, scale: 0.5 }}
animate={isInView ? { opacity: 1, scale: 1 } : { opacity: 0, scale: 0.5 }}
transition={{ delay: 0.4, duration: 0.6, type: "spring" }}
>
<span className="text-6xl font-heading font-bold text-neon/30">
{pillar.number}
</motion.span>
<motion.div
className="w-12 h-12 bg-neon/20 rounded-xl flex items-center justify-center"
initial={{ opacity: 0, rotate: -180 }}
animate={isInView ? { opacity: 1, rotate: 0 } : { opacity: 0, rotate: -180 }}
transition={{ delay: 0.5, duration: 0.6 }}
>
</span>
<div className="w-12 h-12 bg-neon/20 rounded-xl flex items-center justify-center">
<Icon className="w-6 h-6 text-neon" />
</motion.div>
</div>
</div>
<h3 className="font-heading font-bold text-3xl text-foreground">
{pillar.title}
</h3>
<p className="text-lg text-foreground-muted leading-relaxed">
{pillar.description}
</p>
<motion.div whileHover={buttonHover} whileTap={buttonTap}>
<Link
to="/services"
className="btn-ghost group flex items-center space-x-2 w-fit"
onClick={() => window.scrollTo(0, 0)}
>
<span>Learn more</span>
<motion.div
animate={{ x: [0, 3, 0] }}
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
>
<ArrowRight className="w-4 h-4" />
</motion.div>
</Link>
</motion.div>
<Link
to="/services"
className="btn-ghost group flex items-center space-x-2 w-fit"
onClick={() => window.scrollTo(0, 0)}
>
<span>Learn more</span>
<ArrowRight className="w-4 h-4 transition-transform group-hover:translate-x-1" />
</Link>
</div>
{/* Image with 3D Tilt Effect */}
<TiltCard className="flex-1">
{/* Image */}
<div className="flex-1 parallax">
<div className="card-dark p-2 group hover:shadow-neon transition-all duration-500">
<div className="relative overflow-hidden rounded-xl">
<motion.img
src={pillar.image}
alt={pillar.title}
className="w-full h-64 lg:h-80 object-cover"
whileHover={{ scale: 1.05 }}
transition={{ duration: 0.4, ease: "easeOut" }}
loading="lazy"
style={{ willChange: 'transform' }}
/>
{/* Simplified shine effect on hover */}
<motion.div
className="absolute inset-0 bg-gradient-to-tr from-transparent via-white/8 to-transparent pointer-events-none"
initial={{ x: '-100%', y: '-100%' }}
whileHover={{ x: '100%', y: '100%' }}
transition={{ duration: 0.5, ease: "easeOut" }}
/>
</div>
<img
src={pillar.image}
alt={pillar.title}
className="w-full h-64 lg:h-80 object-cover rounded-xl transition-transform duration-500 group-hover:scale-105"
loading="lazy"
/>
</div>
</TiltCard>
</div>
</div>
</motion.div>
</ScrollReveal>
);
})}
</div>

View File

@ -1,3 +1,5 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@ -86,36 +88,11 @@
scroll-behavior: smooth;
}
/* Accessibility: Focus visible styles */
*:focus-visible {
outline: 2px solid hsl(var(--neon));
outline-offset: 3px;
border-radius: 4px;
}
/* Skip link for keyboard navigation */
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: hsl(var(--neon));
color: hsl(var(--background));
padding: 8px 16px;
text-decoration: none;
z-index: 100;
font-weight: 600;
border-radius: 0 0 8px 0;
}
.skip-link:focus {
top: 0;
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
@ -124,29 +101,6 @@
transition-duration: 0.01ms !important;
}
}
/* Screen reader only class */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.sr-only:focus,
.sr-only:active {
position: static;
width: auto;
height: auto;
overflow: visible;
clip: auto;
white-space: normal;
}
}
@layer components {
@ -194,26 +148,6 @@
@apply rounded-[var(--radius)] px-8 py-4 font-semibold;
@apply transition-all duration-300 ease-out;
@apply shadow-[0_0_0_1px_hsl(var(--neon))] hover:shadow-[0_0_20px_hsl(var(--neon)/0.5)];
position: relative;
overflow: hidden;
}
.btn-neon::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.btn-neon:hover::before {
width: 300px;
height: 300px;
}
.btn-ghost {
@ -221,116 +155,16 @@
@apply rounded-[var(--radius)] px-8 py-4 font-semibold;
@apply transition-all duration-300 ease-out;
@apply hover:shadow-[0_0_15px_hsl(var(--neon)/0.3)];
position: relative;
overflow: hidden;
}
.btn-ghost::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(to right, transparent, hsl(var(--neon)), transparent);
transform: translateX(-100%);
transition: transform 0.5s ease-out;
}
.btn-ghost:hover::after {
transform: translateX(100%);
}
/* Card styles */
.card-dark {
@apply bg-card border border-card-border rounded-[var(--radius-lg)];
@apply backdrop-blur-sm shadow-[var(--shadow-card)];
position: relative;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-dark::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: linear-gradient(45deg,
transparent 30%,
hsl(var(--neon) / 0.1) 50%,
transparent 70%);
border-radius: inherit;
opacity: 0;
transition: opacity 0.4s ease;
z-index: -1;
}
.card-dark:hover::before {
opacity: 1;
}
.card-dark:hover {
transform: translateY(-4px);
border-color: hsl(var(--neon) / 0.3);
}
/* Typography helpers */
.text-balance {
text-wrap: balance;
}
/* Smooth focus ring for better UX */
.focus-ring {
@apply focus:outline-none focus:ring-2 focus:ring-neon focus:ring-offset-2 focus:ring-offset-background;
transition: all 0.2s ease;
}
/* Magnetic hover effect for interactive elements */
.magnetic-hover {
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.magnetic-hover:hover {
transform: translateY(-2px);
}
/* Glassmorphism effect */
.glass {
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
/* Shimmer effect for loading states */
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
.shimmer {
position: relative;
overflow: hidden;
}
.shimmer::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(51, 102, 255, 0.1),
transparent
);
animation: shimmer 2s infinite;
}
}

View File

@ -4,19 +4,10 @@ import { Shield, Users, Zap, MapPin } from 'lucide-react';
import ScrollReveal from '@/components/ScrollReveal';
import CountUpNumber from '@/components/CountUpNumber';
import { useEffect, useRef } from 'react';
import { useSEO } from '@/hooks/useSEO';
const About = () => {
const imageRef = useRef<HTMLImageElement>(null);
useSEO({
title: 'About Bay Area Affiliates | IT Experts Corpus Christi Since 2010',
description: 'Local IT expertise for the Coastal Bend since 2010. 150+ businesses served, 99.9% uptime. Security-first approach with clear communication. Meet the team.',
keywords: 'about Bay Area Affiliates, IT company Corpus Christi, managed services provider Texas, local IT support Coastal Bend',
canonical: 'https://bayarea-cc.com/about',
ogImage: 'https://bayarea-cc.com/og-image.png',
});
useEffect(() => {
const handleScroll = () => {
if (imageRef.current) {
@ -75,7 +66,7 @@ const About = () => {
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero section with image background */}
<section className="relative h-screen flex items-center justify-center overflow-hidden">
{/* Background image with parallax */}
@ -87,7 +78,7 @@ const About = () => {
className="w-full h-[110%] object-cover will-change-transform"
style={{ transform: 'translateY(0px) scale(1.1)' }}
loading="eager"
fetchPriority="high"
fetchpriority="high"
/>
</div>

View File

@ -81,7 +81,7 @@ const Blog = () => {
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero section with image background */}
<section className="relative h-screen flex items-center justify-center overflow-hidden">
{/* Background image with parallax */}
@ -93,7 +93,7 @@ const Blog = () => {
className="w-full h-[110%] object-cover will-change-transform"
style={{ transform: 'translateY(0px) scale(1.1)' }}
loading="eager"
fetchPriority="high"
fetchpriority="high"
/>
</div>
@ -211,8 +211,9 @@ const Blog = () => {
{/* Scroll to Top Button */}
<button
onClick={scrollToTop}
className={`fixed bottom-8 right-8 z-50 p-4 bg-neon text-neon-foreground rounded-full shadow-lg shadow-neon/50 transition-all duration-300 hover:scale-110 hover:shadow-neon ${showScrollTop ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-16 pointer-events-none'
}`}
className={`fixed bottom-8 right-8 z-50 p-4 bg-neon text-neon-foreground rounded-full shadow-lg shadow-neon/50 transition-all duration-300 hover:scale-110 hover:shadow-neon ${
showScrollTop ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-16 pointer-events-none'
}`}
aria-label="Scroll to top"
>
<ArrowUp className="w-6 h-6" />

View File

@ -112,7 +112,7 @@ const BlogPost = () => {
<div className="min-h-screen bg-background-deep">
<Navigation />
<main role="main">
<main>
{/* Hero section with featured image */}
<section className="relative h-[60vh] min-h-[400px] flex items-center justify-center overflow-hidden">
{/* Background image */}
@ -122,7 +122,7 @@ const BlogPost = () => {
alt={post.title}
className="w-full h-full object-cover"
loading="eager"
fetchPriority="high"
fetchpriority="high"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background-deep via-background-deep/60 to-transparent"></div>
</div>

View File

@ -5,17 +5,8 @@ import { Mail, Phone, MapPin, Clock, DollarSign, Headphones } from 'lucide-react
import ScrollReveal from '@/components/ScrollReveal';
import { useToast } from '@/hooks/use-toast';
import { useEffect, useRef } from 'react';
import { useSEO } from '@/hooks/useSEO';
const Contact = () => {
useSEO({
title: 'Contact Bay Area Affiliates | Free IT Consultation Corpus Christi',
description: 'Get in touch for a free IT consultation. 24-hour response, free assessment, no obligation. Serving Corpus Christi, Portland, Rockport & the Coastal Bend.',
keywords: 'contact IT support Corpus Christi, free IT consultation, Bay Area Affiliates phone, IT services quote Texas',
canonical: 'https://bayarea-cc.com/contact',
ogImage: 'https://bayarea-cc.com/og-image.png',
});
const { toast } = useToast();
const imageRef = useRef<HTMLImageElement>(null);
const [formData, setFormData] = useState({
@ -90,7 +81,7 @@ const Contact = () => {
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero section with image background */}
<section className="relative h-screen flex items-center justify-center overflow-hidden">
{/* Background image with parallax */}
@ -167,7 +158,7 @@ const Contact = () => {
required
value={formData.name}
onChange={handleChange}
className="w-full px-4 py-3 bg-white/5 backdrop-blur-sm border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-neon focus:bg-white/10 focus:shadow-[0_0_15px_rgba(51,102,255,0.3)] transition-all duration-300 text-foreground placeholder-foreground-muted/50"
className="w-full px-4 py-3 bg-input border border-input-border rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-transparent text-foreground"
placeholder="Your full name"
/>
</div>
@ -183,7 +174,7 @@ const Contact = () => {
required
value={formData.email}
onChange={handleChange}
className="w-full px-4 py-3 bg-white/5 backdrop-blur-sm border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-neon focus:bg-white/10 focus:shadow-[0_0_15px_rgba(51,102,255,0.3)] transition-all duration-300 text-foreground placeholder-foreground-muted/50"
className="w-full px-4 py-3 bg-input border border-input-border rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-transparent text-foreground"
placeholder="your@email.com"
/>
</div>
@ -200,7 +191,7 @@ const Contact = () => {
name="company"
value={formData.company}
onChange={handleChange}
className="w-full px-4 py-3 bg-white/5 backdrop-blur-sm border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-neon focus:bg-white/10 focus:shadow-[0_0_15px_rgba(51,102,255,0.3)] transition-all duration-300 text-foreground placeholder-foreground-muted/50"
className="w-full px-4 py-3 bg-input border border-input-border rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-transparent text-foreground"
placeholder="Your company name"
/>
</div>
@ -215,7 +206,7 @@ const Contact = () => {
name="phone"
value={formData.phone}
onChange={handleChange}
className="w-full px-4 py-3 bg-white/5 backdrop-blur-sm border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-neon focus:bg-white/10 focus:shadow-[0_0_15px_rgba(51,102,255,0.3)] transition-all duration-300 text-foreground placeholder-foreground-muted/50"
className="w-full px-4 py-3 bg-input border border-input-border rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-transparent text-foreground"
placeholder="(361) 555-0123"
/>
</div>
@ -232,7 +223,7 @@ const Contact = () => {
rows={5}
value={formData.message}
onChange={handleChange}
className="w-full px-4 py-3 bg-white/5 backdrop-blur-sm border border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-neon focus:bg-white/10 focus:shadow-[0_0_15px_rgba(51,102,255,0.3)] transition-all duration-300 text-foreground placeholder-foreground-muted/50 resize-none"
className="w-full px-4 py-3 bg-input border border-input-border rounded-lg focus:outline-none focus:ring-2 focus:ring-neon focus:border-transparent text-foreground resize-none"
placeholder="Tell us about your IT needs or challenges..."
/>
</div>
@ -381,47 +372,6 @@ const Contact = () => {
</ScrollReveal>
</div>
</section>
{/* Service Area Map */}
<section className="py-16 bg-background-deep">
<div className="w-full max-w-[95%] mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal width="100%">
<div className="text-center mb-8">
<h2 className="font-heading font-bold text-3xl text-foreground mb-4">
Our Service Area
</h2>
<p className="text-foreground-muted max-w-2xl mx-auto">
Proudly serving Corpus Christi, Portland, Rockport, Aransas Pass, Kingsville, Port Aransas, and the entire Coastal Bend region.
</p>
</div>
<div className="card-dark p-2 overflow-hidden">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d113726.84791849895!2d-97.48659164550781!3d27.800587899999997!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x8668fa3c40818e93%3A0x4e3c0a1c2bef9c65!2sCorpus%20Christi%2C%20TX!5e0!3m2!1sen!2sus!4v1736364000000!5m2!1sen!2sus"
width="100%"
height="450"
style={{ border: 0, borderRadius: '1rem' }}
allowFullScreen
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
title="Bay Area Affiliates Service Area - Corpus Christi and Coastal Bend"
aria-label="Google Maps showing our service area in Corpus Christi and the Coastal Bend region"
/>
</div>
<div className="mt-8 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
{['Corpus Christi', 'Portland', 'Rockport', 'Aransas Pass', 'Kingsville', 'Port Aransas'].map((city) => (
<div key={city} className="text-center">
<div className="card-dark p-3">
<MapPin className="w-5 h-5 text-neon mx-auto mb-1" />
<p className="text-xs text-foreground font-medium">{city}</p>
</div>
</div>
))}
</div>
</ScrollReveal>
</div>
</section>
</main>
<Footer />

View File

@ -9,27 +9,27 @@ import { Monitor, HardDrive, Cpu, CheckCircle, DollarSign, Truck, Shield, Award
const DesktopHardware = () => {
useEffect(() => {
document.title = 'Desktop Hardware Corpus Christi | Bay Area Affiliates';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Quality new/refurbished desktops, fast local installation.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'Refurbished Desktops Corpus Christi—Quality Business Hardware');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Quality new/refurbished desktops, fast local installation.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'Affordable PC Business Hardware Corpus Christi');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Quality new/refurbished desktops, fast local installation.');
@ -39,7 +39,7 @@ const DesktopHardware = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-gray-900 via-gray-800 to-gray-600 text-white py-20">
<div className="container mx-auto px-4">
@ -72,7 +72,7 @@ const DesktopHardware = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Professional Desktop Solutions</h2>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card>
<CardHeader>
@ -93,7 +93,7 @@ const DesktopHardware = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
@ -113,7 +113,7 @@ const DesktopHardware = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
@ -133,7 +133,7 @@ const DesktopHardware = () => {
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
@ -163,7 +163,7 @@ const DesktopHardware = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Desktop Categories for Every Business Need</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-blue-500">
<h3 className="font-bold text-lg mb-4">Essential Business</h3>
@ -180,7 +180,7 @@ const DesktopHardware = () => {
<div className="text-sm text-gray-500">Starting price</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-orange-500">
<h3 className="font-bold text-lg mb-4">Professional Plus</h3>
<p className="text-gray-600 mb-4">Enhanced performance for demanding business applications and multitasking.</p>
@ -196,7 +196,7 @@ const DesktopHardware = () => {
<div className="text-sm text-gray-500">Starting price</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-purple-500">
<h3 className="font-bold text-lg mb-4">Workstation Class</h3>
<p className="text-gray-600 mb-4">High-performance systems for CAD, design, development, and intensive applications.</p>
@ -213,7 +213,7 @@ const DesktopHardware = () => {
</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">Why Choose Refurbished Business Hardware?</h3>
<div className="grid md:grid-cols-2 gap-8">
@ -270,7 +270,7 @@ const DesktopHardware = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Complete Installation & Setup Service</h2>
<div className="grid md:grid-cols-4 gap-8">
<Card className="text-center">
<CardHeader>
@ -283,7 +283,7 @@ const DesktopHardware = () => {
<p className="text-sm">Assess your needs and recommend the optimal hardware configuration for your business.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -295,7 +295,7 @@ const DesktopHardware = () => {
<p className="text-sm">Install and configure software, security settings, and business applications.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -307,7 +307,7 @@ const DesktopHardware = () => {
<p className="text-sm">On-site installation, network integration, and user account setup.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -330,10 +330,10 @@ const DesktopHardware = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Trusted Hardware Supplier for Coastal Bend SMBs</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates has supplied and serviced business computers throughout the Corpus Christi area since 1999.
Bay Area Affiliates has supplied and serviced business computers throughout the Corpus Christi area since 1999.
We understand local business needs and provide personalized hardware solutions.
</p>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="text-4xl font-bold text-blue-600 mb-2">5,000+</div>
@ -348,11 +348,11 @@ const DesktopHardware = () => {
<div className="text-gray-600">Years Experience</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Local Hardware Support Throughout the Coastal Bend</h3>
<p className="text-gray-700 mb-6">
From single desktop replacements to complete office refreshes, we provide comprehensive
From single desktop replacements to complete office refreshes, we provide comprehensive
hardware solutions with local pickup, delivery, and installation services.
</p>
<div className="flex flex-wrap justify-center gap-4">
@ -374,10 +374,10 @@ const DesktopHardware = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-6">Upgrade Your Business Hardware Today</h2>
<p className="text-xl mb-8 text-gray-100">
Get quality business desktops with professional installation and local support.
Get quality business desktops with professional installation and local support.
Free consultation and custom quotes for Corpus Christi area businesses.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Award className="w-5 h-5 mr-2" />
@ -387,7 +387,7 @@ const DesktopHardware = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-gray-200">
Free consultation Local delivery Professional installation 12-month warranty
</p>

View File

@ -6,48 +6,18 @@ import ProcessTimeline from '@/components/home/ProcessTimeline';
import ServicesOverview from '@/components/home/ServicesOverview';
import ProofSection from '@/components/home/ProofSection';
import CTASection from '@/components/home/CTASection';
import TrustedBy from '@/components/home/TrustedBy';
import ScrollReveal from '@/components/ScrollReveal';
import { useSEO } from '@/hooks/useSEO';
const Index = () => {
useSEO({
title: 'IT Support Corpus Christi ⚡ 24/7 Managed Services | Bay Area Affiliates',
description: 'Stop IT headaches! 25+ years experience, 2-hour response time, 24/7 monitoring. Managed IT services in Corpus Christi & Coastal Bend. Free assessment. Call (361) 765-8400',
keywords: 'managed IT services corpus christi, IT support coastal bend, 24/7 IT monitoring, business computer solutions, Windows 11 migration, VPN setup, network security corpus christi',
canonical: 'https://bayarea-cc.com/',
ogImage: 'https://bayarea-cc.com/og-image.png',
});
return (
<div className="min-h-screen">
<Navigation />
<main id="main-content" role="main">
<main>
<HeroSection />
<ScrollReveal delay={0.2} width="100%">
<TrustedBy />
</ScrollReveal>
<ScrollReveal width="100%">
<ValuePillars />
</ScrollReveal>
<ScrollReveal width="100%">
<ProcessTimeline />
</ScrollReveal>
<ScrollReveal width="100%">
<ServicesOverview />
</ScrollReveal>
<ScrollReveal width="100%">
<ProofSection />
</ScrollReveal>
<ScrollReveal width="100%">
<CTASection />
</ScrollReveal>
<ValuePillars />
<ProcessTimeline />
<ServicesOverview />
<ProofSection />
<CTASection />
</main>
<Footer />
</div>

View File

@ -9,27 +9,27 @@ import { HardDrive, Cloud, Shield, CheckCircle, Database, Server, Lock, Archive
const NetworkAttachedStorage = () => {
useEffect(() => {
document.title = 'NAS Setup Corpus Christi | Bay Area Affiliates';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Secure, scalable backups for business data. Book install.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'Network Attached Storage Setup Corpus Christi—Business Data Solutions');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Secure, scalable backups for business data. Book install.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'NAS Backup Storage SMB Corpus Christi');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Secure, scalable backups for business data. Book install.');
@ -39,7 +39,7 @@ const NetworkAttachedStorage = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-blue-900 via-blue-800 to-blue-600 text-white py-20">
<div className="container mx-auto px-4">
@ -72,7 +72,7 @@ const NetworkAttachedStorage = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Protect Your Business Data with Professional NAS</h2>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card>
<CardHeader>
@ -93,7 +93,7 @@ const NetworkAttachedStorage = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
@ -113,7 +113,7 @@ const NetworkAttachedStorage = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
@ -133,7 +133,7 @@ const NetworkAttachedStorage = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
@ -163,7 +163,7 @@ const NetworkAttachedStorage = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">NAS Solutions for Every Business Size</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-blue-500">
<h3 className="font-bold text-lg mb-4">Essential NAS</h3>
@ -181,7 +181,7 @@ const NetworkAttachedStorage = () => {
<div className="text-sm text-gray-500">Complete setup</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-orange-500">
<h3 className="font-bold text-lg mb-4">Professional NAS</h3>
<p className="text-gray-600 mb-4">Advanced features for growing businesses with critical data protection needs.</p>
@ -198,7 +198,7 @@ const NetworkAttachedStorage = () => {
<div className="text-sm text-gray-500">Complete setup</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-purple-500">
<h3 className="font-bold text-lg mb-4">Enterprise NAS</h3>
<p className="text-gray-600 mb-4">High-performance storage for businesses with demanding data requirements.</p>
@ -216,7 +216,7 @@ const NetworkAttachedStorage = () => {
</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">Why Businesses Choose NAS Over Cloud Storage</h3>
<div className="grid md:grid-cols-2 gap-8">
@ -263,7 +263,7 @@ const NetworkAttachedStorage = () => {
</ul>
</div>
</div>
<div className="mt-6 bg-blue-50 p-6 rounded-lg">
<h4 className="font-semibold text-blue-700 mb-3">ROI Calculation Example</h4>
<div className="grid md:grid-cols-2 gap-6 text-sm">
@ -288,7 +288,7 @@ const NetworkAttachedStorage = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Professional NAS Installation Process</h2>
<div className="grid md:grid-cols-4 gap-8">
<Card className="text-center">
<CardHeader>
@ -301,7 +301,7 @@ const NetworkAttachedStorage = () => {
<p className="text-sm">Analyze your data storage needs, backup requirements, and growth projections.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -313,7 +313,7 @@ const NetworkAttachedStorage = () => {
<p className="text-sm">Set up NAS hardware, configure RAID, and install necessary software applications.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -325,7 +325,7 @@ const NetworkAttachedStorage = () => {
<p className="text-sm">Transfer existing data safely and set up automated backup schedules for all devices.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -349,11 +349,11 @@ const NetworkAttachedStorage = () => {
<h2 className="text-3xl font-bold text-center mb-8">Emergency Data Recovery Services</h2>
<div className="text-center mb-8">
<p className="text-xl text-gray-600">
Already experienced data loss? We provide emergency recovery services while setting up
Already experienced data loss? We provide emergency recovery services while setting up
your new NAS to prevent future disasters.
</p>
</div>
<div className="grid md:grid-cols-2 gap-8">
<div className="bg-white p-6 rounded-lg shadow">
<h3 className="text-xl font-bold mb-4 text-red-600">What We Recover</h3>
@ -366,7 +366,7 @@ const NetworkAttachedStorage = () => {
<li> Photos and multimedia files</li>
</ul>
</div>
<div className="bg-white p-6 rounded-lg shadow">
<h3 className="text-xl font-bold mb-4 text-red-600">Recovery Process</h3>
<ul className="space-y-2 text-gray-700">
@ -379,7 +379,7 @@ const NetworkAttachedStorage = () => {
</ul>
</div>
</div>
<div className="mt-8 text-center">
<Button size="lg" className="bg-red-600 hover:bg-red-700 text-white">
<Lock className="w-5 h-5 mr-2" />
@ -396,10 +396,10 @@ const NetworkAttachedStorage = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Data Storage Specialists Serving the Coastal Bend</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates has protected business data for Corpus Christi companies since 1999.
Bay Area Affiliates has protected business data for Corpus Christi companies since 1999.
We understand the critical importance of data security and business continuity.
</p>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="text-4xl font-bold text-blue-600 mb-2">250+</div>
@ -414,11 +414,11 @@ const NetworkAttachedStorage = () => {
<div className="text-gray-600">Data Loss with Our NAS</div>
</div>
</div>
<div className="bg-blue-50 p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Protecting Business Data Throughout the Coastal Bend</h3>
<p className="text-gray-700 mb-6">
From small professional practices to growing enterprises, we provide comprehensive
From small professional practices to growing enterprises, we provide comprehensive
data storage and backup solutions with local support and maintenance.
</p>
<div className="flex flex-wrap justify-center gap-4">
@ -440,10 +440,10 @@ const NetworkAttachedStorage = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-6">Don't Risk Your Business Data</h2>
<p className="text-xl mb-8 text-blue-100">
Get professional NAS installation and data protection from Corpus Christi's trusted IT experts.
Get professional NAS installation and data protection from Corpus Christi's trusted IT experts.
Protect your business before disaster strikes.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Database className="w-5 h-5 mr-2" />
@ -453,7 +453,7 @@ const NetworkAttachedStorage = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-blue-200">
Free consultation Professional installation 24/7 monitoring Local support
</p>

View File

@ -9,27 +9,27 @@ import { Network, Router, Shield, CheckCircle, Users, Monitor, Lock, Search } fr
const NetworkInfrastructure = () => {
useEffect(() => {
document.title = 'Network Support Corpus Christi | Bay Area Affiliates';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Reliable SMB network solutions—secure, scalable, supported.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'Network Infrastructure Support Corpus Christi—SMB IT Solutions');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Reliable SMB network solutions—secure, scalable, supported.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'Router Setup IT Audit Corpus Christi');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Reliable SMB network solutions—secure, scalable, supported.');
@ -39,7 +39,7 @@ const NetworkInfrastructure = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-teal-900 via-teal-800 to-teal-600 text-white py-20">
<div className="container mx-auto px-4">
@ -72,7 +72,7 @@ const NetworkInfrastructure = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Comprehensive Network Solutions</h2>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card>
<CardHeader>
@ -93,7 +93,7 @@ const NetworkInfrastructure = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-teal-100 rounded-full flex items-center justify-center mb-4">
@ -113,7 +113,7 @@ const NetworkInfrastructure = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-teal-100 rounded-full flex items-center justify-center mb-4">
@ -133,7 +133,7 @@ const NetworkInfrastructure = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-teal-100 rounded-full flex items-center justify-center mb-4">
@ -163,7 +163,7 @@ const NetworkInfrastructure = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Scalable Network Solutions for Growing SMBs</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-teal-500">
<h3 className="font-bold text-lg mb-4">Starter Network</h3>
@ -181,7 +181,7 @@ const NetworkInfrastructure = () => {
<div className="text-sm text-gray-500">Complete setup</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-orange-500">
<h3 className="font-bold text-lg mb-4">Professional Network</h3>
<p className="text-gray-600 mb-4">Advanced infrastructure for growing businesses with 15-50 employees.</p>
@ -198,7 +198,7 @@ const NetworkInfrastructure = () => {
<div className="text-sm text-gray-500">Complete setup</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-purple-500">
<h3 className="font-bold text-lg mb-4">Enterprise Network</h3>
<p className="text-gray-600 mb-4">Comprehensive infrastructure for larger organizations with 50+ employees.</p>
@ -216,7 +216,7 @@ const NetworkInfrastructure = () => {
</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">Network Performance Benefits</h3>
<div className="grid md:grid-cols-2 gap-8">
@ -273,7 +273,7 @@ const NetworkInfrastructure = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Professional Network Implementation</h2>
<div className="grid md:grid-cols-4 gap-8">
<Card className="text-center">
<CardHeader>
@ -286,7 +286,7 @@ const NetworkInfrastructure = () => {
<p className="text-sm">Comprehensive evaluation of current infrastructure, requirements, and growth plans.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -298,7 +298,7 @@ const NetworkInfrastructure = () => {
<p className="text-sm">Custom network design with equipment selection and detailed implementation plan.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -310,7 +310,7 @@ const NetworkInfrastructure = () => {
<p className="text-sm">Professional installation, configuration, and testing with minimal business disruption.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -332,7 +332,7 @@ const NetworkInfrastructure = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Network Problems We Solve</h2>
<div className="grid md:grid-cols-2 gap-8">
<div className="bg-white p-6 rounded-lg shadow">
<h3 className="text-xl font-bold mb-4 text-red-600">Performance Issues</h3>
@ -345,7 +345,7 @@ const NetworkInfrastructure = () => {
<li> Printers frequently going offline</li>
</ul>
</div>
<div className="bg-white p-6 rounded-lg shadow">
<h3 className="text-xl font-bold mb-4 text-red-600">Security Concerns</h3>
<ul className="space-y-3 text-gray-700">
@ -358,11 +358,11 @@ const NetworkInfrastructure = () => {
</ul>
</div>
</div>
<div className="mt-8 bg-white p-6 rounded-lg shadow text-center">
<h3 className="text-xl font-bold mb-4 text-teal-600">Emergency Network Support</h3>
<p className="text-gray-700 mb-4">
When your network goes down, every minute counts. Our emergency support team provides
When your network goes down, every minute counts. Our emergency support team provides
same-day response for critical network issues affecting your business operations.
</p>
<Button className="bg-red-600 hover:bg-red-700 text-white">
@ -380,10 +380,10 @@ const NetworkInfrastructure = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Trusted Network Experts in the Coastal Bend</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates has designed and maintained business networks throughout the Corpus Christi area since 1999.
Bay Area Affiliates has designed and maintained business networks throughout the Corpus Christi area since 1999.
We understand the unique connectivity challenges of coastal businesses.
</p>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="text-4xl font-bold text-teal-600 mb-2">300+</div>
@ -398,11 +398,11 @@ const NetworkInfrastructure = () => {
<div className="text-gray-600">Average Response Time</div>
</div>
</div>
<div className="bg-teal-50 p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Network Support Across the Coastal Bend</h3>
<p className="text-gray-700 mb-6">
From small office networks to multi-location enterprises, we provide comprehensive
From small office networks to multi-location enterprises, we provide comprehensive
network infrastructure solutions with 24/7 monitoring and local technical support.
</p>
<div className="flex flex-wrap justify-center gap-4">
@ -424,10 +424,10 @@ const NetworkInfrastructure = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-6">Secure Your Business Network Infrastructure</h2>
<p className="text-xl mb-8 text-teal-100">
Get a comprehensive network assessment and custom infrastructure solution from Corpus Christi's
Get a comprehensive network assessment and custom infrastructure solution from Corpus Christi's
trusted IT experts. Free consultation for SMBs.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Router className="w-5 h-5 mr-2" />
@ -437,7 +437,7 @@ const NetworkInfrastructure = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-teal-200">
Free network assessment Same-day emergency support Local experts 24/7 monitoring
</p>

View File

@ -9,27 +9,27 @@ import { Zap, HardDrive, TrendingUp, CheckCircle, DollarSign, Clock, Gauge, BarC
const PerformanceUpgrades = () => {
useEffect(() => {
document.title = 'SSD Upgrade Corpus Christi | Bay Area Affiliates';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Boost device speed and reliability. Contact for upgrade today.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'SSD Upgrade Corpus Christi—Boost SMB Performance');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Boost device speed and reliability. Contact for upgrade today.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'Desktop Speed & Laptop Optimization Corpus Christi');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Boost device speed and reliability. Contact for upgrade today.');
@ -39,7 +39,7 @@ const PerformanceUpgrades = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-purple-900 via-purple-800 to-purple-600 text-white py-20">
<div className="container mx-auto px-4">
@ -72,7 +72,7 @@ const PerformanceUpgrades = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Transform Your Business Performance</h2>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card>
<CardHeader>
@ -95,7 +95,7 @@ const PerformanceUpgrades = () => {
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-4">
@ -113,7 +113,7 @@ const PerformanceUpgrades = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-4">
@ -132,7 +132,7 @@ const PerformanceUpgrades = () => {
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-4">
@ -160,7 +160,7 @@ const PerformanceUpgrades = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Professional Upgrade Process</h2>
<div className="grid md:grid-cols-4 gap-8 mb-12">
<Card className="text-center">
<CardHeader>
@ -173,7 +173,7 @@ const PerformanceUpgrades = () => {
<p className="text-sm">Comprehensive performance testing to identify bottlenecks and optimal upgrade path.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -185,7 +185,7 @@ const PerformanceUpgrades = () => {
<p className="text-sm">Right-sized SSD recommendation based on your usage patterns and budget.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -197,7 +197,7 @@ const PerformanceUpgrades = () => {
<p className="text-sm">Seamless transfer of all files, programs, and settings to the new SSD.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -210,7 +210,7 @@ const PerformanceUpgrades = () => {
</CardContent>
</Card>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">Performance Upgrade Options</h3>
<div className="grid md:grid-cols-3 gap-6">
@ -227,7 +227,7 @@ const PerformanceUpgrades = () => {
<div className="text-sm text-gray-600">per device</div>
</div>
</div>
<div className="border-2 border-purple-500 p-6 rounded-lg bg-purple-50">
<h4 className="font-bold text-lg mb-4 text-purple-600">Professional</h4>
<ul className="space-y-2 text-sm mb-4">
@ -242,7 +242,7 @@ const PerformanceUpgrades = () => {
<div className="text-sm text-gray-600">per device</div>
</div>
</div>
<div className="border-2 border-gray-200 p-6 rounded-lg">
<h4 className="font-bold text-lg mb-4 text-purple-600">Enterprise</h4>
<ul className="space-y-2 text-sm mb-4">
@ -270,10 +270,10 @@ const PerformanceUpgrades = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Trusted Performance Experts in the Coastal Bend</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates has optimized thousands of computers for Corpus Christi businesses since 1999.
Bay Area Affiliates has optimized thousands of computers for Corpus Christi businesses since 1999.
We know which upgrades deliver the best ROI for your specific needs.
</p>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="text-4xl font-bold text-purple-600 mb-2">2,500+</div>
@ -288,11 +288,11 @@ const PerformanceUpgrades = () => {
<div className="text-gray-600">Average Turnaround</div>
</div>
</div>
<div className="bg-purple-50 p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Serving All Coastal Bend SMBs</h3>
<p className="text-gray-700 mb-6">
From single-device upgrades to fleet optimization, we provide on-site and in-shop services
From single-device upgrades to fleet optimization, we provide on-site and in-shop services
throughout the Corpus Christi area.
</p>
<div className="flex flex-wrap justify-center gap-4">
@ -314,10 +314,10 @@ const PerformanceUpgrades = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-6">Stop Waiting for Slow Computers</h2>
<p className="text-xl mb-8 text-purple-100">
Transform your business productivity with professional SSD upgrades. Same-day service available
Transform your business productivity with professional SSD upgrades. Same-day service available
for Corpus Christi area businesses.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Clock className="w-5 h-5 mr-2" />
@ -327,7 +327,7 @@ const PerformanceUpgrades = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-purple-200">
Free speed analysis Same-day service Local pickup & delivery
</p>

View File

@ -9,27 +9,27 @@ import { Printer, Scan, Network, CheckCircle, Wrench, Clock, Settings, Users } f
const PrinterScannerInstallation = () => {
useEffect(() => {
document.title = 'Printer Install Corpus Christi | Bay Area Affiliates';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Hassle-free device setup—local experts, same-day support.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'Printer Installation Corpus Christi—Same-Day Setup');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Hassle-free device setup—local experts, same-day support.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'Scanner Setup & Office Device Support Corpus Christi');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Hassle-free device setup—local experts, same-day support.');
@ -39,7 +39,7 @@ const PrinterScannerInstallation = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-indigo-900 via-indigo-800 to-indigo-600 text-white py-20">
<div className="container mx-auto px-4">
@ -72,7 +72,7 @@ const PrinterScannerInstallation = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">End Office Device Frustrations</h2>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card>
<CardHeader>
@ -92,7 +92,7 @@ const PrinterScannerInstallation = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mb-4">
@ -111,7 +111,7 @@ const PrinterScannerInstallation = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mb-4">
@ -130,7 +130,7 @@ const PrinterScannerInstallation = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mb-4">
@ -159,7 +159,7 @@ const PrinterScannerInstallation = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Professional Installation Process</h2>
<div className="grid md:grid-cols-4 gap-8 mb-12">
<Card className="text-center">
<CardHeader>
@ -172,7 +172,7 @@ const PrinterScannerInstallation = () => {
<p className="text-sm">Evaluate your office layout, network setup, and device requirements.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -184,7 +184,7 @@ const PrinterScannerInstallation = () => {
<p className="text-sm">Install drivers, configure network settings, and optimize device performance.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -196,7 +196,7 @@ const PrinterScannerInstallation = () => {
<p className="text-sm">Configure access for all users and train staff on device operation.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -209,7 +209,7 @@ const PrinterScannerInstallation = () => {
</CardContent>
</Card>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">Common Device Issues We Solve</h3>
<div className="grid md:grid-cols-2 gap-8">
@ -274,7 +274,7 @@ const PrinterScannerInstallation = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">We Support All Major Brands</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="bg-gray-50 p-6 rounded-lg text-center">
<h3 className="font-bold text-lg mb-4">Printer Brands</h3>
@ -285,7 +285,7 @@ const PrinterScannerInstallation = () => {
<div>Kyocera Sharp Konica</div>
</div>
</div>
<div className="bg-gray-50 p-6 rounded-lg text-center">
<h3 className="font-bold text-lg mb-4">Scanner Types</h3>
<div className="space-y-2 text-gray-700">
@ -296,7 +296,7 @@ const PrinterScannerInstallation = () => {
<div>Photo Scanners</div>
</div>
</div>
<div className="bg-gray-50 p-6 rounded-lg text-center">
<h3 className="font-bold text-lg mb-4">Connection Types</h3>
<div className="space-y-2 text-gray-700">
@ -318,10 +318,10 @@ const PrinterScannerInstallation = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Corpus Christi's Office Device Specialists</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates has been solving office equipment challenges for Coastal Bend businesses since 1999.
Bay Area Affiliates has been solving office equipment challenges for Coastal Bend businesses since 1999.
We know the unique needs of local SMBs and provide same-day service.
</p>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="text-4xl font-bold text-indigo-600 mb-2">1,200+</div>
@ -336,11 +336,11 @@ const PrinterScannerInstallation = () => {
<div className="text-gray-600">Technical Support</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Serving the Entire Coastal Bend</h3>
<p className="text-gray-700 mb-6">
On-site installation and support for businesses throughout the Corpus Christi area.
On-site installation and support for businesses throughout the Corpus Christi area.
We bring the expertise to your location.
</p>
<div className="flex flex-wrap justify-center gap-4">
@ -362,10 +362,10 @@ const PrinterScannerInstallation = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-6">Stop Wrestling with Office Devices</h2>
<p className="text-xl mb-8 text-indigo-100">
Get professional printer and scanner installation from Corpus Christi's trusted IT experts.
Get professional printer and scanner installation from Corpus Christi's trusted IT experts.
Same-day service available for urgent business needs.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Clock className="w-5 h-5 mr-2" />
@ -375,7 +375,7 @@ const PrinterScannerInstallation = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-indigo-200">
Free consultation Same-day service Local on-site support
</p>

View File

@ -3,20 +3,11 @@ import Footer from '@/components/Footer';
import { Monitor, Wifi, Cloud, Shield, Database, Settings, CheckCircle } from 'lucide-react';
import ScrollReveal from '@/components/ScrollReveal';
import { useLayoutEffect, useRef } from 'react';
import { useSEO } from '@/hooks/useSEO';
const Services = () => {
const pageRef = useRef<HTMLDivElement>(null);
const heroImageRef = useRef<HTMLImageElement>(null);
useSEO({
title: 'IT Services Corpus Christi | Managed IT, Network, Cloud | Bay Area Affiliates',
description: 'Complete IT solutions for Corpus Christi businesses. Hardware support, network infrastructure, cloud services, secure remote access, backup & Microsoft 365. Free consultation.',
keywords: 'IT services Corpus Christi, managed IT Coastal Bend, network infrastructure Texas, cloud services, VPN setup, Microsoft 365',
canonical: 'https://bayarea-cc.com/services',
ogImage: 'https://bayarea-cc.com/og-image.png',
});
useLayoutEffect(() => {
// Dynamically import GSAP only when needed to reduce initial bundle size
let ctx: any;
@ -172,7 +163,7 @@ const Services = () => {
<div ref={pageRef} className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero section with image background */}
<section className="relative h-screen flex items-center justify-center overflow-hidden">
{/* Background image with parallax */}
@ -183,7 +174,7 @@ const Services = () => {
alt="Corpus Christi IT services data center with enterprise networking equipment and server infrastructure"
className="w-full h-[110%] object-cover will-change-transform"
loading="eager"
fetchPriority="high"
fetchpriority="high"
/>
</div>

View File

@ -9,27 +9,27 @@ import { Shield, Lock, Zap, CheckCircle, Users, Globe, Server, Smartphone } from
const VpnSetup = () => {
useEffect(() => {
document.title = 'VPN Setup Corpus Christi | Business WireGuard Experts';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Secure remote access for SMBs—Corpus Christi\'s WireGuard VPN experts. Get fast, reliable VPN setup today.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'Corpus Christi WireGuard VPN Setup—Fast, Secure for SMBs');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Secure remote access for SMBs—Corpus Christi\'s WireGuard VPN experts. Get fast, reliable VPN setup today.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'WireGuard VPN—Remote Work Corpus Christi');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Secure remote access for SMBs—Corpus Christi\'s WireGuard VPN experts. Get fast, reliable VPN setup today.');
@ -39,7 +39,7 @@ const VpnSetup = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-green-900 via-green-800 to-green-600 text-white py-20">
<div className="container mx-auto px-4">
@ -72,7 +72,7 @@ const VpnSetup = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Why WireGuard for Corpus SMBs</h2>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card>
<CardHeader>
@ -85,7 +85,7 @@ const VpnSetup = () => {
<p className="text-gray-600">WireGuard outperforms traditional VPN protocols with minimal latency and maximum throughput for your remote teams.</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-4">
@ -97,7 +97,7 @@ const VpnSetup = () => {
<p className="text-gray-600">Advanced cryptography and smaller codebase mean fewer vulnerabilities and stronger protection for your business data.</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-4">
@ -109,7 +109,7 @@ const VpnSetup = () => {
<p className="text-gray-600">Easy configuration and maintenance with automatic key exchange and seamless roaming between networks.</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-4">
@ -131,7 +131,7 @@ const VpnSetup = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Benefits of Secure Remote Work</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="w-20 h-20 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -140,7 +140,7 @@ const VpnSetup = () => {
<h3 className="text-xl font-bold mb-2">Work From Anywhere</h3>
<p className="text-gray-600">Access your office network securely from home, client sites, or anywhere in the world.</p>
</div>
<div className="text-center">
<div className="w-20 h-20 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<Server className="w-10 h-10 text-blue-600" />
@ -148,7 +148,7 @@ const VpnSetup = () => {
<h3 className="text-xl font-bold mb-2">Secure File Access</h3>
<p className="text-gray-600">Connect to your business servers, printers, and shared drives as if you're in the office.</p>
</div>
<div className="text-center">
<div className="w-20 h-20 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<Smartphone className="w-10 h-10 text-blue-600" />
@ -157,7 +157,7 @@ const VpnSetup = () => {
<p className="text-gray-600">One VPN solution for all your deviceslaptops, phones, tablets, and desktops.</p>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">WireGuard vs Traditional VPN</h3>
<div className="overflow-x-auto">
@ -208,7 +208,7 @@ const VpnSetup = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Fast, Reliable VPN Deployment</h2>
<div className="grid md:grid-cols-4 gap-8">
<Card className="text-center">
<CardHeader>
@ -221,7 +221,7 @@ const VpnSetup = () => {
<p className="text-sm">Evaluate your network infrastructure and remote access needs.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -233,7 +233,7 @@ const VpnSetup = () => {
<p className="text-sm">Install and configure WireGuard server on your network.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -245,7 +245,7 @@ const VpnSetup = () => {
<p className="text-sm">Set up VPN clients on all employee devices with secure keys.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -268,10 +268,10 @@ const VpnSetup = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Trusted VPN Experts Serving the Coastal Bend</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates specializes in secure network solutions for local businesses.
Bay Area Affiliates specializes in secure network solutions for local businesses.
We've helped hundreds of Corpus Christi SMBs implement reliable remote access.
</p>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<div className="bg-white p-6 rounded-lg shadow">
<h3 className="text-xl font-bold mb-4 text-green-600">Local Business Focus</h3>
@ -282,7 +282,7 @@ const VpnSetup = () => {
<li> Bilingual technical support</li>
</ul>
</div>
<div className="bg-white p-6 rounded-lg shadow">
<h3 className="text-xl font-bold mb-4 text-green-600">WireGuard Specialization</h3>
<ul className="text-left space-y-2 text-gray-700">
@ -293,7 +293,7 @@ const VpnSetup = () => {
</ul>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Areas We Serve</h3>
<div className="flex flex-wrap justify-center gap-4">
@ -315,10 +315,10 @@ const VpnSetup = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-6">Secure Your Remote Work Today</h2>
<p className="text-xl mb-8 text-green-100">
Get a fast, secure WireGuard VPN setup from Corpus Christi's trusted IT experts.
Get a fast, secure WireGuard VPN setup from Corpus Christi's trusted IT experts.
Protect your business data and enable productive remote work.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Lock className="w-5 h-5 mr-2" />
@ -328,7 +328,7 @@ const VpnSetup = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-green-200">
Free consultation Same-day setup available Local support team
</p>

View File

@ -9,27 +9,27 @@ import { Globe, Mail, Palette, CheckCircle, Smartphone, Search, TrendingUp, User
const WebServices = () => {
useEffect(() => {
document.title = 'Web Services Corpus Christi | Bay Area Affiliates';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Elevate your local business presence—sites, domains, email.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'Web Services Corpus Christi—Professional Business Websites');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Elevate your local business presence—sites, domains, email.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'Web Design Domain Email Portland Coastal Bend SMB');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Elevate your local business presence—sites, domains, email.');
@ -39,7 +39,7 @@ const WebServices = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-emerald-900 via-emerald-800 to-emerald-600 text-white py-20">
<div className="container mx-auto px-4">
@ -72,7 +72,7 @@ const WebServices = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Complete Digital Presence Solutions</h2>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card>
<CardHeader>
@ -93,7 +93,7 @@ const WebServices = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-emerald-100 rounded-full flex items-center justify-center mb-4">
@ -113,7 +113,7 @@ const WebServices = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-emerald-100 rounded-full flex items-center justify-center mb-4">
@ -133,7 +133,7 @@ const WebServices = () => {
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="w-16 h-16 bg-emerald-100 rounded-full flex items-center justify-center mb-4">
@ -163,7 +163,7 @@ const WebServices = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Website Packages for Local SMBs</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-emerald-500">
<h3 className="font-bold text-lg mb-4">Business Starter</h3>
@ -181,7 +181,7 @@ const WebServices = () => {
<div className="text-sm text-gray-500">One-time setup</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-orange-500">
<h3 className="font-bold text-lg mb-4">Professional Plus</h3>
<p className="text-gray-600 mb-4">Advanced features for growing businesses with online sales needs.</p>
@ -198,7 +198,7 @@ const WebServices = () => {
<div className="text-sm text-gray-500">One-time setup</div>
</div>
</div>
<div className="bg-white p-6 rounded-lg shadow border-l-4 border-purple-500">
<h3 className="font-bold text-lg mb-4">Enterprise Solution</h3>
<p className="text-gray-600 mb-4">Comprehensive digital presence for established businesses and organizations.</p>
@ -216,7 +216,7 @@ const WebServices = () => {
</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">Why Local Businesses Choose Our Web Services</h3>
<div className="grid md:grid-cols-2 gap-8">
@ -273,7 +273,7 @@ const WebServices = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Boost Your Local Business Visibility</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="w-20 h-20 bg-emerald-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -282,7 +282,7 @@ const WebServices = () => {
<h3 className="text-xl font-bold mb-2">Get Found Online</h3>
<p className="text-gray-600">Appear at the top of Google searches when Corpus Christi customers look for your services.</p>
</div>
<div className="text-center">
<div className="w-20 h-20 bg-emerald-100 rounded-full flex items-center justify-center mx-auto mb-4">
<Smartphone className="w-10 h-10 text-emerald-600" />
@ -290,7 +290,7 @@ const WebServices = () => {
<h3 className="text-xl font-bold mb-2">Mobile-First Design</h3>
<p className="text-gray-600">Your website looks perfect on phones, tablets, and desktopscritical for local searches.</p>
</div>
<div className="text-center">
<div className="w-20 h-20 bg-emerald-100 rounded-full flex items-center justify-center mx-auto mb-4">
<TrendingUp className="w-10 h-10 text-emerald-600" />
@ -299,7 +299,7 @@ const WebServices = () => {
<p className="text-gray-600">Track website visitors, leads, and customer inquiries with detailed analytics reporting.</p>
</div>
</div>
<div className="bg-emerald-50 p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-6 text-center">Local SEO Success Stories</h3>
<div className="grid md:grid-cols-2 gap-8">
@ -314,7 +314,7 @@ const WebServices = () => {
</div>
<div className="mt-6 text-center">
<p className="text-gray-700">
"Bay Area Affiliates redesigned our website and now we get 3x more customer inquiries from online searches.
"Bay Area Affiliates redesigned our website and now we get 3x more customer inquiries from online searches.
They understand the Corpus Christi market better than anyone."
</p>
<p className="text-emerald-600 font-semibold mt-2"> Local Business Owner</p>
@ -329,7 +329,7 @@ const WebServices = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Professional Web Development Process</h2>
<div className="grid md:grid-cols-4 gap-8">
<Card className="text-center">
<CardHeader>
@ -342,7 +342,7 @@ const WebServices = () => {
<p className="text-sm">In-person consultation to understand your business goals and target customers.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -354,7 +354,7 @@ const WebServices = () => {
<p className="text-sm">Custom website design that reflects your brand and appeals to local customers.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -366,7 +366,7 @@ const WebServices = () => {
<p className="text-sm">Professional coding and testing to ensure optimal performance and security.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -389,10 +389,10 @@ const WebServices = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Serving Coastal Bend Businesses Since 1999</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates has created professional websites for hundreds of local businesses throughout
Bay Area Affiliates has created professional websites for hundreds of local businesses throughout
the Corpus Christi area. We understand what works in our local market.
</p>
<div className="grid md:grid-cols-4 gap-8 mb-12">
<div className="text-center">
<div className="text-4xl font-bold text-emerald-600 mb-2">200+</div>
@ -411,11 +411,11 @@ const WebServices = () => {
<div className="text-gray-600">Support Team</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Web Services Throughout the Coastal Bend</h3>
<p className="text-gray-700 mb-6">
From Portland's waterfront businesses to Kingsville's agricultural services, we create websites
From Portland's waterfront businesses to Kingsville's agricultural services, we create websites
that connect local businesses with their communities and drive real results.
</p>
<div className="flex flex-wrap justify-center gap-4">
@ -437,10 +437,10 @@ const WebServices = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-6">Ready to Grow Your Local Business Online?</h2>
<p className="text-xl mb-8 text-emerald-100">
Get a professional website that attracts local customers and grows your business.
Get a professional website that attracts local customers and grows your business.
Free consultation and custom quote for Coastal Bend SMBs.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Globe className="w-5 h-5 mr-2" />
@ -450,7 +450,7 @@ const WebServices = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-emerald-200">
Free consultation Local market expertise Ongoing support Mobile-first design
</p>

View File

@ -9,27 +9,27 @@ import { Clock, Shield, Zap, CheckCircle, AlertTriangle } from 'lucide-react';
const Windows11Transition = () => {
useEffect(() => {
document.title = 'Windows 11 Upgrade Corpus Christi | Bay Area Affiliates';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Windows 10 support ends Oct 14, 2025—upgrade now for security. Trusted service for Corpus Christi businesses.');
}
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
ogTitle.setAttribute('content', 'Windows 11 Upgrade: Secure Your Business Before Deadline');
}
const ogDescription = document.querySelector('meta[property="og:description"]');
if (ogDescription) {
ogDescription.setAttribute('content', 'Windows 10 support ends Oct 14, 2025—upgrade now for security. Trusted service for Corpus Christi businesses.');
}
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
if (twitterTitle) {
twitterTitle.setAttribute('content', 'Corpus Christi Windows 11 Upgrade—Urgent for Business Security');
}
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
if (twitterDescription) {
twitterDescription.setAttribute('content', 'Windows 10 support ends Oct 14, 2025—upgrade now for security. Trusted service for Corpus Christi businesses.');
@ -39,7 +39,7 @@ const Windows11Transition = () => {
return (
<div className="min-h-screen">
<Navigation />
<main role="main">
<main>
{/* Hero Section */}
<section className="bg-gradient-to-br from-blue-900 via-blue-800 to-blue-600 text-white py-20">
<div className="container mx-auto px-4">
@ -77,11 +77,11 @@ const Windows11Transition = () => {
<h2 className="text-2xl font-bold text-red-800">Windows 10 Support Ends Oct 14, 2025</h2>
</div>
<p className="text-red-700 text-lg">
After this date, Microsoft will no longer provide security updates, bug fixes, or technical support for Windows 10.
After this date, Microsoft will no longer provide security updates, bug fixes, or technical support for Windows 10.
Your business will be vulnerable to security threats and compliance issues.
</p>
</div>
<div className="grid md:grid-cols-2 gap-8">
<Card>
<CardHeader>
@ -106,7 +106,7 @@ const Windows11Transition = () => {
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-green-600">Benefits of Windows 11</CardTitle>
@ -140,7 +140,7 @@ const Windows11Transition = () => {
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">Seamless Migration for Local SMBs</h2>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<Card className="text-center">
<CardHeader>
@ -153,7 +153,7 @@ const Windows11Transition = () => {
<p>We evaluate your current hardware and software for Windows 11 compatibility.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -165,7 +165,7 @@ const Windows11Transition = () => {
<p>Custom migration plan tailored to minimize business disruption.</p>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
@ -178,7 +178,7 @@ const Windows11Transition = () => {
</CardContent>
</Card>
</div>
<div className="bg-blue-50 p-8 rounded-lg">
<h3 className="text-2xl font-bold mb-4">Enhanced Security & Features</h3>
<div className="grid md:grid-cols-2 gap-6">
@ -212,10 +212,10 @@ const Windows11Transition = () => {
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-3xl font-bold mb-8">Trusted by Corpus Christi Businesses for 25+ Years</h2>
<p className="text-xl text-gray-600 mb-8">
Bay Area Affiliates has been helping local businesses navigate technology transitions since 1999.
Bay Area Affiliates has been helping local businesses navigate technology transitions since 1999.
We understand the unique needs of Coastal Bend SMBs.
</p>
<div className="grid md:grid-cols-3 gap-8 mb-12">
<div className="text-center">
<div className="text-4xl font-bold text-blue-600 mb-2">25+</div>
@ -230,7 +230,7 @@ const Windows11Transition = () => {
<div className="text-gray-600">Local Support</div>
</div>
</div>
<div className="bg-white p-8 rounded-lg shadow-lg">
<h3 className="text-2xl font-bold mb-4">Serving the Entire Coastal Bend</h3>
<div className="flex flex-wrap justify-center gap-4">
@ -254,7 +254,7 @@ const Windows11Transition = () => {
<p className="text-xl mb-8 text-blue-100">
Schedule your Windows 11 upgrade assessment today. Protect your business before the October 14, 2025 deadline.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="bg-orange-600 hover:bg-orange-700">
<Clock className="w-5 h-5 mr-2" />
@ -264,7 +264,7 @@ const Windows11Transition = () => {
Call (361) 765-8400
</Button>
</div>
<p className="text-blue-200">
Free consultation Same-day response Local experts
</p>

View File

@ -1,210 +0,0 @@
import { Variants } from 'framer-motion';
// Smooth easing curves
export const easing = {
smooth: [0.6, 0.01, 0.05, 0.95],
snappy: [0.25, 0.46, 0.45, 0.94],
bouncy: [0.68, -0.55, 0.265, 1.55],
elegant: [0.43, 0.13, 0.23, 0.96],
};
// Hero section animations
export const heroVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.15,
delayChildren: 0.3,
},
},
};
export const heroItemVariants: Variants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.8,
ease: easing.elegant,
},
},
};
// Fade in up animation
export const fadeInUp: Variants = {
hidden: {
opacity: 0,
y: 60,
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.7,
ease: easing.elegant,
},
},
};
// Scale in animation
export const scaleIn: Variants = {
hidden: {
opacity: 0,
scale: 0.8,
},
visible: {
opacity: 1,
scale: 1,
transition: {
duration: 0.6,
ease: easing.smooth,
},
},
};
// Slide in from left
export const slideInLeft: Variants = {
hidden: {
opacity: 0,
x: -60,
},
visible: {
opacity: 1,
x: 0,
transition: {
duration: 0.7,
ease: easing.elegant,
},
},
};
// Slide in from right
export const slideInRight: Variants = {
hidden: {
opacity: 0,
x: 60,
},
visible: {
opacity: 1,
x: 0,
transition: {
duration: 0.7,
ease: easing.elegant,
},
},
};
// Stagger container
export const staggerContainer: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.12,
delayChildren: 0.1,
},
},
};
// Stagger item
export const staggerItem: Variants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.5,
ease: easing.elegant,
},
},
};
// Button hover animation
export const buttonHover = {
scale: 1.02,
transition: {
duration: 0.2,
ease: easing.snappy,
},
};
export const buttonTap = {
scale: 0.98,
};
// Card hover animation
export const cardHover = {
y: -8,
transition: {
duration: 0.3,
ease: easing.smooth,
},
};
// Glow effect
export const glowHover = {
boxShadow: '0 0 30px rgba(51, 102, 255, 0.6)',
transition: {
duration: 0.3,
},
};
// Navigation animation
export const navVariants: Variants = {
hidden: {
y: -100,
opacity: 0,
},
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.6,
ease: easing.elegant,
},
},
};
// Page transition
export const pageTransition = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -20 },
transition: { duration: 0.4, ease: easing.elegant },
};
// Parallax scroll effect
export const parallaxScroll = (scrollY: number, factor: number = 0.5) => ({
y: scrollY * factor,
transition: { type: 'tween', ease: 'linear', duration: 0 },
});
// Scroll reveal with intersection observer
export const scrollRevealVariants: Variants = {
hidden: {
opacity: 0,
y: 50,
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.7,
ease: easing.elegant,
},
},
};
// Magnetic button effect (advanced)
export const magneticEffect = (x: number, y: number, strength: number = 0.3) => ({
x: x * strength,
y: y * strength,
transition: {
type: 'spring',
stiffness: 150,
damping: 15,
mass: 0.1,
},
});

View File

@ -1,100 +0,0 @@
import { onCLS, onFCP, onLCP, onTTFB, onINP, type Metric } from 'web-vitals';
function sendToAnalytics(metric: Metric) {
// Log to console in development
if (import.meta.env.DEV) {
console.log('Web Vitals:', {
name: metric.name,
value: metric.value,
rating: metric.rating,
delta: metric.delta,
id: metric.id,
});
}
// Send to Google Analytics if available
if (typeof window !== 'undefined' && (window as any).gtag) {
(window as any).gtag('event', metric.name, {
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
event_category: 'Web Vitals',
event_label: metric.id,
non_interaction: true,
});
}
// Send to custom analytics endpoint if needed
if (import.meta.env.PROD && import.meta.env.VITE_ANALYTICS_ENDPOINT) {
fetch(import.meta.env.VITE_ANALYTICS_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
metric: metric.name,
value: metric.value,
rating: metric.rating,
id: metric.id,
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent,
}),
keepalive: true,
}).catch((error) => {
console.error('Failed to send web vitals:', error);
});
}
}
export function reportWebVitals() {
// Core Web Vitals
onCLS(sendToAnalytics);
onLCP(sendToAnalytics);
onINP(sendToAnalytics); // Replaces deprecated FID
// Other important metrics
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
}
// Performance observer for additional metrics
export function observePerformance() {
if (typeof window === 'undefined' || !('PerformanceObserver' in window)) {
return;
}
// Observe long tasks (blocking the main thread)
try {
const longTaskObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.warn('Long Task detected:', {
duration: entry.duration,
startTime: entry.startTime,
name: entry.name,
});
}
}
});
longTaskObserver.observe({ entryTypes: ['longtask'] });
} catch (e) {
// Long task API not supported
}
// Observe layout shifts
try {
const layoutShiftObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if ((entry as any).hadRecentInput) continue;
const value = (entry as any).value;
if (value > 0.1) {
console.warn('Large Layout Shift:', {
value,
startTime: entry.startTime,
sources: (entry as any).sources,
});
}
}
});
layoutShiftObserver.observe({ entryTypes: ['layout-shift'] });
} catch (e) {
// Layout shift API not supported
}
}