Both approved sheets ask for it: Unified Communications section 15 and Contact
Center section 18 each list the pages that should link back to them. Home, the
services index, the header and the footer already did. Managed Support and the
four industry pages did not, and the industry pages linked to no service at all.
Each industry page's own description already tells the reader Queue North
"delivers phone, contact center, network, and IT support" for that industry, so
all four qualify. The anchors are descriptive rather than the page names:
"Business phone and UCaaS solutions" and "Contact center and CCaaS solutions".
Rendered through the shared RelatedLinks component, in the industry sidebar card
and after "Ideal For" on a service page. It renders nothing without links, so
the five service pages that have none are untouched: only 5 of the 18 pages gain
a Related services block, and they are exactly the five the sheets name.
Verified against the build: both hrefs are present on managed-support and on all
four industry pages, the built-HTML audit passes with every link and fragment
resolving, and no other page gained the block.
Closes#221.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Live until now, on every service and industry page:
"...seamless integration Delivered by Queue North, a veteran-owned..."
"...for medical providers Queue North delivers phone, contact center..."
clampDescription joined its fragments with a space, and no shortDesc in
services.js or industries.js ends in a full stop. Nothing added one. The
description is the first thing a searcher reads, and it read as a run-on on
eleven of the eighteen pages.
buildDescription replaces it and joins fragments AS SENTENCES. It also takes an
`approved` description, which it emits verbatim: never joined, never clamped.
Levi's approved copy for the two long-form pages is 164 and 191 characters, and
the old 158-character clamp would have cut his sentences off with an ellipsis.
ServiceDetail reads it from `page.seo`, which the content commit fills in.
ServiceDetail and IndustryDetail now build their canonical from SITE_URL rather
than repeating the origin. The three files release.sh greps for that literal are
untouched, so the origin guard still has three copies to compare.
Verified against a build, page by page: all 11 detail descriptions gained
exactly a full stop and changed in no other way, and no other page's description
moved.
Closes#234.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
- Phone: (906) 482-6616 → (321) 730-8020 direct, (888) 656-2850 toll-free
- Add toll-free number to Footer and Contact page
- Add LinkedIn link to footer
- Add 8x8 trademark disclaimer to footer legal section
- Fix JSON-LD phone numbers in Home.jsx
- Add support phone to Support page
- Strip all Houghton, MI and Upper Peninsula location references
from meta tags, JSON-LD, and fallback descriptions
(not present on original site, unverified)
- Change JSON-LD areaServed from Houghton/UP to United States
- Update Support.jsx Zoho Desk link placeholder
- Added react-helmet-async + HelmetProvider to main.jsx
- Per-page Helmet components on all 8 pages (title, description, OG tags)
- JSON-LD structured data (Organization, LocalBusiness, Service)
- Created public/sitemap.xml with all 17 routes
- Created public/robots.txt
- Fixed heading hierarchy (no h1->h3 skips)
- Improved image alt text throughout
- Fixed docs/zoho-setup.md env defaults clarification
- Replace all undefined shadcn/ui Tailwind classes with correct config values
(text-muted-foreground→text-muted, text-card-foreground→text-text,
bg-secondary→bg-section-alt, ring-ring→ring-primary-navy, etc.)
- Replace text-cyan/bg-cyan with text-primary-cyan/bg-primary-cyan
- Convert all internal <a href> to React Router <Link to> for SPA navigation
- Remove self-referencing /contact button on Contact page
- Add tel: links for phone numbers, mailto: links for emails in
Contact.jsx and Support.jsx hero badges and info sections
- Fix Header.jsx mobile menu button hover:text-cyan→hover:text-primary-cyan (batch 0.6.3)
- Remove duplicate App.css, consolidate into index.css as single Tailwind entry point
- Move maxWidth.container to tailwind.config.js theme extension
- Update App.jsx import from ./App.css to ./index.css
- Fix router.jsx to use dynamic :slug routes for services and industries
- Fix ServiceDetail.jsx and IndustryDetail.jsx to use useParams()
- Convert Header.jsx and MobileNav.jsx <a> tags to React Router <Link> components
- Add scripts/docker-test.sh for persistence verification
- Add project-requirements.md