// components/pricing/ComparisonTable.tsx import { Plan } from "./types"; import { CheckIcon, MinusIcon } from "./icons"; export default function ComparisonTable({ plans }: { plans: Plan[] }) { // Outcome-focused features mapped to each plan const matrix: Record string | boolean> = { "DNS & email monitoring": () => true, "Automated backups": () => true, "Restore test cadence": (id) => id === "essential" ? "Quarterly" : id === "growth" ? "Quarterly" : "Monthly", "Managed updates (WordPress/Next.js)": () => true, "Cloudflare WAF & bot tuning": (id) => id !== "essential", "Web Vitals reporting": (id) => (id === "growth" || id === "mission" ? "Monthly" : false), "On-call paging": (id) => (id === "mission" ? "24/7" : false), "First response SLA": (id) => id === "essential" ? "NBD (8×5)" : id === "growth" ? "4h (8×5)" : "1h (24/7)", "Uptime target": (id) => id === "essential" ? "99.9%" : id === "growth" ? "99.95%" : "99.99%", }; const features = Object.keys(matrix); return (

What’s inside each plan

Outcome-driven features, not laundry lists.

{plans.map((p) => ( ))} {features.map((feature) => ( {plans.map((p) => { const val = matrix[feature](p.id); const isBool = typeof val === "boolean"; return ( ); })} ))}
Feature {p.name}
{feature} {isBool ? ( val ? ( ) : ( ) ) : ( {val as string} )}
“Uptime target” is an SLO over a rolling 30-day period. Credits apply if response SLAs are missed.
); }