{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pricing-card",
  "type": "registry:ui",
  "title": "Pricing Card",
  "description": "Pricing plan card with title, price, feature list, CTA, and an optional 'Popular' pill.",
  "dependencies": [
    "@saasflare/ui"
  ],
  "files": [
    {
      "path": "components/ui/pricing-card.tsx",
      "type": "registry:ui",
      "content": "// @toreview\n\"use client\"\n\n/**\n * @fileoverview Saasflare PricingCard — plan display for pricing pages.\n * @module packages/ui/components/ui/pricing-card\n * @layer core\n *\n * Displays a pricing plan with name, price, feature list, and CTA button.\n * Supports a \"featured\" variant for highlighting the recommended plan.\n *\n * Features accept either a plain string or a descriptor, so a real pricing\n * table can show what a tier *does not* include and explain a limit inline\n * without the consumer rebuilding the list markup.\n *\n * @example\n * import { PricingCard } from \"@saasflare/ui\";\n *\n * <PricingCard\n *   name=\"Pro\"\n *   price=\"$29\"\n *   period=\"month\"\n *   description=\"For growing teams\"\n *   features={[\"Unlimited projects\", \"Priority support\", \"Analytics\"]}\n *   cta={<Button>Get Started</Button>}\n *   featured\n * />\n *\n * @example\n * // Mixed list: tooltips and an excluded row\n * <PricingCard\n *   name=\"Free\"\n *   price=\"$0\"\n *   features={[\n *     \"3 projects\",\n *     { label: \"5 seats\", tooltip: \"Invite teammates from Settings → Team.\" },\n *     { label: \"API access\", excluded: true },\n *   ]}\n * />\n */\n\nimport * as React from \"react\"\nimport { CheckIcon, InfoIcon, MinusIcon } from \"@saasflare/ui\"\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@saasflare/ui\"\nimport { cn } from \"@saasflare/ui\"\nimport { useSaasflareProps, type SaasflareComponentProps } from \"@saasflare/ui\"\n\n/** A feature row with more to say than its label. */\ninterface PricingCardFeature {\n  /** Feature label. */\n  label: string\n  /** Explanation shown behind an info icon. */\n  tooltip?: string\n  /** Render as *not* included — muted, struck through, minus icon. */\n  excluded?: boolean\n}\n\n/** Props for the PricingCard component */\ninterface PricingCardProps extends Omit<React.ComponentProps<\"div\">, keyof SaasflareComponentProps>, SaasflareComponentProps {\n  /** Plan name (e.g. \"Starter\", \"Pro\", \"Enterprise\") */\n  name: string\n  /** Formatted price (e.g. \"$29\", \"Free\", \"$99\") */\n  price: string\n  /** Billing period (e.g. \"month\", \"year\") */\n  period?: string\n  /** Short plan description */\n  description?: string\n  /**\n   * Feature rows. Plain strings are treated as included features; use the\n   * object form for a tooltip or to mark a feature as excluded.\n   */\n  features: ReadonlyArray<string | PricingCardFeature>\n  /** CTA button element */\n  cta?: React.ReactNode\n  /** Highlight as recommended plan */\n  featured?: boolean\n  /**\n   * Ribbon text on a featured card. Defaults to `\"Recommended\"`.\n   * Set your own for other languages or a different message (\"Best value\").\n   */\n  badge?: React.ReactNode\n}\n\n/** Normalises the two accepted feature shapes into one. */\nfunction toFeature(feature: string | PricingCardFeature): PricingCardFeature {\n  return typeof feature === \"string\" ? { label: feature } : feature\n}\n\n/**\n * Mounts a TooltipProvider only when a tooltip is actually present, so a card\n * without tooltips stays free of the extra context — and a card *with* them\n * never crashes on a missing provider. Same pattern as SidebarProvider.\n */\nfunction FeatureList({ children, hasTooltip }: { children: React.ReactNode; hasTooltip: boolean }) {\n  const list = (\n    <ul className=\"mt-6 space-y-2.5\" role=\"list\">\n      {children}\n    </ul>\n  )\n  return hasTooltip ? <TooltipProvider delayDuration={150}>{list}</TooltipProvider> : list\n}\n\n/**\n * Pricing plan card with features list and CTA.\n *\n * @component\n * @layer core\n *\n * @example\n * <PricingCard\n *   name=\"Enterprise\"\n *   price=\"$99\"\n *   period=\"month\"\n *   description=\"For large organizations\"\n *   features={[\"Everything in Pro\", \"SSO\", \"Custom integrations\"]}\n *   cta={<Button variant=\"solid\" intent=\"primary\">Contact Sales</Button>}\n *   featured\n * />\n */\nfunction PricingCard({\n  name,\n  price,\n  period,\n  description,\n  features,\n  cta,\n  featured = false,\n  badge = \"Recommended\",\n  className,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: PricingCardProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  const rows = features.map(toFeature)\n  const hasTooltip = rows.some((row) => Boolean(row.tooltip))\n\n  return (\n    <div\n      {...props}\n      data-slot=\"pricing-card\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n      data-featured={featured ? \"true\" : undefined}\n      className={cn(\n        \"relative flex flex-col rounded-xl border bg-card p-6 text-card-foreground shadow-sm\",\n        featured && \"border-primary shadow-md ring-1 ring-primary/20\",\n        className\n      )}\n    >\n      {featured && badge && (\n        <div className=\"absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-primary px-3 py-0.5 text-xs font-medium text-primary-foreground\">\n          {badge}\n        </div>\n      )}\n      <div className=\"space-y-2\">\n        <h3 className=\"text-lg font-semibold\">{name}</h3>\n        {description && (\n          <p className=\"text-sm text-muted-foreground\">{description}</p>\n        )}\n      </div>\n      <div className=\"mt-4 flex items-baseline gap-1\">\n        <span className=\"text-3xl font-bold tracking-tight\">{price}</span>\n        {period && (\n          <span className=\"text-sm text-muted-foreground\">/{period}</span>\n        )}\n      </div>\n      <FeatureList hasTooltip={hasTooltip}>\n        {rows.map((feature) => {\n          return (\n            <li\n              key={feature.label}\n              data-excluded={feature.excluded ? \"true\" : undefined}\n              className={cn(\n                \"flex items-start gap-2 text-sm\",\n                feature.excluded && \"text-muted-foreground\"\n              )}\n            >\n              {feature.excluded ? (\n                <MinusIcon\n                  weight={sf.iconWeight}\n                  className=\"mt-0.5 size-4 shrink-0 text-muted-foreground\"\n                  aria-hidden=\"true\"\n                />\n              ) : (\n                <CheckIcon\n                  weight={sf.iconWeight}\n                  className=\"mt-0.5 size-4 shrink-0 text-success\"\n                  aria-hidden=\"true\"\n                />\n              )}\n              <span className={cn(feature.excluded && \"line-through decoration-muted-foreground/50\")}>\n                {feature.label}\n              </span>\n              {feature.tooltip && (\n                <Tooltip>\n                  <TooltipTrigger\n                    // A button, not a bare icon: tooltips must be reachable\n                    // by keyboard, and `aria-label` gives the trigger a name.\n                    type=\"button\"\n                    aria-label={`More about ${feature.label}`}\n                    className=\"mt-0.5 text-muted-foreground transition-colors hover:text-foreground\"\n                  >\n                    <InfoIcon weight={sf.iconWeight} className=\"size-4 shrink-0\" aria-hidden=\"true\" />\n                  </TooltipTrigger>\n                  <TooltipContent className=\"max-w-56 text-wrap\">{feature.tooltip}</TooltipContent>\n                </Tooltip>\n              )}\n            </li>\n          )\n        })}\n      </FeatureList>\n      {/* `mt-auto` pins the CTA to the bottom of the card. Without it the\n          button sits directly under the feature list, so in a row of plans\n          with different feature counts the CTAs land at different heights —\n          the one thing that makes an otherwise finished pricing table look\n          unfinished. Needs the parent grid to stretch its items, which is\n          the default. */}\n      {cta && <div className=\"mt-auto pt-6\">{cta}</div>}\n    </div>\n  )\n}\n\nexport { PricingCard, type PricingCardProps, type PricingCardFeature }\n",
      "target": "components/ui/pricing-card.tsx"
    }
  ],
  "$meta": {
    "source": "https://ui.saasflare.io/r/pricing-card.json"
  }
}
