165 lines
5.9 KiB
JavaScript
165 lines
5.9 KiB
JavaScript
import SEO from '@/components/SEO'
|
|
import { buildBreadcrumbLd } from '@/lib/seo'
|
|
import { ShieldCheck } from 'lucide-react'
|
|
import { EFFECTIVE_DATE, LAST_UPDATED, PRIVACY_EMAIL, sections } from '@/data/privacyPolicy'
|
|
|
|
const EmailLink = ({ className = '' }) => (
|
|
<a
|
|
href={`mailto:${PRIVACY_EMAIL}`}
|
|
className={`font-semibold text-primary-blue underline underline-offset-4 hover:text-primary-navy transition-colors ${className}`}
|
|
>
|
|
{PRIVACY_EMAIL}
|
|
</a>
|
|
)
|
|
|
|
// Renders a paragraph, splitting the privacy address out as a mailto link when present.
|
|
const Paragraph = ({ text, linkEmail }) => {
|
|
if (!linkEmail || !text.includes(PRIVACY_EMAIL)) {
|
|
return <p className="mt-4 text-base leading-relaxed text-soft-text">{text}</p>
|
|
}
|
|
|
|
const [before, after] = text.split(PRIVACY_EMAIL)
|
|
return (
|
|
<p className="mt-4 text-base leading-relaxed text-soft-text">
|
|
{before}
|
|
<EmailLink />
|
|
{after}
|
|
</p>
|
|
)
|
|
}
|
|
|
|
const Block = ({ block }) => {
|
|
switch (block.type) {
|
|
case 'h3':
|
|
return <h3 className="mt-8 text-lg font-semibold text-primary-navy">{block.text}</h3>
|
|
|
|
case 'ul':
|
|
return (
|
|
<ul className="mt-4 space-y-2">
|
|
{block.items.map((item) => (
|
|
<li key={item} className="flex gap-3 text-base leading-relaxed text-soft-text">
|
|
<span className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-primary-cyan" aria-hidden="true" />
|
|
<span>{item}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)
|
|
|
|
case 'callout':
|
|
return (
|
|
<p className="mt-5 rounded-md border border-border border-l-[3px] border-l-accent-gold bg-section-alt p-5 text-base leading-relaxed text-text">
|
|
{block.text}
|
|
</p>
|
|
)
|
|
|
|
case 'email':
|
|
return (
|
|
<p className="mt-4 text-base leading-relaxed">
|
|
<EmailLink />
|
|
</p>
|
|
)
|
|
|
|
case 'contactBlock':
|
|
return (
|
|
<div className="mt-5 rounded-md border border-border bg-section-alt p-6">
|
|
<p className="text-base font-semibold text-primary-navy">Queue North Technologies</p>
|
|
<p className="mt-2 text-base leading-relaxed text-soft-text">
|
|
Email: <EmailLink />
|
|
</p>
|
|
<p className="mt-1 text-base leading-relaxed text-soft-text">
|
|
Website:{' '}
|
|
<a
|
|
href="https://queuenorth.com"
|
|
className="font-semibold text-primary-blue underline underline-offset-4 hover:text-primary-navy transition-colors"
|
|
>
|
|
https://queuenorth.com
|
|
</a>
|
|
</p>
|
|
</div>
|
|
)
|
|
|
|
default:
|
|
return <Paragraph text={block.text} linkEmail={block.linkEmail} />
|
|
}
|
|
}
|
|
|
|
const PrivacyPolicy = () => {
|
|
const numberedSections = sections.filter((section) => section.number)
|
|
|
|
return (
|
|
<>
|
|
<SEO
|
|
title="Privacy Policy | Queue North Technologies"
|
|
description="How Queue North collects, uses, discloses, retains, and protects information across our website, advertisements, and hosted lead forms."
|
|
url="https://queuenorth.com/privacy-policy"
|
|
jsonLd={buildBreadcrumbLd([{ name: 'Privacy Policy', path: '/privacy-policy' }])}
|
|
/>
|
|
|
|
{/* Page Hero */}
|
|
<section className="bg-primary-navy py-16 lg:py-20 text-white">
|
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<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">
|
|
<ShieldCheck className="h-4 w-4" aria-hidden="true" />
|
|
Legal
|
|
</div>
|
|
<h1 className="mt-6 text-4xl md:text-5xl font-bold leading-tight">
|
|
Queue North Technologies Privacy Policy
|
|
</h1>
|
|
<dl className="mt-6 flex flex-col gap-2 text-sm text-white/75 sm:flex-row sm:gap-8">
|
|
<div className="flex gap-2">
|
|
<dt className="font-semibold text-white">Effective Date:</dt>
|
|
<dd>{EFFECTIVE_DATE}</dd>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<dt className="font-semibold text-white">Last Updated:</dt>
|
|
<dd>{LAST_UPDATED}</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Policy Body */}
|
|
<section className="bg-background py-16 lg:py-20">
|
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
{/* Contents */}
|
|
<nav aria-label="Privacy policy contents" className="rounded-md border border-border bg-white p-6 shadow-sm">
|
|
<h2 className="text-sm font-semibold uppercase tracking-wide text-primary-blue">Contents</h2>
|
|
<ol className="mt-4 grid grid-cols-1 gap-x-8 gap-y-2 sm:grid-cols-2">
|
|
{numberedSections.map((section) => (
|
|
<li key={section.id} className="text-sm">
|
|
<a
|
|
href={`#${section.id}`}
|
|
className="text-soft-text hover:text-primary-blue transition-colors"
|
|
>
|
|
<span className="font-numeric font-semibold text-primary-navy">{section.number}.</span>{' '}
|
|
{section.title}
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ol>
|
|
</nav>
|
|
|
|
{sections.map((section) => (
|
|
<article key={section.id} id={section.id} className="mt-12 scroll-mt-28">
|
|
<h2 className="text-2xl md:text-3xl font-bold text-primary-navy">
|
|
{section.number ? (
|
|
<>
|
|
<span className="font-numeric">{section.number}.</span> {section.title}
|
|
</>
|
|
) : (
|
|
section.title
|
|
)}
|
|
</h2>
|
|
{section.blocks.map((block, index) => (
|
|
<Block key={`${section.id}-${index}`} block={block} />
|
|
))}
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default PrivacyPolicy
|