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 { buildBreadcrumbLd } from '@/lib/seo'
2026-05-12 01:04:17 -05:00
import { industries } from '@/data/industries'
2026-05-18 14:25:45 -05:00
import { ArrowRight , Building2 , CheckCircle2 , HeartPulse , ShoppingCart , Factory , Landmark } from 'lucide-react'
2026-05-17 17:42:03 -05:00
import { Link } from 'react-router-dom'
2026-05-17 21:41:01 -05:00
2026-05-18 14:25:45 -05:00
const iconMap = {
2026-05-17 21:41:01 -05:00
'heart-pulse' : HeartPulse ,
'shopping-cart' : ShoppingCart ,
2026-05-18 14:25:45 -05:00
'factory' : Factory ,
'landmark' : Landmark ,
2026-05-17 21:41:01 -05:00
}
2026-05-12 01:04:17 -05:00
2026-05-26 12:22:11 -05:00
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 Industries = ( ) => {
return (
2026-05-17 17:11:29 -05:00
< >
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 = "Industries We Serve | Queue North"
2026-05-18 13:55:06 -05:00
description = "Queue North Technologies serves healthcare, retail, manufacturing, education, and finance industries with tailored communications and IT solutions."
url = "https://queuenorth.com/industries"
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 = { buildBreadcrumbLd ( [ { name : 'Industries' , path : '/industries' } ] ) }
2026-05-18 13:55:06 -05:00
/ >
2026-05-18 14:25:45 -05:00
{ /* Hero */ }
2026-05-26 12:22:11 -05:00
< section className = "relative isolate overflow-hidden bg-primary-navy py-16 lg:py-24 text-white" >
< div className = "absolute inset-0 -z-10" >
< img
src = "/assets/local-networking.webp"
alt = "Network infrastructure used by businesses across industries"
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"
2026-05-26 12:22:11 -05:00
/ >
< div className = "absolute inset-0 bg-primary-navy/82" / >
< div className = "absolute inset-0 bg-gradient-to-r from-primary-navy via-primary-navy/92 to-primary-navy/45" / >
< / div >
2026-05-17 17:11:29 -05:00
< div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
2026-05-18 14:25:45 -05:00
< div className = "max-w-3xl" >
< 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 mb-6" >
< Building2 className = "h-4 w-4" aria - hidden = "true" / >
Industries
< / div >
< h1 className = "text-4xl md:text-5xl lg:text-6xl font-bold leading-tight mb-6" >
Built around how your industry actually works .
< / h1 >
< p className = "text-lg md:text-xl text-white/75 max-w-2xl leading-relaxed mb-8" >
Communications and infrastructure solutions shaped by the compliance , workflow , and connectivity demands of your specific environment .
2026-05-17 17:11:29 -05:00
< / p >
2026-05-18 14:25:45 -05:00
< Link
2026-05-27 22:33:54 -05:00
to = "/contact#contact-form"
2026-05-18 14:25:45 -05:00
className = "inline-flex 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"
>
Talk to a Specialist
< ArrowRight className = "h-4 w-4" aria - hidden = "true" / >
< / Link >
2026-05-17 17:11:29 -05:00
< / div >
< / div >
2026-05-12 01:04:17 -05:00
< / section >
{ /* Industries Grid */ }
2026-05-26 12:22:11 -05:00
< section className = "bg-background py-16 lg:py-24" >
2026-05-17 17:11:29 -05:00
< div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
2026-05-26 12:22:11 -05:00
< div className = "mb-10 max-w-3xl" >
< p className = "text-sm font-semibold uppercase tracking-wide text-primary-blue" > Industry fit < / p >
< h2 className = "mt-3 text-3xl md:text-4xl font-bold text-primary-navy" > Communications needs change by environment . < / h2 >
< p className = "mt-4 text-lg text-soft-text" >
We adapt platform , network , and support recommendations to the way your teams actually operate .
< / p >
< / div >
2026-05-18 13:45:39 -05:00
< div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
2026-05-26 12:22:11 -05:00
{ industries . map ( ( industry , index ) => {
2026-05-18 14:25:45 -05:00
const Icon = iconMap [ industry . icon ] || Building2
2026-05-26 12:22:11 -05:00
const accent = industryAccentStyles [ index % industryAccentStyles . length ]
2026-05-17 21:41:01 -05:00
return (
2026-05-18 14:25:45 -05:00
< div
key = { industry . id }
2026-05-26 12:22:11 -05:00
className = { ` group flex flex-col rounded-md border border-border bg-white p-8 shadow-sm hover:shadow-md transition-all ${ accent . card } ` }
2026-05-18 14:25:45 -05:00
>
2026-05-26 12:22:11 -05:00
< div className = { ` flex h-14 w-14 items-center justify-center rounded-md ${ accent . iconWrap } mb-6 flex-shrink-0 ` } >
< Icon className = { ` h-7 w-7 ${ accent . icon } ` } aria - hidden = "true" / >
2026-05-12 02:45:25 -05:00
< / div >
2026-05-18 14:25:45 -05:00
< h2 className = "text-2xl font-bold text-primary-navy mb-3" > { industry . name } < / h2 >
< p className = "text-soft-text leading-relaxed mb-6" > { industry . shortDesc } < / p >
< ul className = "space-y-2.5 mb-8 flex-1" >
{ industry . solutions . slice ( 0 , 3 ) . map ( ( solution , i ) => (
< li key = { i } className = "flex items-start gap-3 text-sm text-text" >
< CheckCircle2 className = "h-4 w-4 text-primary-blue flex-shrink-0 mt-0.5" aria - hidden = "true" / >
{ solution }
< / li >
) ) }
< / ul >
< Link
to = { ` /industries/ ${ industry . id } ` }
fix(ui): header, Cisco logo and tap targets, found by rendering on devices
Every page on ten emulated phones and tablets: 190 loads, 21 blocking and 530
high findings, on pages that had passed Batch 11 and Batch 16.
#214 was fixed in v0.9.5 and released still broken. The check was a desktop
window sized to 768, which has a scrollbar, so the layout viewport was ~753px
and the md breakpoint the fix was about never engaged. At a true 768 the brand,
six nav links and the CTA want 787px against 736px of container, so no amount of
gap tightening closes it. The desktop row now starts at lg, and 768 to 1023 gets
the Sheet menu, which is the better tablet experience regardless: 44px rows
instead of 17px ones, and submenus that do not depend on hover. The CTA takes
shrink-0 and whitespace-nowrap, so the next row that does not fit overflows
visibly instead of being squeezed past the edge while the total still measures
as fitting.
The Cisco mark fills 66% of its 700x700 canvas, so both tiles scaled it up
inside overflow-hidden and cut 13px off the trademark on / and 24px on /about.
Cropping the viewBox to the artwork renders it at 62x46 on /, the same size as
the 8x8 logo beside it, unscaled and unclipped.
491 standalone links were 17 to 26px tall. .tap-target grows the hit box and
takes it back out of the layout, and every list using it moves to space-y-4:
at space-y-2 neighbouring boxes overlap and measure 33px while not being there.
Footer links also take block, so the row is the target. A 39x36 word is judged
as a compact target wanting 44px; a 200x36 row wants 32.
scripts/device-sweep.mjs and scripts/lib/css-audit.js are the instrument. They
stay out of verify.sh: playwright is global here and the sweep needs a running
server, and a guard that cannot run on a clean clone is one that gets skipped.
After the fixes: zero blocking, zero high across all ten devices. Each defect
was then re-introduced and reported again (clipped x1, media_overflow x1,
past_viewport x19, touch_target x342).
closes #238
closes #239
closes #240
closes #241
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 07:09:32 -05:00
className = "tap-target inline-flex items-center gap-1.5 text-sm font-semibold text-primary-navy hover:text-primary-blue transition-colors"
2026-05-18 14:25:45 -05:00
aria - label = { ` Learn more about ${ industry . name } solutions ` }
>
See how we help
< ArrowRight className = "h-4 w-4" aria - hidden = "true" / >
< / Link >
2026-05-12 02:45:25 -05:00
< / div >
2026-05-17 21:41:01 -05:00
)
} ) }
2026-05-12 01:04:17 -05:00
< / div >
2026-05-17 17:11:29 -05:00
< / div >
2026-05-12 01:04:17 -05:00
< / section >
{ /* CTA */ }
2026-05-26 12:22:11 -05:00
< section className = "bg-section-alt py-16 lg:py-24" >
2026-05-18 14:25:45 -05:00
< div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
2026-05-26 12:22:11 -05:00
< div className = "rounded-md bg-primary-navy px-6 py-10 text-white shadow-md sm:px-8 lg:flex lg:items-center lg:justify-between lg:gap-10 lg:px-10" >
2026-05-18 14:25:45 -05:00
< div >
< h2 className = "text-3xl md:text-4xl font-bold mb-3" > Don ' t see your industry ? < / h2 >
< p className = "text-white/70 max-w-2xl" >
We work with businesses across all sectors . If you have specific compliance , connectivity , or workflow requirements , let ' s talk .
< / p >
< / div >
2026-05-26 12:22:11 -05:00
< div className = "mt-7 flex flex-col gap-3 sm:flex-row lg:mt-0 lg:flex-shrink-0" >
< Link
2026-05-27 22:33:54 -05:00
to = "/contact#contact-form"
2026-05-27 12:42:25 -05:00
className = "inline-flex w-full sm:w-auto h-11 items-center justify-center gap-2 rounded-md bg-white px-6 text-sm font-semibold text-primary-navy hover:bg-section-alt transition-colors"
2026-05-26 12:22:11 -05:00
>
Talk to a Specialist
< ArrowRight className = "h-4 w-4" aria - hidden = "true" / >
< / Link >
< a
href = "tel:+13217308020"
2026-05-27 12:42:25 -05:00
className = "inline-flex w-full sm:w-auto h-11 items-center justify-center rounded-md border border-white/35 px-6 text-sm font-semibold text-white hover:bg-white/10 transition-colors"
2026-05-26 12:22:11 -05:00
>
Call ( 321 ) 730 - 8020
< / a >
< / div >
2026-05-17 17:11:29 -05:00
< / div >
< / div >
2026-05-12 01:04:17 -05:00
< / section >
2026-05-17 17:11:29 -05:00
< / >
2026-05-12 01:04:17 -05:00
)
}
export default Industries