2026-05-17 21:37:42 -05:00
|
|
|
import { Helmet } from 'react-helmet-async'
|
|
|
|
|
import { Link } from 'react-router-dom'
|
2026-05-28 00:41:24 -05:00
|
|
|
import { ArrowRight, Building2, Compass, Headphones, Home, LifeBuoy, Network, ShieldCheck } from 'lucide-react'
|
|
|
|
|
|
|
|
|
|
const recoveryLinks = [
|
|
|
|
|
{
|
|
|
|
|
title: 'Services',
|
|
|
|
|
description: 'Communications, support, cabling, wireless, and networking.',
|
|
|
|
|
href: '/services',
|
|
|
|
|
icon: Network,
|
|
|
|
|
accent: 'text-primary-cyan',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: 'Industries',
|
|
|
|
|
description: 'Solutions by business environment.',
|
|
|
|
|
href: '/industries',
|
|
|
|
|
icon: Building2,
|
|
|
|
|
accent: 'text-teal-300',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: 'Support',
|
|
|
|
|
description: 'Help for live systems, users, and endpoints.',
|
|
|
|
|
href: '/support',
|
|
|
|
|
icon: LifeBuoy,
|
|
|
|
|
accent: 'text-amber-300',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const signalPoints = [
|
|
|
|
|
{ label: 'Route check', value: 'Active', icon: Compass },
|
|
|
|
|
{ label: 'Partner desk', value: 'Online', icon: Headphones },
|
|
|
|
|
{ label: 'Uptime focus', value: 'Locked', icon: ShieldCheck },
|
|
|
|
|
]
|
2026-05-17 21:37:42 -05:00
|
|
|
|
|
|
|
|
export default function NotFound() {
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<Helmet>
|
|
|
|
|
<title>Page Not Found | Queue North Technologies</title>
|
2026-05-28 00:41:24 -05:00
|
|
|
<meta name="description" content="The Queue North page you requested could not be found. Return home, explore services, or contact our team for help." />
|
|
|
|
|
<meta name="robots" content="noindex, follow" />
|
2026-05-17 21:37:42 -05:00
|
|
|
</Helmet>
|
2026-05-28 00:41:24 -05:00
|
|
|
|
|
|
|
|
<section className="relative isolate overflow-hidden bg-primary-navy text-white">
|
2026-05-26 12:22:11 -05:00
|
|
|
<div className="absolute inset-0 -z-10">
|
|
|
|
|
<img
|
2026-05-28 00:41:24 -05:00
|
|
|
src="/assets/about-image.webp"
|
2026-05-26 12:22:11 -05:00
|
|
|
alt=""
|
2026-05-28 00:41:24 -05:00
|
|
|
className="h-full w-full object-cover object-[66%_top] md:object-[62%_top]"
|
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"
|
2026-05-26 12:22:11 -05:00
|
|
|
/>
|
2026-05-28 00:41:24 -05:00
|
|
|
<div className="absolute inset-0 bg-primary-navy/86" />
|
|
|
|
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_72%_22%,rgba(34,211,238,0.28),transparent_28%),radial-gradient(circle_at_18%_74%,rgba(245,158,11,0.16),transparent_24%),linear-gradient(115deg,#071A2A_0%,rgba(11,42,60,0.96)_46%,rgba(7,26,42,0.74)_100%)]" />
|
|
|
|
|
<div className="absolute inset-0 opacity-[0.13] [background-image:linear-gradient(rgba(255,255,255,0.12)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.12)_1px,transparent_1px)] [background-size:48px_48px]" />
|
|
|
|
|
<div className="absolute left-0 right-0 top-0 h-px bg-gradient-to-r from-transparent via-primary-cyan/70 to-transparent" />
|
2026-05-26 12:22:11 -05:00
|
|
|
</div>
|
2026-05-28 00:41:24 -05:00
|
|
|
|
|
|
|
|
<div className="mx-auto grid min-h-[calc(100vh-4rem)] max-w-7xl grid-cols-[minmax(0,1fr)] items-center gap-12 px-4 py-16 sm:px-6 md:py-20 lg:grid-cols-[minmax(0,1.03fr)_minmax(0,0.97fr)] lg:px-8 lg:py-24">
|
|
|
|
|
<div className="min-w-0 w-full max-w-[calc(100vw-2rem)] sm:max-w-3xl">
|
|
|
|
|
<div className="inline-flex items-center gap-2 rounded-md border border-white/[0.15] bg-white/10 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-primary-cyan shadow-[0_0_40px_rgba(34,211,238,0.18)] backdrop-blur">
|
|
|
|
|
<Compass className="h-4 w-4" aria-hidden="true" />
|
|
|
|
|
Route recalibration
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<p className="mt-8 font-numeric text-8xl leading-none text-white sm:text-9xl md:text-[10rem]">
|
|
|
|
|
404
|
|
|
|
|
</p>
|
|
|
|
|
<h1 className="mt-4 max-w-2xl text-3xl font-bold leading-tight text-white sm:text-5xl lg:text-6xl">
|
|
|
|
|
This route dropped <span className="block sm:inline">off the network.</span>
|
|
|
|
|
</h1>
|
|
|
|
|
<p className="mt-6 max-w-2xl text-base leading-relaxed text-white/75 sm:text-lg md:text-xl">
|
|
|
|
|
That page is gone or renamed. We'll get you back to a live connection.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<div className="mt-8 flex flex-col gap-3 sm:flex-row">
|
|
|
|
|
<Link to="/" className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-md bg-white px-5 text-sm font-semibold text-primary-navy transition-colors hover:bg-section-alt sm:w-auto" aria-label="Return to the Queue North home page">
|
|
|
|
|
Back to Home
|
|
|
|
|
<Home className="h-4 w-4" aria-hidden="true" />
|
|
|
|
|
</Link>
|
|
|
|
|
<Link to="/contact#contact-form" className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-md border border-white/35 px-5 text-sm font-semibold text-white transition-colors hover:bg-white/10 sm:w-auto" aria-label="Contact Queue North Technologies">
|
|
|
|
|
Talk to Us
|
|
|
|
|
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
|
|
|
|
</Link>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="mt-10 grid gap-3 sm:grid-cols-3">
|
|
|
|
|
{signalPoints.map((point) => {
|
|
|
|
|
const Icon = point.icon
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div key={point.label} className="rounded-md border border-white/10 bg-white/[0.06] p-4 backdrop-blur">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className="flex h-9 w-9 items-center justify-center rounded-md bg-white/10 text-primary-cyan">
|
|
|
|
|
<Icon className="h-4 w-4" aria-hidden="true" />
|
|
|
|
|
</span>
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-xs uppercase tracking-wide text-white/50">{point.label}</p>
|
|
|
|
|
<p className="text-sm font-semibold text-white">{point.value}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
2026-05-26 12:22:11 -05:00
|
|
|
</div>
|
2026-05-28 00:41:24 -05:00
|
|
|
|
|
|
|
|
<div className="relative min-w-0 w-full max-w-[calc(100vw-2rem)] lg:max-w-none">
|
|
|
|
|
<div className="absolute inset-0 rounded-md bg-primary-cyan/10 blur-3xl sm:-inset-4 sm:rounded-[2rem]" aria-hidden="true" />
|
|
|
|
|
<div className="relative overflow-hidden rounded-md border border-white/[0.12] bg-white/[0.07] shadow-2xl shadow-black/30 backdrop-blur-xl">
|
|
|
|
|
<div className="border-b border-white/10 px-5 py-4">
|
|
|
|
|
<div className="flex items-center justify-between gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary-cyan">Recovery paths</p>
|
|
|
|
|
<p className="mt-1 text-sm text-white/60">Choose the cleanest next hop.</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-primary-cyan text-primary-navy shadow-[0_0_28px_rgba(34,211,238,0.45)]">
|
|
|
|
|
<Compass className="h-5 w-5" aria-hidden="true" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="relative p-5 sm:p-6">
|
|
|
|
|
<div className="pointer-events-none absolute inset-x-6 top-1/2 h-px bg-gradient-to-r from-transparent via-primary-cyan/50 to-transparent" aria-hidden="true" />
|
|
|
|
|
<div className="pointer-events-none absolute left-1/2 top-6 bottom-6 w-px bg-gradient-to-b from-transparent via-white/20 to-transparent" aria-hidden="true" />
|
|
|
|
|
|
|
|
|
|
<div className="mb-5 rounded-md border border-primary-cyan/25 bg-primary-navy/80 p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]">
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
<div className="relative flex h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-md border border-primary-cyan/35 bg-primary-cyan/10">
|
|
|
|
|
<span className="absolute h-9 w-9 animate-ping rounded-full bg-primary-cyan/20" aria-hidden="true" />
|
|
|
|
|
<Compass className="relative h-7 w-7 text-primary-cyan" aria-hidden="true" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="min-w-0">
|
|
|
|
|
<p className="text-sm font-semibold text-white">Queue North core</p>
|
|
|
|
|
<p className="mt-1 text-sm leading-relaxed text-white/60 break-words">Systems and support are still fully reachable.</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid gap-3">
|
|
|
|
|
{recoveryLinks.map((item) => {
|
|
|
|
|
const Icon = item.icon
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Link
|
|
|
|
|
key={item.title}
|
|
|
|
|
to={item.href}
|
|
|
|
|
className="group rounded-md border border-white/10 bg-white/[0.06] p-4 transition-all hover:-translate-y-0.5 hover:border-primary-cyan/45 hover:bg-white/[0.1] hover:no-underline"
|
|
|
|
|
>
|
|
|
|
|
<div className="flex items-start gap-4">
|
|
|
|
|
<span className="flex h-11 w-11 shrink-0 items-center justify-center rounded-md bg-white/10">
|
|
|
|
|
<Icon className={`h-5 w-5 ${item.accent}`} aria-hidden="true" />
|
|
|
|
|
</span>
|
|
|
|
|
<div className="min-w-0 flex-1">
|
|
|
|
|
<div className="flex items-center justify-between gap-3">
|
|
|
|
|
<h2 className="text-base font-semibold text-white">{item.title}</h2>
|
|
|
|
|
<ArrowRight className="h-4 w-4 shrink-0 text-white/35 transition-transform group-hover:translate-x-1 group-hover:text-primary-cyan" aria-hidden="true" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="mt-1 text-sm leading-relaxed text-white/60 break-words">{item.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Link>
|
|
|
|
|
)
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-17 21:37:42 -05:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</>
|
|
|
|
|
)
|
|
|
|
|
}
|