Queue-North-Website/src/pages/Home.jsx

385 lines
20 KiB
React
Raw Normal View History

2026-05-18 13:55:06 -05:00
import SEO from '@/components/SEO'
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
import { websiteLd } from '@/lib/seo'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/Card'
2026-05-12 01:04:17 -05:00
import { services } from '@/data/services'
import { industries } from '@/data/industries'
import { Link } from 'react-router-dom'
2026-05-26 13:36:16 -05:00
import { ArrowRight, MapPin, MessageCircle, Users, LifeBuoy, GraduationCap, Link as LinkIcon, Wifi, Network, Headphones, UserCheck, Activity, ShieldCheck, HeartPulse, ShoppingCart, Factory, Landmark, Building2 } from 'lucide-react'
// Icon map for industries - converts icon string to lucide component
const industryIcons = {
'heart-pulse': HeartPulse,
'shopping-cart': ShoppingCart,
factory: Factory,
landmark: Landmark,
}
2026-05-12 01:04:17 -05:00
2026-05-25 18:33:19 -05:00
const serviceAccentStyles = [
{
card: 'border-t-[3px] border-t-primary-blue hover:border-primary-blue/50',
iconWrap: 'bg-sky-50',
icon: 'text-primary-blue',
link: 'text-primary-blue hover:text-primary-navy',
},
{
card: 'border-t-[3px] border-t-teal-500 hover:border-teal-500/50',
iconWrap: 'bg-teal-50',
icon: 'text-teal-600',
link: 'text-teal-700 hover:text-primary-navy',
},
{
card: 'border-t-[3px] border-t-cyan-400 hover:border-cyan-400/60',
iconWrap: 'bg-cyan-50',
icon: 'text-cyan-600',
link: 'text-cyan-700 hover:text-primary-navy',
},
{
card: 'border-t-[3px] border-t-accent-gold hover:border-accent-gold/60',
iconWrap: 'bg-amber-50',
icon: 'text-amber-600',
link: 'text-amber-700 hover:text-primary-navy',
},
]
const industryAccentStyles = [
{ card: 'border-t-[3px] border-t-rose-400 hover:border-rose-400/60', iconWrap: 'bg-rose-50', icon: 'text-rose-600' },
{ card: 'border-t-[3px] border-t-primary-blue hover:border-primary-blue/50', iconWrap: 'bg-sky-50', icon: 'text-primary-blue' },
{ card: 'border-t-[3px] border-t-accent-gold hover:border-accent-gold/60', iconWrap: 'bg-amber-50', icon: 'text-amber-600' },
{ card: 'border-t-[3px] border-t-teal-500 hover:border-teal-500/50', iconWrap: 'bg-teal-50', icon: 'text-teal-600' },
]
2026-05-12 01:04:17 -05:00
const Home = () => {
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
// Single Organization entity — no LocalBusiness/address markup, since Queue North
// does not publish a physical location.
const organizationLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
'@id': 'https://queuenorth.com/#organization',
name: 'Queue North Technologies',
url: 'https://queuenorth.com',
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
image: 'https://queuenorth.com/assets/og-image.png',
telephone: '+1-321-730-8020',
2026-05-18 14:02:12 -05:00
logo: {
'@type': 'ImageObject',
url: 'https://queuenorth.com/logo.png',
},
2026-05-27 14:43:50 -05:00
description: 'Veteran-owned 8x8 Certified Partner and Cisco Certified Partner providing business phone systems, UCaaS, contact center, IT support, and networking solutions.',
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
areaServed: {
'@type': 'Country',
name: 'United States',
},
2026-05-18 14:02:12 -05:00
contactPoint: [
{
'@type': 'ContactPoint',
telephone: '+1-321-730-8020',
contactType: 'customer service',
areaServed: 'US',
},
{
'@type': 'ContactPoint',
telephone: '+1-888-656-2850',
contactType: 'customer service',
contactOption: 'TollFree',
areaServed: 'US',
},
],
sameAs: [
'https://www.linkedin.com/company/queue-north-technologies-llc',
'https://www.facebook.com/QueueNorth',
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
'https://www.instagram.com/queue_north/',
2026-05-18 14:02:12 -05:00
],
}
2026-05-12 01:04:17 -05:00
return (
<>
2026-05-18 13:55:06 -05:00
<SEO
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
title="Business Phone, UCaaS & IT Services | Queue North"
description="Veteran-owned 8x8 and Cisco Certified Partner delivering business phone systems, UCaaS, contact center, IT support, and networking. 25+ years."
2026-05-18 13:55:06 -05:00
url="https://queuenorth.com"
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
jsonLd={[organizationLd, websiteLd]}
2026-05-18 13:55:06 -05:00
/>
2026-05-12 01:04:17 -05:00
{/* Hero Section */}
<section className="relative isolate overflow-hidden bg-primary-navy text-white">
<div className="absolute inset-0 -z-10">
<img
src="/assets/hero-tech.webp"
alt="Queue North technician working inside a communications rack"
className="h-full w-full object-cover object-center"
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
loading="eager"
fetchPriority="high"
decoding="async"
/>
<div className="absolute inset-0 bg-primary-navy/65" />
<div className="absolute inset-0 bg-gradient-to-r from-primary-navy via-primary-navy/90 to-primary-navy/25" />
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 md:py-24 lg:py-28">
2026-05-26 12:26:13 -05:00
<div className="max-w-5xl">
<div className="inline-flex items-center gap-2 rounded-md border border-white/20 bg-white/10 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-primary-cyan">
Veteran-owned communications and infrastructure partner
2026-05-12 01:04:17 -05:00
</div>
2026-05-26 12:31:26 -05:00
<h1 className="mt-6 max-w-4xl text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold leading-[1.05]">
Business communications built for uptime, support, and accountability.
</h1>
<p className="mt-6 text-lg md:text-2xl text-white/80 max-w-2xl leading-relaxed">
Business phone, contact center, network, and IT support built around one accountable implementation partner.
</p>
<div className="mt-8 flex flex-col sm:flex-row gap-3">
2026-05-27 22:33:54 -05:00
<Link to="/contact#contact-form" className="inline-flex w-full sm:w-auto h-11 items-center justify-center gap-2 rounded-md bg-white px-5 text-sm font-semibold text-primary-navy hover:bg-section-alt transition-colors" aria-label="Schedule a consultation">
Schedule Consultation
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
2026-05-27 12:42:25 -05:00
<Link to="/services" className="inline-flex w-full sm:w-auto h-11 items-center justify-center rounded-md border border-white/45 px-5 text-sm font-semibold text-white hover:bg-white/10 transition-colors" aria-label="View our services">
View Services
</Link>
2026-05-12 01:04:17 -05:00
</div>
</div>
</div>
</section>
{/* Partner Proof */}
<section className="bg-white border-b border-border py-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
2026-05-27 12:51:08 -05:00
<div className="grid grid-cols-2 gap-x-4 gap-y-6 lg:grid-cols-4">
<div className="flex flex-col items-center gap-2 lg:flex-row lg:gap-3 lg:justify-start">
2026-05-27 13:28:13 -05:00
<span className="flex h-16 w-20 shrink-0 items-center justify-center rounded-md border border-border bg-white p-2">
2026-05-25 18:11:51 -05:00
<img
src="/assets/brand/8x8-logo-dark-gray.png"
alt="8x8 Certified Partner logo"
2026-05-27 13:28:13 -05:00
className="h-full w-full object-contain"
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
loading="lazy"
decoding="async"
2026-05-25 18:11:51 -05:00
/>
</span>
2026-05-27 12:51:08 -05:00
<span className="text-sm font-semibold leading-tight text-primary-navy text-center lg:text-left">8x8 Certified Partner</span>
</div>
2026-05-27 12:51:08 -05:00
<div className="flex flex-col items-center gap-2 lg:flex-row lg:gap-3 lg:justify-start">
2026-05-27 21:35:12 -05:00
<span className="flex h-16 w-20 shrink-0 items-center justify-center rounded-md border border-border bg-white p-1 overflow-hidden">
2026-05-25 18:11:51 -05:00
<img
2026-05-26 12:22:11 -05:00
src="/assets/brand/cisco-partner-logo-midnight.svg"
alt="Cisco Partner certification logo"
2026-05-27 21:35:12 -05:00
className="h-full w-full object-contain scale-[1.5]"
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
loading="lazy"
decoding="async"
2026-05-25 18:11:51 -05:00
/>
</span>
2026-05-27 12:51:08 -05:00
<span className="text-sm font-semibold leading-tight text-primary-navy text-center lg:text-left">Cisco Certified Partner</span>
</div>
2026-05-27 12:51:08 -05:00
<div className="flex flex-col items-center gap-2 lg:flex-row lg:gap-3 lg:justify-start">
<span className="flex h-16 w-20 shrink-0 items-center justify-center rounded-md border border-border bg-white p-1">
<img
src="/assets/brand/veteran-owned-certified-mark.webp"
alt="SBA logo for Veteran-Owned Certified"
className="h-full w-full object-contain"
feat(seo): publish privacy policy, remove street address, prerender all routes (batch 0.9.3) Client directive (Levi Halford, 2026-08-01) ahead of Google/Meta lead forms. Privacy policy: - Publish approved policy verbatim at /privacy-policy (src/data/privacyPolicy.js is the single source of truth; 292/292 source lines verified present) - Privacy Policy link in the footer of every page - Effective/Last Updated 2026-07-31, privacy@queuenorth.com as mailto Remove St. Petersburg street address from every surface named in the brief: footer, contact page, schema markup, SEO metadata, Google Maps links. Collapse ProfessionalService + Organization schema into a single Organization with areaServed: United States; drop geo coordinates, priceRange, openingHours. Add the approved US-coverage sentence to About. No replacement address. Crawler visibility (the site previously served 0 bytes of body HTML without JS): - Prerender all 19 routes at build time via src/entry-server.jsx + scripts/prerender.js - Hoist title/meta/canonical/JSON-LD into <head>; renderToString does not do this and react-helmet-async's context is empty under React 19 - Serve prerendered HTML; return a real 404 for unknown paths instead of 200 - Hydrate instead of discarding the prerendered markup SEO/perf: - Titles <=60 and descriptions <=160 chars across all pages - Add BreadcrumbList to interior pages, WebSite to home - Generate sitemap.xml from the route list with git-derived lastmod - 301 duplicate URL forms (trailing slash, //, /index.html), preserving query - Immutable caching for content-hashed assets; no-cache for HTML - Split the 522 KB bundle into app/react-vendor/router/icons - loading/decoding/fetchpriority + per-route hero preload; drop unused asset Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 01:45:52 -05:00
loading="lazy"
decoding="async"
/>
</span>
<span className="text-sm font-semibold leading-tight text-primary-navy text-center lg:text-left">Veteran-Owned Certified</span>
</div>
2026-05-27 12:51:08 -05:00
<div className="flex flex-col items-center gap-2 lg:flex-row lg:gap-3 lg:justify-start">
2026-05-27 13:28:13 -05:00
<span className="font-numeric flex h-16 w-20 shrink-0 items-center justify-center rounded-md border border-border bg-white text-2xl font-semibold text-primary-navy">
2026-05-26 13:36:16 -05:00
25+
</span>
2026-05-27 12:51:08 -05:00
<span className="text-sm font-semibold leading-tight text-primary-navy text-center lg:text-left">Years Experience</span>
</div>
</div>
</div>
</section>
2026-05-12 01:04:17 -05:00
{/* Services Section */}
<section className="bg-background py-16 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
2026-05-12 01:04:17 -05:00
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold text-primary-navy mb-4">What We Handle</h2>
2026-05-12 01:04:17 -05:00
<p className="text-xl text-soft-text max-w-2xl mx-auto">
From phones to firewalls, we keep your business running
2026-05-12 01:04:17 -05:00
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
2026-05-25 18:33:19 -05:00
{services.map((service, index) => {
const accent = serviceAccentStyles[index % serviceAccentStyles.length]
return (
<Card key={service.id} className={`h-full transition-all hover:shadow-md ${accent.card}`}>
<CardHeader>
<div className="flex items-center gap-3 mb-2">
<div className={`${accent.iconWrap} p-2 rounded-md`} aria-hidden="true">
{service.icon === 'message-circle' && <MessageCircle className={`w-6 h-6 ${accent.icon}`} />}
{service.icon === 'users' && <Users className={`w-6 h-6 ${accent.icon}`} />}
{service.icon === 'life-buoy' && <LifeBuoy className={`w-6 h-6 ${accent.icon}`} />}
{service.icon === 'graduation-cap' && <GraduationCap className={`w-6 h-6 ${accent.icon}`} />}
{service.icon === 'link' && <LinkIcon className={`w-6 h-6 ${accent.icon}`} />}
{service.icon === 'wifi' && <Wifi className={`w-6 h-6 ${accent.icon}`} />}
{service.icon === 'network' && <Network className={`w-6 h-6 ${accent.icon}`} />}
</div>
<CardTitle className="text-left text-primary-navy text-xl" aria-label={service.name}>{service.name}</CardTitle>
</div>
<CardDescription className="font-medium text-teal-900" aria-label={service.homeDesc}>{service.homeDesc}</CardDescription>
</CardHeader>
<CardContent>
<div className="flex flex-col gap-2">
<Link to={`/services/${service.id}`} className={`inline-flex items-center gap-1 text-sm font-semibold ${accent.link}`} aria-label={`Learn more about ${service.name}`}>
Learn more
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
2026-05-25 18:33:19 -05:00
</CardContent>
</Card>
)
})}
2026-05-12 01:04:17 -05:00
</div>
</div>
</section>
{/* Why Queue North */}
<section className="bg-section-alt py-16 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
2026-05-12 01:04:17 -05:00
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold text-primary-navy mb-4">Why Queue North</h2>
<p className="text-xl text-soft-text max-w-2xl mx-auto mb-6">
Four concrete differentiators that set us apart
2026-05-12 01:04:17 -05:00
</p>
<div>
2026-05-27 22:33:54 -05:00
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors" aria-label="Request a consultation">
Request Consultation
</Link>
</div>
2026-05-12 01:04:17 -05:00
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
2026-05-25 18:33:19 -05:00
<Card className="bg-white/80 border-t-[3px] border-t-primary-blue">
<CardContent className="pt-6">
2026-05-25 18:33:19 -05:00
<div className="bg-sky-50 text-primary-blue p-3 rounded-md mb-4 inline-flex">
<Headphones className="w-6 h-6" />
</div>
<h3 className="text-lg font-semibold text-primary-navy mb-2">Responsiveness</h3>
<p className="text-sm text-soft-text">
When you call, a human answers not a ticket queue, not a chatbot. Real support from people who know your system.
</p>
</CardContent>
</Card>
2026-05-25 18:33:19 -05:00
<Card className="bg-white/80 border-t-[3px] border-t-teal-500">
<CardContent className="pt-6">
2026-05-25 18:33:19 -05:00
<div className="bg-teal-50 text-teal-600 p-3 rounded-md mb-4 inline-flex">
<UserCheck className="w-6 h-6" />
</div>
<h3 className="text-lg font-semibold text-primary-navy mb-2">Direct Support</h3>
<p className="text-sm text-soft-text">
No account managers between you and the engineer solving your problem. You talk to the team that designed and deployed your system.
2026-05-12 01:04:17 -05:00
</p>
</CardContent>
</Card>
2026-05-25 18:33:19 -05:00
<Card className="bg-white/80 border-t-[3px] border-t-cyan-400">
<CardContent className="pt-6">
2026-05-25 18:33:19 -05:00
<div className="bg-cyan-50 text-cyan-600 p-3 rounded-md mb-4 inline-flex">
<Activity className="w-6 h-6" />
</div>
<h3 className="text-lg font-semibold text-primary-navy mb-2">Proactive Monitoring</h3>
<p className="text-sm text-soft-text">
We catch problems before you notice them. 24/7 monitoring means we're often resolving issues before your phone rings.
2026-05-12 01:04:17 -05:00
</p>
</CardContent>
</Card>
2026-05-25 18:33:19 -05:00
<Card className="bg-white/80 border-t-[3px] border-t-accent-gold">
<CardContent className="pt-6">
2026-05-25 18:33:19 -05:00
<div className="bg-amber-50 text-amber-600 p-3 rounded-md mb-4 inline-flex">
<ShieldCheck className="w-6 h-6" />
</div>
<h3 className="text-lg font-semibold text-primary-navy mb-2">Vendor Neutrality</h3>
<p className="text-sm text-soft-text">
As an 8x8 and Cisco Certified Partner, we recommend what works best for you not what pays the highest commission. We've tested the alternatives so you don't have to.
2026-05-12 01:04:17 -05:00
</p>
</CardContent>
</Card>
</div>
</div>
</section>
{/* Industries Section */}
<section className="bg-background py-16 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
2026-05-12 01:04:17 -05:00
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold text-primary-navy mb-4">Industries We Serve</h2>
<p className="text-xl text-soft-text max-w-2xl mx-auto">
Tailored solutions for healthcare, retail, manufacturing, and more
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
2026-05-25 18:33:19 -05:00
{industries.map((industry, index) => {
const IconComponent = industryIcons[industry.icon] || Building2
2026-05-25 18:33:19 -05:00
const accent = industryAccentStyles[index % industryAccentStyles.length]
return (
2026-05-25 18:33:19 -05:00
<Card key={industry.id} className={`h-full transition-all hover:shadow-md ${accent.card}`}>
<CardContent className="p-6">
2026-05-25 18:33:19 -05:00
<div className={`w-14 h-14 rounded-md ${accent.iconWrap} flex items-center justify-center mb-4`} aria-hidden="true">
<IconComponent className={`w-8 h-8 ${accent.icon}`} />
</div>
2026-05-25 18:33:19 -05:00
<h3 className="text-left text-xl font-semibold text-primary-navy mb-3" aria-label={industry.name}>{industry.name}</h3>
<p className="text-sm text-soft-text mb-4" aria-label={industry.homeDesc || 'Industry-specific solutions designed to address your unique challenges and requirements.'}>{industry.homeDesc || 'Industry-specific solutions designed to address your unique challenges and requirements.'}</p>
<Link to={`/industries/${industry.id}`} className="inline-flex items-center gap-1 text-sm font-semibold text-primary-navy hover:text-primary-blue" aria-label={`Learn more about ${industry.name} industry solutions`}>
Learn more
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</CardContent>
</Card>
)
})}
2026-05-12 01:04:17 -05:00
</div>
</div>
</section>
{/* Final CTA - Free Migration Offer */}
<section className="bg-section-alt py-16 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h2 className="text-3xl md:text-4xl font-bold text-primary-navy mb-6">
What we'll help you do
2026-05-12 01:04:17 -05:00
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
2026-05-26 17:08:55 -05:00
<div className="flex items-start gap-3 justify-center sm:justify-start">
<div className="bg-primary-navy/10 p-2 rounded-md flex-shrink-0">
<MapPin className="w-5 h-5 text-primary-navy" />
</div>
2026-05-26 17:08:55 -05:00
<p className="text-center sm:text-left text-sm md:text-base text-soft-text">
Identify the features you actually need
</p>
</div>
2026-05-26 17:08:55 -05:00
<div className="flex items-start gap-3 justify-center sm:justify-start">
<div className="bg-teal-500/10 p-2 rounded-md flex-shrink-0">
<Users className="w-5 h-5 text-teal-600" />
</div>
2026-05-26 17:08:55 -05:00
<p className="text-center sm:text-left text-sm md:text-base text-soft-text">
Align solutions with operations and budget
</p>
</div>
2026-05-26 17:08:55 -05:00
<div className="flex items-start gap-3 justify-center sm:justify-start">
<div className="bg-teal-500/10 p-2 rounded-md flex-shrink-0">
<Network className="w-5 h-5 text-teal-600" />
</div>
2026-05-26 17:08:55 -05:00
<p className="text-center sm:text-left text-sm md:text-base text-soft-text">
Plan deployment, migration, and training
</p>
</div>
2026-05-26 17:08:55 -05:00
<div className="flex items-start gap-3 justify-center sm:justify-start">
<div className="bg-teal-600/10 p-2 rounded-md flex-shrink-0">
<ShieldCheck className="w-5 h-5 text-teal-600" />
</div>
2026-05-26 17:08:55 -05:00
<p className="text-center sm:text-left text-sm md:text-base text-teal-700 font-semibold">
Ask how you qualify for our free migration
</p>
</div>
</div>
<p className="text-lg text-soft-text mb-8 max-w-2xl mx-auto">
Share a few details and we'll provide clear direction.
2026-05-12 01:04:17 -05:00
</p>
2026-05-27 22:33:54 -05:00
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-6 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors" aria-label="Request a consultation">
2026-05-12 01:04:17 -05:00
Request Consultation
</Link>
2026-05-12 01:04:17 -05:00
</div>
</section>
</>
2026-05-12 01:04:17 -05:00
)
}
export default Home