{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pricing-comparison",
  "type": "registry:block",
  "title": "Pricing comparison",
  "description": "A pricing comparison table with row and column headers. Each cell states its value in text, not only with a tick.",
  "categories": [
    "section"
  ],
  "dependencies": [
    "lucide-react@^1.47.0"
  ],
  "registryDependencies": [
    "https://facadeui.dev/r/badge.json",
    "https://facadeui.dev/r/button.json",
    "https://facadeui.dev/r/container.json",
    "https://facadeui.dev/r/section.json",
    "https://facadeui.dev/r/section-header.json",
    "https://facadeui.dev/r/types.json",
    "https://facadeui.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "src/sections/pricing-comparison.tsx",
      "type": "registry:component",
      "target": "components/sections/pricing-comparison.tsx",
      "content": "/**\n * PricingComparison — the full feature matrix, plan by plan.\n *\n * a11y: this is the section where a `<div>` grid would do the most damage, so it\n * is a real `<table>` and the details matter.\n *\n *  - Plan names are `<th scope=\"col\">`, feature names are `<th scope=\"row\">`.\n *    That pairing is what lets a screen reader announce \"Team, Shared theme\n *    tokens, included\" when the user lands on a cell — without it, the reader\n *    gets \"included\" with no idea of what or whose.\n *  - Group rows use `<th scope=\"colgroup\">` spanning the table, so a feature\n *    inherits its group heading.\n *  - A tick or a cross is never the only content of a cell: each carries\n *    visually hidden \"Included\" / \"Not included\" text, and the icon is\n *    `aria-hidden`. Colour and shape alone fail 1.4.1.\n *  - The table has a `<caption>`. It can be visually hidden, but it is always\n *    there, because a table without one is an unnamed grid.\n *  - The scroll container is focusable, so a wide matrix can be scrolled without\n *    a mouse.\n *\n * Dependencies: lucide-react, react, @/lib/types, @/lib/utils,\n * @/components/ui/badge, @/components/ui/button, @/components/ui/container,\n * @/components/ui/section, @/components/ui/section-header.\n */\n\nimport { CheckIcon, MinusIcon } from \"lucide-react\"\nimport type { ElementType, ReactNode } from \"react\"\n\nimport type { CtaItem, LinkComponent, SectionBaseProps } from \"@/lib/types\"\nimport { cn, slugId } from \"@/lib/utils\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Container } from \"@/components/ui/container\"\nimport { Section } from \"@/components/ui/section\"\nimport { SectionHeader } from \"@/components/ui/section-header\"\n\nexport interface ComparisonPlan {\n  name: string\n  price?: string\n  srPrice?: string\n  period?: string\n  cta?: CtaItem\n  featured?: boolean\n  featuredLabel?: string\n}\n\n/** `true`/`false` render a tick or a dash; a string renders as text. */\nexport type ComparisonValue = boolean | string\n\nexport interface ComparisonFeature {\n  label: string\n  /** One entry per plan, in the same order as `plans`. */\n  values: ComparisonValue[]\n  note?: string\n}\n\nexport interface ComparisonGroup {\n  title: string\n  features: ComparisonFeature[]\n}\n\nexport interface PricingComparisonProps extends SectionBaseProps {\n  plans: ComparisonPlan[]\n  groups: ComparisonGroup[]\n  title?: string\n  eyebrow?: string\n  description?: string\n  link?: LinkComponent\n  /** Table caption. Always present, but visually hidden by default. */\n  caption?: string\n  showCaption?: boolean\n  note?: ReactNode\n  align?: \"start\" | \"center\"\n}\n\nfunction Cell({ value }: { value: ComparisonValue | undefined }) {\n  if (typeof value === \"string\") {\n    return <span className=\"text-foreground text-sm\">{value}</span>\n  }\n  if (value) {\n    return (\n      <>\n        <CheckIcon\n          aria-hidden\n          focusable=\"false\"\n          className=\"text-primary mx-auto size-5\"\n        />\n        {/* Never rely on the glyph alone. */}\n        <span className=\"sr-only\">Included</span>\n      </>\n    )\n  }\n  return (\n    <>\n      <MinusIcon\n        aria-hidden\n        focusable=\"false\"\n        className=\"text-muted-foreground mx-auto size-5\"\n      />\n      <span className=\"sr-only\">Not included</span>\n    </>\n  )\n}\n\nexport function PricingComparison({\n  plans,\n  groups,\n  title,\n  eyebrow,\n  description,\n  link,\n  caption,\n  showCaption = false,\n  note,\n  align = \"center\",\n  headingLevel = 2,\n  as,\n  spacing = \"md\",\n  className,\n  id,\n}: PricingComparisonProps) {\n  const Link = (link ?? \"a\") as ElementType\n  const titleId = title ? (id ? `${id}-title` : slugId(title)) : undefined\n  const tableCaption =\n    caption ?? (title ? `${title}: plan comparison` : \"Plan comparison\")\n\n  return (\n    <Section\n      as={as ?? (title ? \"section\" : \"div\")}\n      spacing={spacing}\n      labelledBy={titleId}\n      id={id}\n      className={className}\n    >\n      <Container className=\"flex flex-col gap-12\">\n        {title ? (\n          <SectionHeader\n            align={align}\n            eyebrow={eyebrow}\n            title={title}\n            description={description}\n            headingLevel={headingLevel}\n            titleId={titleId}\n          />\n        ) : null}\n\n        <div\n          tabIndex={0}\n          role=\"region\"\n          aria-label={tableCaption}\n          className=\"focus-visible:ring-ring overflow-x-auto rounded-xl border focus-visible:outline-none focus-visible:ring-2\"\n        >\n          <table className=\"w-full border-collapse text-left\">\n            <caption\n              className={cn(\n                \"px-4 py-3 text-sm\",\n                showCaption ? \"text-muted-foreground\" : \"sr-only\",\n              )}\n            >\n              {tableCaption}\n            </caption>\n\n            <thead>\n              <tr className=\"border-b\">\n                <th\n                  scope=\"col\"\n                  className=\"bg-background sticky left-0 px-4 py-4 align-bottom\"\n                >\n                  <span className=\"sr-only\">Feature</span>\n                </th>\n                {plans.map((plan) => (\n                  <th\n                    key={plan.name}\n                    scope=\"col\"\n                    className={cn(\n                      \"min-w-40 px-4 py-4 text-center align-bottom\",\n                      plan.featured && \"bg-accent/40\",\n                    )}\n                  >\n                    <span className=\"flex flex-col items-center gap-2\">\n                      {plan.featured ? (\n                        <Badge variant=\"primary\" size=\"sm\">\n                          {plan.featuredLabel ?? \"Most popular\"}\n                        </Badge>\n                      ) : null}\n                      <span className=\"text-foreground text-base font-semibold\">\n                        {plan.name}\n                      </span>\n                      {plan.price ? (\n                        <span className=\"text-muted-foreground text-sm\">\n                          <span aria-hidden>\n                            {plan.price}\n                            {plan.period}\n                          </span>\n                          <span className=\"sr-only\">\n                            {plan.srPrice ?? plan.price}\n                            {plan.period ? ` ${plan.period.replace(/^\\//, \"per \")}` : \"\"}\n                          </span>\n                        </span>\n                      ) : null}\n                      {plan.cta ? (\n                        <Link\n                          href={plan.cta.href}\n                          aria-label={\n                            plan.cta[\"aria-label\"] ?? `${plan.cta.label} — ${plan.name}`\n                          }\n                          className={buttonVariants({\n                            variant:\n                              plan.cta.variant ?? (plan.featured ? \"primary\" : \"outline\"),\n                            size: \"sm\",\n                          })}\n                        >\n                          {plan.cta.label}\n                        </Link>\n                      ) : null}\n                    </span>\n                  </th>\n                ))}\n              </tr>\n            </thead>\n\n            {groups.map((group) => (\n              <tbody key={group.title} className=\"border-b last:border-b-0\">\n                <tr className=\"bg-muted/50\">\n                  {/* colgroup scope: every feature below inherits this heading. */}\n                  <th\n                    scope=\"colgroup\"\n                    colSpan={plans.length + 1}\n                    className=\"text-foreground px-4 py-2.5 text-sm font-semibold\"\n                  >\n                    {group.title}\n                  </th>\n                </tr>\n                {group.features.map((feature) => (\n                  <tr key={feature.label} className=\"border-t\">\n                    <th\n                      scope=\"row\"\n                      className=\"bg-background sticky left-0 px-4 py-3 text-sm font-normal\"\n                    >\n                      <span className=\"text-foreground\">{feature.label}</span>\n                      {feature.note ? (\n                        <span className=\"text-muted-foreground mt-0.5 block text-pretty text-xs\">\n                          {feature.note}\n                        </span>\n                      ) : null}\n                    </th>\n                    {plans.map((plan, index) => (\n                      <td\n                        key={plan.name}\n                        className={cn(\n                          \"px-4 py-3 text-center\",\n                          plan.featured && \"bg-accent/40\",\n                        )}\n                      >\n                        <Cell value={feature.values[index]} />\n                      </td>\n                    ))}\n                  </tr>\n                ))}\n              </tbody>\n            ))}\n          </table>\n        </div>\n\n        {note ? (\n          <p\n            className={cn(\n              \"text-muted-foreground text-pretty text-sm\",\n              align === \"center\" && \"text-center\",\n            )}\n          >\n            {note}\n          </p>\n        ) : null}\n      </Container>\n    </Section>\n  )\n}\n"
    }
  ],
  "docs": "Import styles/facade-tokens.css in your global stylesheet, after @import \"tailwindcss\". Set headingLevel to fit the page outline (sections default to h2). Pass next/link and next/image through the link and image props. Docs: https://facadeui.dev/components/pricing-comparison"
}
