{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "comparison-1",
  "title": "Comparison 1: us versus the alternatives",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A product-versus-alternatives table where your column is lifted into its own card, with yes, partly, no and text values and a row of headline results. Phones switch between alternatives.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/segmented-control.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/comparison-1/comparison-1.tsx",
      "content": "// Ballmac UI: Comparison 1. https://ui.ballmac.com/blocks/comparison-1\n\"use client\"\n\nimport * as React from \"react\"\nimport { Check, Minus, X } from \"lucide-react\"\n\nimport { SegmentedControl, SegmentedControlItem } from \"@/components/ballmac/segmented-control\"\nimport { cn } from \"@/lib/utils\"\n\ntype Comparison1Value = boolean | \"partial\" | string\n\ntype Comparison1Competitor = {\n  /** Key used by the rows. */\n  key: string\n  /** Name shown in the header. */\n  name: string\n}\n\ntype Comparison1Row = {\n  /** The capability being compared. */\n  label: string\n  /** One short line of detail. */\n  hint?: string\n  /** Value per column: your product uses the key \"us\". true is a check, false a cross, \"partial\" a dash, any other text is shown as text. */\n  values: Record<string, Comparison1Value>\n}\n\ntype Comparison1Reason = { value: string; label: string }\n\ntype Comparison1Props = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n  /** Section heading. */\n  title?: string\n  /** One sentence under the heading. */\n  description?: string\n  /** Your product's name. */\n  product?: string\n  /** The alternatives. */\n  competitors?: Comparison1Competitor[]\n  /** What is compared. */\n  rows?: Comparison1Row[]\n  /** Headline reasons shown under the table. Pass an empty array to hide them. */\n  reasons?: Comparison1Reason[]\n}\n\nconst defaultCompetitors: Comparison1Competitor[] = [\n  { key: \"sheets\", name: \"Spreadsheets\" },\n  { key: \"legacy\", name: \"Legacy suite\" },\n]\n\nconst defaultRows: Comparison1Row[] = [\n  { label: \"Set up in under an hour\", hint: \"From sign-up to first invoice sent\", values: { us: true, sheets: true, legacy: false } },\n  { label: \"Real-time collaboration\", values: { us: true, sheets: \"partial\", legacy: false } },\n  { label: \"Automatic approvals\", hint: \"Rules, reminders and escalation\", values: { us: true, sheets: false, legacy: true } },\n  { label: \"Audit trail on every change\", values: { us: true, sheets: false, legacy: \"partial\" } },\n  { label: \"Bank and card sync\", values: { us: true, sheets: false, legacy: true } },\n  { label: \"Works on your phone\", values: { us: true, sheets: \"partial\", legacy: false } },\n  { label: \"Price for 10 people\", values: { us: \"$250 / mo\", sheets: \"Free\", legacy: \"$900 / mo\" } },\n]\n\nconst defaultReasons: Comparison1Reason[] = [\n  { value: \"5x\", label: \"faster month-end close\" },\n  { value: \"−62%\", label: \"time spent on approvals\" },\n  { value: \"1 hr\", label: \"median time to first invoice\" },\n]\n\nfunction Cell({ value, label, product }: { value: Comparison1Value; label: string; product?: boolean }) {\n  if (typeof value === \"string\" && value !== \"partial\") return <span className={cn(\"text-sm\", product && \"font-semibold\")}>{value}</span>\n  if (value === true)\n    return (\n      <span className={cn(\"inline-flex size-6 items-center justify-center rounded-full\", product ? \"bg-foreground text-background\" : \"bg-chart-2/15\")}>\n        <Check className=\"size-3.5\" aria-hidden=\"true\" />\n        <span className=\"sr-only\">{label}: yes</span>\n      </span>\n    )\n  if (value === \"partial\")\n    return (\n      <span className=\"bg-chart-3/20 inline-flex size-6 items-center justify-center rounded-full\">\n        <Minus className=\"size-3.5\" aria-hidden=\"true\" />\n        <span className=\"sr-only\">{label}: partly</span>\n      </span>\n    )\n  return (\n    <span className=\"text-muted-foreground inline-flex size-6 items-center justify-center\">\n      <X className=\"size-4\" aria-hidden=\"true\" />\n      <span className=\"sr-only\">{label}: no</span>\n    </span>\n  )\n}\n\nfunction Comparison1({\n  title = \"See how we stack up.\",\n  description = \"An honest look at where Acme fits next to the tools you might be using today.\",\n  product = \"Acme\",\n  competitors = defaultCompetitors,\n  rows = defaultRows,\n  reasons = defaultReasons,\n  className,\n  ...props\n}: Comparison1Props) {\n  const [pick, setPick] = React.useState(competitors[0]?.key ?? \"\")\n  const chosen = competitors.find((c) => c.key === pick) ?? competitors[0]\n\n  return (\n    <section data-slot=\"comparison-1\" className={cn(\"mx-auto max-w-5xl px-4 py-20 sm:px-6 md:py-28\", className)} {...props}>\n      <div className=\"mx-auto max-w-2xl text-center\">\n        <h2 className=\"text-3xl font-semibold tracking-[-0.035em] text-balance sm:text-4xl lg:text-5xl\">{title}</h2>\n        <p className=\"text-muted-foreground mt-4 text-lg text-pretty\">{description}</p>\n      </div>\n\n      {/* Phones: us against one alternative at a time. */}\n      {chosen && (\n        <div className=\"mt-10 md:hidden\">\n          <SegmentedControl aria-label=\"Compare with\" value={chosen.key} onValueChange={setPick} fullWidth>\n            {competitors.map((c) => (\n              <SegmentedControlItem key={c.key} value={c.key}>{c.name}</SegmentedControlItem>\n            ))}\n          </SegmentedControl>\n          <div className=\"bg-card mt-5 overflow-hidden rounded-3xl border\">\n            <div className=\"bg-muted/50 grid grid-cols-[1fr_5.5rem_5.5rem] items-center gap-2 border-b px-4 py-3 text-xs font-semibold\">\n              <span className=\"sr-only\">Capability</span>\n              <span className=\"col-start-2 text-center\">{product}</span>\n              <span className=\"text-muted-foreground text-center\">{chosen.name}</span>\n            </div>\n            <ul className=\"divide-y\">\n              {rows.map((r) => (\n                <li key={r.label} className=\"grid grid-cols-[1fr_5.5rem_5.5rem] items-center gap-2 px-4 py-3.5\">\n                  <span className=\"text-sm\">{r.label}</span>\n                  <span className=\"text-center\"><Cell value={r.values.us ?? false} label={`${product}, ${r.label}`} product /></span>\n                  <span className=\"text-center\"><Cell value={r.values[chosen.key] ?? false} label={`${chosen.name}, ${r.label}`} /></span>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </div>\n      )}\n\n      {/* Tablets and up: the product column is lifted into its own card. */}\n      <div className=\"mt-16 hidden md:block\">\n        <table className=\"w-full border-separate border-spacing-0 text-start\">\n          <caption className=\"sr-only\">{product} compared with {competitors.map((c) => c.name).join(\" and \")}</caption>\n          <thead>\n            <tr>\n              <th scope=\"col\" className=\"w-[38%] pb-5\"><span className=\"sr-only\">Capability</span></th>\n              <th scope=\"col\" className=\"bg-card relative w-[20%] rounded-t-3xl border-x border-t px-4 pt-7 pb-5 text-center shadow-[0_-30px_80px_-50px_rgb(0_0_0/0.35)]\">\n                <span aria-hidden=\"true\" className=\"bg-chart-1/60 absolute inset-x-8 top-0 h-px\" />\n                <span className=\"text-lg font-semibold tracking-tight\">{product}</span>\n              </th>\n              {competitors.map((c) => (\n                <th key={c.key} scope=\"col\" className=\"text-muted-foreground px-4 pb-5 text-center text-sm font-medium\">{c.name}</th>\n              ))}\n            </tr>\n          </thead>\n          <tbody>\n            {rows.map((r, i) => {\n              const last = i === rows.length - 1\n              return (\n                <tr key={r.label}>\n                  <th scope=\"row\" className=\"border-t py-4 pe-4 text-start font-normal\">\n                    <span className=\"block text-[15px] font-medium\">{r.label}</span>\n                    {r.hint && <span className=\"text-muted-foreground block text-sm\">{r.hint}</span>}\n                  </th>\n                  <td className={cn(\"bg-card border-x border-t px-4 py-4 text-center\", last && \"rounded-b-3xl border-b shadow-[0_40px_80px_-50px_rgb(0_0_0/0.35)]\")}>\n                    <Cell value={r.values.us ?? false} label={`${product}, ${r.label}`} product />\n                  </td>\n                  {competitors.map((c) => (\n                    <td key={c.key} className=\"border-t px-4 py-4 text-center\"><Cell value={r.values[c.key] ?? false} label={`${c.name}, ${r.label}`} /></td>\n                  ))}\n                </tr>\n              )\n            })}\n          </tbody>\n        </table>\n      </div>\n\n      {reasons.length > 0 && (\n        <dl className=\"mt-16 grid gap-px overflow-hidden rounded-3xl border bg-border sm:grid-cols-3\">\n          {reasons.map((r) => (\n            <div key={r.label} className=\"bg-card flex flex-col-reverse p-6 text-center sm:p-8\">\n              <dt className=\"text-muted-foreground mt-2 text-sm\">{r.label}</dt>\n              <dd className=\"text-4xl font-semibold tracking-[-0.04em] tabular-nums\">{r.value}</dd>\n            </div>\n          ))}\n        </dl>\n      )}\n    </section>\n  )\n}\n\nexport { Comparison1, type Comparison1Props, type Comparison1Row, type Comparison1Competitor, type Comparison1Reason, type Comparison1Value }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/comparison-1/comparison-1.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "comparison",
      "versus",
      "alternatives",
      "table",
      "switch",
      "features"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "A switch-from-X page section. Pass product, competitors=[{ key, name }] and rows=[{ label, hint?, values: { us, [key]: true | false | 'partial' | text } }].",
      "whenToUse": [
        "Landing pages aimed at people evaluating alternatives",
        "Migration and 'vs' pages"
      ],
      "whenNotToUse": [
        "Comparing your own plans (use pricing-3)"
      ],
      "composesWith": [
        "pricing-2",
        "testimonials-2",
        "cta-1",
        "faq-2"
      ],
      "a11y": [
        {
          "keys": "Arrow Left / Right (phones)",
          "action": "Switches the alternative shown"
        },
        {
          "keys": "Screen reader",
          "action": "Each cell reads as 'Name, capability: yes / partly / no'"
        }
      ],
      "customization": [
        "values use the key 'us' for your product",
        "reasons: [] hides the headline results"
      ]
    },
    "examples": [
      "comparison-1-demo",
      "comparison-1-single"
    ],
    "url": "https://ui.ballmac.com/components/comparison-1"
  },
  "categories": [
    "blocks",
    "comparison"
  ],
  "type": "registry:block"
}