741 lines
31 KiB
TypeScript
741 lines
31 KiB
TypeScript
import type { FAQItem } from "@/lib/types";
|
|
import type { Metadata } from "next";
|
|
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import {
|
|
ArrowRight,
|
|
Check,
|
|
CheckCircle2,
|
|
Compass,
|
|
Link2,
|
|
Radar,
|
|
Smartphone,
|
|
RefreshCw,
|
|
BarChart3,
|
|
Utensils,
|
|
ChevronRight,
|
|
ShieldCheck,
|
|
Zap,
|
|
} from "lucide-react";
|
|
|
|
import Breadcrumbs, { BreadcrumbItem } from "@/components/Breadcrumbs";
|
|
import SeoJsonLd from "@/components/SeoJsonLd";
|
|
import { FAQSection } from "@/components/aeo/FAQSection";
|
|
import {
|
|
MarketingPageTracker,
|
|
TrackedCtaLink,
|
|
} from "@/components/marketing/MarketingAnalytics";
|
|
import { AnswerFirstBlock } from "@/components/marketing/AnswerFirstBlock";
|
|
import { Button } from "@/components/ui/Button";
|
|
import { Card } from "@/components/ui/Card";
|
|
import { HeroSpotlight } from "@/components/marketing/HeroSpotlight";
|
|
import { breadcrumbSchema, faqPageSchema } from "@/lib/schema";
|
|
|
|
type LinkCard = {
|
|
href: string;
|
|
title: string;
|
|
description: string;
|
|
};
|
|
|
|
type UseCasePageTemplateProps = {
|
|
title: string;
|
|
description: string;
|
|
eyebrow: string;
|
|
intro: string;
|
|
pageType: "commercial" | "use_case";
|
|
cluster: string;
|
|
useCase?: string;
|
|
breadcrumbs: BreadcrumbItem[];
|
|
answer: string;
|
|
whenToUse: string[];
|
|
comparisonItems: {
|
|
label: string;
|
|
value: boolean;
|
|
text?: string;
|
|
}[];
|
|
howToSteps: string[];
|
|
primaryCta: {
|
|
href: string;
|
|
label: string;
|
|
};
|
|
secondaryCta: {
|
|
href: string;
|
|
label: string;
|
|
};
|
|
workflowTitle: string;
|
|
workflowIntro: string;
|
|
workflowCards: {
|
|
title: string;
|
|
description: string;
|
|
}[];
|
|
checklistTitle: string;
|
|
checklist: string[];
|
|
supportLinks: LinkCard[];
|
|
faq: FAQItem[];
|
|
heroImage?: string;
|
|
heroImageAlt?: string;
|
|
authoritySignals?: string[];
|
|
directAnswer?: string;
|
|
schemaData?: Record<string, unknown>[];
|
|
};
|
|
|
|
export function buildUseCaseMetadata({
|
|
title,
|
|
description,
|
|
canonicalPath,
|
|
}: {
|
|
title: string;
|
|
description: string;
|
|
canonicalPath: string;
|
|
}): Metadata {
|
|
const canonical = `https://www.qrmaster.net${canonicalPath}`;
|
|
|
|
return {
|
|
title: {
|
|
absolute: `${title} | QR Master`,
|
|
},
|
|
description,
|
|
alternates: {
|
|
canonical,
|
|
languages: {
|
|
"x-default": canonical,
|
|
en: canonical,
|
|
},
|
|
},
|
|
openGraph: {
|
|
title: `${title} | QR Master`,
|
|
description,
|
|
url: canonical,
|
|
type: "website",
|
|
images: ["/og-image.png"],
|
|
},
|
|
twitter: {
|
|
title: `${title} | QR Master`,
|
|
description,
|
|
},
|
|
};
|
|
}
|
|
|
|
function getWorkflowIcon(index: number) {
|
|
switch (index) {
|
|
case 0:
|
|
return Smartphone;
|
|
case 1:
|
|
return RefreshCw;
|
|
case 2:
|
|
return BarChart3;
|
|
default:
|
|
return Radar;
|
|
}
|
|
}
|
|
|
|
function RestaurantMockup() {
|
|
return (
|
|
<div className="relative w-full aspect-[4/3] max-w-[440px] mx-auto flex items-center justify-center select-none">
|
|
{/* Subtle flat shadow under elements */}
|
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 w-4/5 h-4 bg-slate-100/60 rounded-full blur-xl pointer-events-none" />
|
|
|
|
{/* Table stand card (Flat vector look) */}
|
|
<div
|
|
className="absolute left-6 bottom-4 w-[160px] sm:w-[170px] bg-white border border-slate-200/80 p-3.5 shadow-md rounded-xl flex flex-col items-center justify-between z-10"
|
|
style={{
|
|
transform: "perspective(1000px) rotateY(8deg) rotateX(4deg) translateZ(10px)",
|
|
}}
|
|
>
|
|
{/* Table number / header */}
|
|
<div className="w-full flex justify-between items-center border-b border-slate-100 pb-1.5 mb-2">
|
|
<div className="flex items-center gap-1">
|
|
<Utensils className="w-3 text-slate-400" />
|
|
<span className="text-[8px] font-bold text-slate-500 tracking-wide uppercase">Bistro</span>
|
|
</div>
|
|
<span className="text-[8px] font-semibold bg-slate-100 text-slate-655 px-1 py-0.5 rounded">T-12</span>
|
|
</div>
|
|
|
|
{/* Scan instruction */}
|
|
<div className="text-center mb-2.5">
|
|
<p className="text-[8px] font-bold text-slate-850 uppercase tracking-widest leading-none">Scan for Menu</p>
|
|
</div>
|
|
|
|
{/* Flat QR Code */}
|
|
<div className="w-20 h-20 bg-white p-1.5 border border-slate-150 rounded shadow-sm flex items-center justify-center">
|
|
<svg viewBox="0 0 100 100" className="w-full h-full text-slate-800" fill="currentColor">
|
|
{/* Finder Pattern Top-Left */}
|
|
<rect x="0" y="0" width="30" height="30" />
|
|
<rect x="5" y="5" width="20" height="20" fill="white" />
|
|
<rect x="10" y="10" width="10" height="10" />
|
|
|
|
{/* Finder Pattern Top-Right */}
|
|
<rect x="70" y="0" width="30" height="30" />
|
|
<rect x="75" y="5" width="20" height="20" fill="white" />
|
|
<rect x="80" y="80" width="10" height="10" fill="white" />
|
|
|
|
{/* Finder Pattern Bottom-Left */}
|
|
<rect x="0" y="70" width="30" height="30" />
|
|
<rect x="5" y="75" width="20" height="20" fill="white" />
|
|
<rect x="10" y="80" width="10" height="10" />
|
|
|
|
{/* Align Pattern Bottom-Right */}
|
|
<rect x="75" y="75" width="10" height="10" />
|
|
<rect x="70" y="70" width="5" height="5" />
|
|
|
|
{/* Timing patterns */}
|
|
<rect x="35" y="10" width="5" height="5" />
|
|
<rect x="45" y="10" width="5" height="5" />
|
|
<rect x="55" y="10" width="5" height="5" />
|
|
<rect x="10" y="35" width="5" height="5" />
|
|
<rect x="10" y="45" width="5" height="5" />
|
|
<rect x="10" y="55" width="5" height="5" />
|
|
|
|
{/* Random data blocks */}
|
|
<rect x="40" y="20" width="10" height="15" />
|
|
<rect x="55" y="20" width="10" height="10" />
|
|
<rect x="35" y="35" width="15" height="15" />
|
|
<rect x="55" y="35" width="10" height="20" />
|
|
<rect x="40" y="55" width="10" height="10" />
|
|
<rect x="40" y="70" width="15" height="10" />
|
|
<rect x="45" y="85" width="10" height="10" />
|
|
<rect x="70" y="40" width="15" height="10" />
|
|
<rect x="80" y="55" width="10" height="10" />
|
|
<rect x="70" y="25" width="5" height="10" />
|
|
<rect x="30" y="80" width="5" height="15" />
|
|
</svg>
|
|
</div>
|
|
|
|
{/* Stand base detail */}
|
|
<div className="w-14 h-1 bg-slate-200 rounded-full mt-2.5 opacity-60" />
|
|
</div>
|
|
|
|
{/* Smartphone (Flat vector look) */}
|
|
<div
|
|
className="absolute right-6 top-2 w-[180px] sm:w-[190px] h-[310px] sm:h-[340px] bg-slate-100 border-4 border-slate-350 shadow-lg rounded-[2rem] flex flex-col overflow-hidden z-20"
|
|
style={{
|
|
transform: "perspective(1000px) rotateY(-6deg) rotateX(3deg)",
|
|
}}
|
|
>
|
|
{/* Dynamic Island */}
|
|
<div className="absolute top-1.5 left-1/2 -translate-x-1/2 w-12 h-2.5 bg-slate-355 rounded-full z-30" />
|
|
|
|
{/* Smartphone Screen Content */}
|
|
<div className="flex-1 bg-white flex flex-col text-slate-805 pt-5 relative overflow-hidden">
|
|
{/* Custom menu header */}
|
|
<div className="bg-slate-50 px-2.5 py-1.5 border-b border-slate-100 flex items-center justify-between">
|
|
<span className="text-[8px] font-bold text-slate-700 tracking-tight uppercase">Bistro</span>
|
|
<span className="text-[6px] text-emerald-600 font-semibold bg-emerald-50 px-1 py-0.5 rounded">Active Menu</span>
|
|
</div>
|
|
|
|
{/* Menu List */}
|
|
<div className="flex-1 px-2.5 py-2 overflow-y-auto space-y-1.5">
|
|
{/* Menu Item 1 */}
|
|
<div className="border border-slate-100 p-1.5 rounded-lg flex justify-between items-center bg-white shadow-sm">
|
|
<div className="flex-1 pr-1">
|
|
<div className="flex justify-between items-baseline">
|
|
<span className="text-[8px] font-bold text-slate-855 leading-tight">Charred Ribeye</span>
|
|
<span className="text-[8px] font-bold text-slate-655">$34</span>
|
|
</div>
|
|
<p className="text-[6px] text-slate-450 leading-snug mt-0.5">Rosemary butter</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Menu Item 2 */}
|
|
<div className="border border-slate-100 p-1.5 rounded-lg flex justify-between items-center bg-white shadow-sm">
|
|
<div className="flex-1 pr-1">
|
|
<div className="flex justify-between items-baseline">
|
|
<span className="text-[8px] font-bold text-slate-855 leading-tight">Mushroom Pasta</span>
|
|
<span className="text-[8px] font-bold text-slate-655">$22</span>
|
|
</div>
|
|
<p className="text-[6px] text-slate-450 leading-snug mt-0.5">Fresh fettuccine</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Menu Item 3 */}
|
|
<div className="border border-slate-100 p-1.5 rounded-lg flex justify-between items-center bg-white shadow-sm">
|
|
<div className="flex-1 pr-1">
|
|
<div className="flex justify-between items-baseline">
|
|
<span className="text-[8px] font-bold text-slate-855 leading-tight">Crème Brûlée</span>
|
|
<span className="text-[8px] font-bold text-slate-655">$11</span>
|
|
</div>
|
|
<p className="text-[6px] text-slate-450 leading-snug mt-0.5">Peach & vanilla</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Cart Button */}
|
|
<div className="p-2 bg-slate-50 border-t border-slate-100 flex items-center justify-between z-10">
|
|
<span className="text-[8px] font-bold text-slate-700">$56.00</span>
|
|
<button className="bg-slate-900 text-white text-[7px] font-bold py-1 px-3 rounded shadow-sm hover:bg-slate-800 transition-colors">
|
|
Order Menu
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Flat Connecting Line (Blueprint look, dipped downward further) */}
|
|
<svg className="absolute inset-0 w-full h-full z-15 pointer-events-none" viewBox="0 0 440 330">
|
|
<path
|
|
d="M 160,185 Q 210,225 255,190"
|
|
fill="none"
|
|
stroke="#cbd5e1"
|
|
strokeWidth="1.5"
|
|
strokeDasharray="4,4"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function GenericMockup() {
|
|
return (
|
|
<div className="relative w-full aspect-[4/3] max-w-[440px] mx-auto flex items-center justify-center select-none">
|
|
{/* Subtle flat shadow under elements */}
|
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 w-4/5 h-4 bg-slate-100/60 rounded-full blur-xl pointer-events-none" />
|
|
|
|
{/* Flat Dashboard Card */}
|
|
<div
|
|
className="absolute left-6 top-4 w-[210px] sm:w-[220px] bg-white border border-slate-200/80 rounded-xl p-3.5 shadow-md z-10 flex flex-col justify-between"
|
|
style={{
|
|
transform: "perspective(1000px) rotateY(8deg) rotateX(4deg)",
|
|
}}
|
|
>
|
|
<div className="flex justify-between items-center border-b border-slate-100 pb-1.5 mb-2.5">
|
|
<div className="flex items-center gap-1">
|
|
<span className="w-1.5 h-1.5 rounded-full bg-slate-400" />
|
|
<span className="text-[8px] font-bold text-slate-505 uppercase tracking-wider">Overview</span>
|
|
</div>
|
|
<span className="text-[8px] font-bold bg-slate-100 text-slate-655 px-1 py-0.5 rounded">Active</span>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-1.5 mb-2.5">
|
|
<div className="bg-slate-50/50 p-1.5 rounded border border-slate-100">
|
|
<span className="text-[6px] font-semibold text-slate-400 uppercase">Scans</span>
|
|
<p className="text-xs font-bold text-slate-700 mt-0.5">8,421</p>
|
|
</div>
|
|
<div className="bg-slate-50/50 p-1.5 rounded border border-slate-100">
|
|
<span className="text-[6px] font-semibold text-slate-400 uppercase">Conversion</span>
|
|
<p className="text-xs font-bold text-slate-700 mt-0.5">14.2%</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mini Chart */}
|
|
<div className="space-y-1">
|
|
<span className="text-[6px] font-semibold text-slate-400 uppercase block">Activity</span>
|
|
<div className="h-10 flex items-end justify-between gap-1 pt-1">
|
|
{[30, 45, 25, 60, 80, 50, 75].map((val, idx) => (
|
|
<div key={idx} className="flex-1 bg-slate-150 rounded-t-sm" style={{ height: `${val}%` }} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Smartphone (Flat vector look) */}
|
|
<div
|
|
className="absolute right-6 bottom-4 w-[150px] sm:w-[160px] h-[250px] sm:h-[285px] bg-slate-100 border-4 border-slate-350 shadow-lg rounded-[1.75rem] flex flex-col overflow-hidden z-20"
|
|
style={{
|
|
transform: "perspective(1000px) rotateY(-8deg) rotateX(4deg)",
|
|
}}
|
|
>
|
|
{/* Dynamic Island */}
|
|
<div className="absolute top-1.5 left-1/2 -translate-x-1/2 w-10 h-2 bg-slate-355 rounded-full z-30" />
|
|
|
|
{/* Screen */}
|
|
<div className="flex-1 bg-white flex flex-col pt-5 p-2 items-center justify-between text-slate-800 relative">
|
|
<div className="w-full text-center py-0.5">
|
|
<span className="text-[7px] font-bold text-slate-400 uppercase tracking-widest block">Redirecting</span>
|
|
</div>
|
|
|
|
<div className="w-16 h-18 border border-slate-150 rounded flex items-center justify-center p-1.5 bg-slate-50">
|
|
<svg viewBox="0 0 100 100" className="w-10 h-10 text-slate-700" fill="currentColor">
|
|
<rect x="0" y="0" width="30" height="30" />
|
|
<rect x="5" y="5" width="20" height="20" fill="white" />
|
|
<rect x="10" y="10" width="10" height="10" />
|
|
<rect x="70" y="0" width="30" height="30" />
|
|
<rect x="75" y="5" width="20" height="20" fill="white" />
|
|
<rect x="80" y="80" width="10" height="10" fill="white" />
|
|
<rect x="0" y="70" width="30" height="30" />
|
|
<rect x="5" y="75" width="20" height="20" fill="white" />
|
|
<rect x="10" y="80" width="10" height="10" />
|
|
<rect x="75" y="75" width="10" height="10" />
|
|
<rect x="40" y="20" width="10" height="10" />
|
|
</svg>
|
|
</div>
|
|
|
|
<div className="w-full bg-slate-50 border border-slate-100 p-1 rounded flex items-center gap-1 shadow-sm text-[7px] text-slate-600">
|
|
<span className="text-[6px] font-semibold text-slate-400 truncate leading-none">redirecting to URL...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function UseCasePageTemplate({
|
|
title,
|
|
description,
|
|
eyebrow,
|
|
intro,
|
|
pageType,
|
|
cluster,
|
|
useCase,
|
|
breadcrumbs,
|
|
answer,
|
|
whenToUse,
|
|
comparisonItems,
|
|
howToSteps,
|
|
primaryCta,
|
|
secondaryCta,
|
|
workflowTitle,
|
|
workflowIntro,
|
|
workflowCards,
|
|
checklistTitle,
|
|
checklist,
|
|
supportLinks,
|
|
faq,
|
|
heroImage,
|
|
heroImageAlt = "QR code workflow example",
|
|
authoritySignals = [],
|
|
directAnswer,
|
|
schemaData = [],
|
|
}: UseCasePageTemplateProps) {
|
|
return (
|
|
<>
|
|
<SeoJsonLd
|
|
data={[...schemaData, breadcrumbSchema(breadcrumbs), faqPageSchema(faq)]}
|
|
/>
|
|
<MarketingPageTracker
|
|
pageType={pageType}
|
|
cluster={cluster}
|
|
useCase={useCase}
|
|
/>
|
|
|
|
<div className="min-h-screen bg-white">
|
|
{/* Pure white, highly polished, minimalist hero section with mouse tracking spotlight glow */}
|
|
<HeroSpotlight>
|
|
<div className="container mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
|
|
<Breadcrumbs
|
|
items={breadcrumbs}
|
|
className="mb-8 [&_a]:text-slate-500 [&_a:hover]:text-slate-900 [&_span]:text-slate-400 [&_[aria-current=page]]:text-slate-800"
|
|
/>
|
|
|
|
<div className="grid gap-16 lg:grid-cols-[1.15fr_0.85fr] lg:items-center">
|
|
<div className="space-y-8">
|
|
<div className="inline-flex items-center rounded-md border border-slate-200 bg-slate-50 px-3 py-1 text-xs font-semibold tracking-wider text-slate-500 uppercase shadow-sm">
|
|
<span>Use Case</span>
|
|
</div>
|
|
|
|
<div className="space-y-5">
|
|
<h1 className="max-w-4xl text-4xl font-light tracking-tight text-slate-900 md:text-5xl lg:text-6xl leading-[1.1] -tracking-[0.03em]">
|
|
{title}
|
|
</h1>
|
|
<p className="max-w-3xl text-base leading-relaxed text-slate-500 md:text-lg">
|
|
{intro}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Sub-benefits (Styled as authentic, clean Framer-style Apple Notification banners) */}
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
{[
|
|
{ title: "Print Stability", body: "Keep printed tables & flyers clean and permanent", icon: ShieldCheck },
|
|
{ title: "Instant Redirection", body: "Update links instantly without reprinting codes", icon: RefreshCw },
|
|
{ title: "Attribution Flows", body: "Built-in campaign tracking and flow analysis", icon: BarChart3 },
|
|
{ title: "Mobile Optimized", body: "Designed to load fast on mobile data connections", icon: Zap },
|
|
].map((item) => (
|
|
<div
|
|
key={item.title}
|
|
className="flex items-center gap-3.5 rounded-[1.25rem] bg-[#f4f4f5]/90 border border-slate-200/30 p-3.5 shadow-[0_8px_24px_rgba(0,0,0,0.02)] backdrop-blur-md transition-all duration-200 hover:scale-[1.01] hover:bg-[#f4f4f5]"
|
|
>
|
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-[11px] bg-slate-900 text-white shadow-sm">
|
|
<item.icon className="h-5 w-5" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-baseline justify-between">
|
|
<span className="text-[13px] font-semibold text-slate-900 truncate">{item.title}</span>
|
|
<span className="text-[10px] text-slate-400 font-medium shrink-0 ml-2">now</span>
|
|
</div>
|
|
<p className="text-[12px] text-slate-500 leading-snug mt-0.5">{item.body}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-3.5 sm:flex-row pt-2">
|
|
<TrackedCtaLink
|
|
href={primaryCta.href}
|
|
ctaLabel={primaryCta.label}
|
|
ctaLocation="hero_primary"
|
|
pageType={pageType}
|
|
cluster={cluster}
|
|
useCase={useCase}
|
|
>
|
|
<Button
|
|
size="lg"
|
|
className="w-full bg-blue-600 hover:bg-blue-700 text-white px-8 py-4 text-base font-semibold sm:w-auto rounded-lg shadow-sm hover:shadow-md transition-all duration-200"
|
|
>
|
|
{primaryCta.label}
|
|
</Button>
|
|
</TrackedCtaLink>
|
|
|
|
<TrackedCtaLink
|
|
href={secondaryCta.href}
|
|
ctaLabel={secondaryCta.label}
|
|
ctaLocation="hero_secondary"
|
|
pageType={pageType}
|
|
cluster={cluster}
|
|
useCase={useCase}
|
|
>
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
className="w-full border-slate-250 bg-white px-8 py-4 text-base text-slate-600 hover:text-slate-800 hover:bg-slate-50 sm:w-auto rounded-lg transition-all duration-200"
|
|
>
|
|
{secondaryCta.label}
|
|
</Button>
|
|
</TrackedCtaLink>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mockup visual wrapped in an elegant macOS-style window frame */}
|
|
<div className="relative flex justify-center lg:justify-end">
|
|
{heroImage ? (
|
|
<div className="w-full max-w-[480px] bg-white border border-slate-200/80 rounded-2xl shadow-[0_20px_50px_rgba(0,0,0,0.06)] p-3">
|
|
{/* Browser header controls */}
|
|
<div className="flex items-center gap-1.5 border-b border-slate-100 pb-2.5 mb-2.5 px-1 select-none">
|
|
<span className="w-2 h-2 rounded-full bg-rose-400/80" />
|
|
<span className="w-2 h-2 rounded-full bg-amber-400/80" />
|
|
<span className="w-2 h-2 rounded-full bg-emerald-400/80" />
|
|
<span className="text-[9px] text-slate-400 font-semibold ml-4 tracking-wide uppercase">Setup Preview</span>
|
|
</div>
|
|
{/* Visual Container */}
|
|
<div className="relative aspect-[4/3] overflow-hidden rounded-xl bg-slate-50/50">
|
|
<Image
|
|
src={heroImage}
|
|
alt={heroImageAlt}
|
|
fill
|
|
priority
|
|
sizes="(min-width: 1024px) 38vw, 100vw"
|
|
className="object-contain"
|
|
/>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="w-full">
|
|
{useCase === 'restaurant-menu-qr-codes' ? (
|
|
<RestaurantMockup />
|
|
) : (
|
|
<GenericMockup />
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</HeroSpotlight>
|
|
|
|
{/* AnswerFirst block - summary and quick details */}
|
|
<div className="container mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
|
|
<AnswerFirstBlock
|
|
whatIsIt={answer}
|
|
whenToUse={whenToUse}
|
|
comparison={{
|
|
leftTitle: "Static QR",
|
|
rightTitle: "Dynamic QR",
|
|
items: comparisonItems,
|
|
}}
|
|
howTo={{
|
|
steps: howToSteps,
|
|
}}
|
|
className="mt-0"
|
|
/>
|
|
</div>
|
|
|
|
{directAnswer ? (
|
|
<div className="container mx-auto max-w-7xl px-4 pb-10 sm:px-6 lg:px-8">
|
|
<p className="text-base leading-7 text-slate-700 sm:text-lg">
|
|
{directAnswer}
|
|
</p>
|
|
{authoritySignals.length ? (
|
|
<div className="mt-6 grid gap-4 sm:grid-cols-3">
|
|
{authoritySignals.map((item) => (
|
|
<div
|
|
key={item}
|
|
className="flex items-start gap-3 rounded-lg border border-slate-200 bg-white px-4 py-3 text-sm text-slate-700"
|
|
>
|
|
<Check className="mt-0.5 h-4 w-4 shrink-0 text-blue-700" />
|
|
<span>{item}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
|
|
{/* Workflow steps block */}
|
|
<section className="bg-slate-50/50 border-y border-slate-150 py-20">
|
|
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
<div className="mb-14 max-w-3xl">
|
|
<h2 className="text-3xl font-semibold tracking-tight text-slate-900 md:text-4xl -tracking-[0.02em]">
|
|
{workflowTitle}
|
|
</h2>
|
|
<p className="mt-4 text-lg leading-relaxed text-slate-500">
|
|
{workflowIntro}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid gap-8 lg:grid-cols-3">
|
|
{workflowCards.map((card, idx) => {
|
|
const Icon = getWorkflowIcon(idx);
|
|
return (
|
|
<Card
|
|
key={card.title}
|
|
className="rounded-xl border-slate-200 bg-white p-8 shadow-sm transition-all duration-250 hover:border-slate-300 hover:shadow-md"
|
|
>
|
|
<div className="mb-6 flex h-11 w-11 items-center justify-center rounded-lg bg-slate-50 border border-slate-100 text-slate-655">
|
|
<Icon className="h-5.5 w-5.5" />
|
|
</div>
|
|
<h3 className="text-xl font-semibold text-slate-900">
|
|
{card.title}
|
|
</h3>
|
|
<p className="mt-3 text-base leading-relaxed text-slate-500">
|
|
{card.description}
|
|
</p>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Symmetrical Checklist & Sibling resources section */}
|
|
<section className="py-20 bg-white">
|
|
<div className="container mx-auto grid max-w-7xl gap-8 px-4 sm:px-6 lg:grid-cols-2 lg:px-8">
|
|
{/* Checklist Card */}
|
|
<Card className="rounded-xl border-slate-200/70 bg-white p-8 sm:p-10 shadow-sm flex flex-col justify-between">
|
|
<div>
|
|
<div className="flex items-center justify-between gap-4 border-b border-slate-100 pb-5">
|
|
<div>
|
|
<div className="text-xs font-semibold uppercase tracking-wider text-slate-450">
|
|
Standard Guidelines
|
|
</div>
|
|
<h2 className="mt-2 text-2xl font-bold text-slate-900 sm:text-3xl -tracking-[0.02em]">
|
|
{checklistTitle}
|
|
</h2>
|
|
</div>
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-50 border border-slate-100 text-slate-500">
|
|
<CheckCircle2 className="h-5 w-5" />
|
|
</div>
|
|
</div>
|
|
|
|
<ul className="mt-8 space-y-4">
|
|
{checklist.map((item) => (
|
|
<li
|
|
key={item}
|
|
className="flex items-start gap-3.5 text-slate-650"
|
|
>
|
|
<CheckCircle2 className="mt-1 h-4.5 w-4.5 shrink-0 text-emerald-500" />
|
|
<span className="leading-relaxed text-slate-600">{item}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</Card>
|
|
|
|
{/* Related links card (Clean white, matching checklist) */}
|
|
<Card className="rounded-xl border-slate-200/70 bg-white p-8 sm:p-10 shadow-sm flex flex-col justify-between">
|
|
<div>
|
|
<div className="flex items-center gap-3 border-b border-slate-100 pb-5">
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-50 border border-slate-100 text-slate-550">
|
|
<Link2 className="h-5 w-5" />
|
|
</div>
|
|
<h2 className="text-2xl font-bold text-slate-900 sm:text-3xl -tracking-[0.02em]">Related resources</h2>
|
|
</div>
|
|
|
|
<div className="mt-6 space-y-4">
|
|
{supportLinks.map((link) => (
|
|
<Link
|
|
key={link.href}
|
|
href={link.href}
|
|
className="group block rounded-xl border border-slate-100 bg-slate-50/30 p-4 transition-all duration-200 hover:bg-slate-50 hover:border-slate-200"
|
|
>
|
|
<div className="flex items-start justify-between gap-4">
|
|
<div>
|
|
<div className="text-base font-semibold text-slate-855 group-hover:text-blue-600 transition-colors">
|
|
{link.title}
|
|
</div>
|
|
<div className="mt-1.5 text-sm leading-relaxed text-slate-500">
|
|
{link.description}
|
|
</div>
|
|
</div>
|
|
<ChevronRight className="mt-1 h-4 w-4 shrink-0 text-slate-400 transition-transform group-hover:translate-x-1 group-hover:text-blue-600" />
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</section>
|
|
|
|
{/* FAQs */}
|
|
<div className="container mx-auto max-w-5xl px-4 pb-12 sm:px-6 lg:px-8">
|
|
<FAQSection items={faq} title={`${title} FAQ`} />
|
|
</div>
|
|
|
|
{/* Bottom CTA Banner (Flat light-gray card, no gradients) */}
|
|
<section className="pb-24 pt-6">
|
|
<div className="container mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">
|
|
<div className="relative rounded-2xl bg-slate-50 border border-slate-200 p-8 sm:p-12 text-slate-900 shadow-sm overflow-hidden">
|
|
<div className="relative z-10 flex flex-col gap-8 lg:flex-row lg:items-center lg:justify-between">
|
|
<div className="max-w-2xl space-y-3">
|
|
<div className="text-xs font-semibold uppercase tracking-wider text-slate-450">
|
|
Next step
|
|
</div>
|
|
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl -tracking-[0.02em] leading-tight text-slate-950">
|
|
Start with a QR setup that keeps your print permanent.
|
|
</h2>
|
|
<p className="text-base leading-relaxed text-slate-500">
|
|
{description}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-3.5 sm:flex-row shrink-0">
|
|
<TrackedCtaLink
|
|
href={primaryCta.href}
|
|
ctaLabel={primaryCta.label}
|
|
ctaLocation="footer_primary"
|
|
pageType={pageType}
|
|
cluster={cluster}
|
|
useCase={useCase}
|
|
>
|
|
<Button
|
|
size="lg"
|
|
className="w-full bg-blue-600 hover:bg-blue-700 text-white px-7 sm:w-auto rounded-lg font-semibold shadow-sm transition-all duration-200"
|
|
>
|
|
{primaryCta.label}
|
|
</Button>
|
|
</TrackedCtaLink>
|
|
|
|
<TrackedCtaLink
|
|
href={secondaryCta.href}
|
|
ctaLabel={secondaryCta.label}
|
|
ctaLocation="footer_secondary"
|
|
pageType={pageType}
|
|
cluster={cluster}
|
|
useCase={useCase}
|
|
>
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
className="w-full border-slate-250 bg-white text-slate-600 hover:text-slate-800 hover:bg-slate-50 sm:w-auto rounded-lg transition-all duration-200"
|
|
>
|
|
{secondaryCta.label}
|
|
</Button>
|
|
</TrackedCtaLink>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|