{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pricing-table-three",
  "title": "Pricing Table Three",
  "description": "A pricing table component with a feature table",
  "dependencies": ["lucide-react", "class-variance-authority", "motion"],
  "registryDependencies": [
    "button",
    "card",
    "badge",
    "radio-group",
    "label",
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/billingsdk/pricing-table-three.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Check } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { type Plan } from \"@/lib/billingsdk-config\";\nimport { useState } from \"react\";\nimport { Label } from \"@/components/ui/label\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { AnimatePresence, motion } from \"motion/react\";\n\nconst sectionVariants = cva(\"mt-10 max-w-7xl mx-auto\", {\n  variants: {\n    variant: {\n      small: \"mt-6\",\n      medium: \"mt-8\",\n      large: \"mt-10\",\n    },\n  },\n  defaultVariants: {\n    variant: \"small\",\n  },\n});\n\nconst toggleContainerVariants = cva(\n  \"bg-muted flex h-11 w-fit shrink-0 items-center rounded-md p-1 text-lg\",\n  {\n    variants: {\n      variant: {\n        small: \"h-9 text-base\",\n        medium: \"h-10 text-lg\",\n        large: \"h-11 text-lg\",\n      },\n    },\n    defaultVariants: {\n      variant: \"large\",\n    },\n  },\n);\n\nconst labelPaddingVariants = cva(\"px-7\", {\n  variants: {\n    variant: {\n      small: \"px-5\",\n      medium: \"px-6\",\n      large: \"px-7\",\n    },\n  },\n  defaultVariants: {\n    variant: \"large\",\n  },\n});\n\nconst cardTitleVariants = cva(\"text-xl\", {\n  variants: {\n    variant: {\n      small: \"text-lg\",\n      medium: \"text-xl\",\n      large: \"text-xl\",\n    },\n  },\n  defaultVariants: {\n    variant: \"large\",\n  },\n});\n\nconst cardDescriptionVariants = cva(\"text-sm\", {\n  variants: {\n    variant: {\n      small: \"text-xs\",\n      medium: \"text-sm\",\n      large: \"text-sm\",\n    },\n  },\n  defaultVariants: {\n    variant: \"large\",\n  },\n});\n\nconst priceTextVariants = cva(\"text-4xl font-medium\", {\n  variants: {\n    variant: {\n      small: \"text-3xl\",\n      medium: \"text-4xl\",\n      large: \"text-4xl\",\n    },\n  },\n  defaultVariants: {\n    variant: \"large\",\n  },\n});\n\nconst featureIconVariants = cva(\"w-4 h-4\", {\n  variants: {\n    variant: {\n      small: \"w-3 h-3\",\n      medium: \"w-4 h-4\",\n      large: \"w-4 h-4\",\n    },\n  },\n  defaultVariants: {\n    variant: \"large\",\n  },\n});\n\nconst footerWrapperVariants = cva(\n  \"flex items-center justify-between bg-muted/50 p-6 border-t border-border\",\n  {\n    variants: {\n      variant: {\n        small: \"p-4\",\n        medium: \"p-5\",\n        large: \"p-6\",\n      },\n    },\n    defaultVariants: {\n      variant: \"large\",\n    },\n  },\n);\n\nconst footerTextVariants = cva(\n  \"text-lg font-medium text-card-foreground text-left w-full my-auto\",\n  {\n    variants: {\n      variant: {\n        small: \"text-base\",\n        medium: \"text-lg\",\n        large: \"text-lg\",\n      },\n    },\n    defaultVariants: {\n      variant: \"large\",\n    },\n  },\n);\n\nexport interface PricingTableProps extends VariantProps<\n  typeof sectionVariants\n> {\n  className?: string;\n  plans: Plan[];\n  onPlanSelect?: (planId: string) => void;\n  showFooter?: boolean;\n  footerText?: string;\n  footerButtonText?: string;\n  onFooterButtonClick?: () => void;\n}\n\nexport function PricingTableThree({\n  className,\n  plans,\n  onPlanSelect,\n  showFooter,\n  footerText,\n  footerButtonText,\n  onFooterButtonClick,\n  variant,\n}: PricingTableProps) {\n  const [isAnnually, setIsAnnually] = useState(false);\n\n  function calculateDiscount(\n    monthlyPrice: string,\n    yearlyPrice: string,\n  ): number {\n    const monthly = parseFloat(monthlyPrice);\n    const yearly = parseFloat(yearlyPrice);\n\n    if (\n      monthlyPrice.toLowerCase() === \"custom\" ||\n      yearlyPrice.toLowerCase() === \"custom\" ||\n      isNaN(monthly) ||\n      isNaN(yearly) ||\n      monthly === 0\n    ) {\n      return 0;\n    }\n\n    const discount = ((monthly * 12 - yearly) / (monthly * 12)) * 100;\n    return Math.round(discount);\n  }\n\n  const yearlyPriceDiscount = plans.length\n    ? Math.max(\n        ...plans.map((plan) =>\n          calculateDiscount(plan.monthlyPrice, plan.yearlyPrice),\n        ),\n      )\n    : 0;\n\n  return (\n    <div className={cn(sectionVariants({ variant }), className)}>\n      {/* Header Section with Toggle */}\n      <div className=\"mb-8 flex flex-col items-center justify-between gap-4 md:flex-row md:items-start md:gap-10\">\n        <div className={cn(toggleContainerVariants({ variant }))}>\n          <RadioGroup\n            defaultValue=\"monthly\"\n            className=\"h-full grid-cols-2\"\n            onValueChange={(value) => {\n              setIsAnnually(value === \"annually\");\n            }}\n          >\n            <div className='has-[button[data-state=\"checked\"]]:bg-background h-full rounded-md transition-all'>\n              <RadioGroupItem\n                value=\"monthly\"\n                id=\"monthly\"\n                className=\"peer sr-only\"\n              />\n              <Label\n                htmlFor=\"monthly\"\n                className={cn(\n                  \"text-muted-foreground peer-data-[state=checked]:text-primary flex h-full cursor-pointer items-center justify-center font-semibold\",\n                  labelPaddingVariants({ variant }),\n                )}\n              >\n                Monthly\n              </Label>\n            </div>\n            <div className='has-[button[data-state=\"checked\"]]:bg-background h-full rounded-md transition-all'>\n              <RadioGroupItem\n                value=\"annually\"\n                id=\"annually\"\n                className=\"peer sr-only\"\n              />\n              <Label\n                htmlFor=\"annually\"\n                className={cn(\n                  \"text-muted-foreground peer-data-[state=checked]:text-primary flex h-full cursor-pointer items-center justify-center gap-1 font-semibold\",\n                  labelPaddingVariants({ variant }),\n                )}\n              >\n                Yearly\n              </Label>\n            </div>\n          </RadioGroup>\n        </div>\n        <div className=\"flex justify-center\">\n          {yearlyPriceDiscount > 0 && (\n            <span className=\"text-muted-foreground mt-2 text-xs\">\n              Save upto {yearlyPriceDiscount}% with yearly plan\n            </span>\n          )}\n        </div>\n      </div>\n\n      <div\n        className={cn(\n          \"grid gap-4 md:gap-0\",\n          plans.length === 1 && \"mx-auto max-w-md grid-cols-1\",\n          plans.length === 2 && \"mx-auto max-w-4xl grid-cols-1 md:grid-cols-2\",\n          plans.length === 3 && \"grid-cols-1 md:grid-cols-3\",\n          plans.length === 4 && \"grid-cols-1 md:grid-cols-2 lg:grid-cols-4\",\n          plans.length >= 5 &&\n            \"grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n        )}\n      >\n        {plans.map((plan) => (\n          <Card\n            key={plan.id}\n            className={cn(\n              \"text-card-foreground relative flex flex-col rounded-xl border shadow-sm transition-all duration-200 md:rounded-none md:border-none md:shadow-none\",\n              plan.highlight === true\n                ? \"bg-muted/30 border-border z-10 shadow-lg md:-mt-8 md:rounded-md md:border-t\"\n                : \"bg-card\",\n            )}\n          >\n            {plan.badge && (\n              <Badge className=\"bg-secondary text-secondary-foreground absolute -top-3 left-1/2 -translate-x-1/2 transform px-3 py-1 text-xs\">\n                {plan.badge}\n              </Badge>\n            )}\n            <CardHeader className=\"pb-4\">\n              <div className=\"space-y-2\">\n                <h3\n                  className={cn(\n                    cardTitleVariants({ variant }),\n                    \"text-left font-semibold\",\n                  )}\n                >\n                  {plan.title}\n                </h3>\n                <p\n                  className={cn(\n                    cardDescriptionVariants({ variant }),\n                    \"text-muted-foreground w-full text-left\",\n                  )}\n                >\n                  {plan.description}\n                </p>\n              </div>\n              <div className=\"space-y-1 text-left\">\n                <AnimatePresence mode=\"wait\">\n                  {isAnnually ? (\n                    <motion.div\n                      key=\"yearly\"\n                      initial={{ opacity: 0, y: 10 }}\n                      animate={{ opacity: 1, y: 0 }}\n                      exit={{ opacity: 0, y: -10 }}\n                      transition={{ duration: 0.3 }}\n                    >\n                      <span\n                        className={cn(\n                          priceTextVariants({ variant }),\n                          \"text-left\",\n                        )}\n                      >\n                        {parseFloat(plan.yearlyPrice) >= 0 && (\n                          <>{plan.currency}</>\n                        )}\n                        {plan.yearlyPrice}\n                        {calculateDiscount(\n                          plan.monthlyPrice,\n                          plan.yearlyPrice,\n                        ) > 0 && (\n                          <span className=\"ml-2 text-xs underline\">\n                            {calculateDiscount(\n                              plan.monthlyPrice,\n                              plan.yearlyPrice,\n                            )}\n                            % off\n                          </span>\n                        )}\n                      </span>\n                      <p className=\"text-muted-foreground\">Per year</p>\n                    </motion.div>\n                  ) : (\n                    <motion.div\n                      key=\"monthly\"\n                      initial={{ opacity: 0, y: 10 }}\n                      animate={{ opacity: 1, y: 0 }}\n                      exit={{ opacity: 0, y: -10 }}\n                      transition={{ duration: 0.3 }}\n                    >\n                      <span\n                        className={cn(\n                          priceTextVariants({ variant }),\n                          \"text-left\",\n                        )}\n                      >\n                        {parseFloat(plan.monthlyPrice) >= 0 && (\n                          <>{plan.currency}</>\n                        )}\n                        {plan.monthlyPrice}\n                      </span>\n                      <p className=\"text-muted-foreground\">Per month</p>\n                    </motion.div>\n                  )}\n                </AnimatePresence>\n              </div>\n            </CardHeader>\n            <CardContent className=\"flex flex-1 flex-col space-y-6\">\n              <div className=\"flex-1 space-y-4\">\n                {plan.features.map((feature, index) => (\n                  <div key={index} className=\"flex items-center gap-3\">\n                    {feature.icon === \"check\" ? (\n                      <div className=\"bg-primary h-2 w-2 rounded-sm\"></div>\n                    ) : (\n                      <div\n                        className={cn(\n                          featureIconVariants({ variant }),\n                          feature.iconColor || \"text-muted-foreground\",\n                        )}\n                      >\n                        <Check\n                          className={cn(featureIconVariants({ variant }))}\n                        />\n                      </div>\n                    )}\n                    <span className=\"text-sm\">{feature.name}</span>\n                    <span className=\"text-muted-foreground ml-auto text-sm\">\n                      Included\n                    </span>\n                  </div>\n                ))}\n              </div>\n\n              <Button\n                className={cn(\n                  \"mt-auto w-full hover:cursor-pointer\",\n                  plan.highlight === true\n                    ? \"focus-visible:ring-ring hover:bg-primary/90 group bg-primary text-primary-foreground ring-primary before:from-primary-foreground/20 after:from-primary-foreground/10 relative isolate inline-flex h-9 w-full items-center justify-center gap-2 overflow-hidden rounded-md px-3 py-2 text-left text-sm font-medium whitespace-nowrap shadow ring-1 transition duration-300 ease-[cubic-bezier(0.4,0.36,0,1)] before:pointer-events-none before:absolute before:inset-0 before:-z-10 before:rounded-md before:bg-gradient-to-b before:opacity-80 before:transition-opacity before:duration-300 before:ease-[cubic-bezier(0.4,0.36,0,1)] after:pointer-events-none after:absolute after:inset-0 after:-z-10 after:rounded-md after:bg-gradient-to-b after:to-transparent after:mix-blend-overlay focus-visible:ring-1 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\"\n                    : \"bg-secondary hover:bg-secondary/80 text-secondary-foreground\",\n                )}\n                onClick={() => onPlanSelect?.(plan.id)}\n              >\n                {plan.buttonText}\n              </Button>\n            </CardContent>\n          </Card>\n        ))}\n      </div>\n\n      {/* Footer Section */}\n      {showFooter !== false && (\n        <div\n          className={cn(\n            footerWrapperVariants({ variant }),\n            plans.length === 1 && \"mx-auto max-w-md\",\n            plans.length === 2 && \"mx-auto max-w-4xl\",\n          )}\n        >\n          <div className=\"flex w-full flex-col justify-between gap-4 md:flex-row\">\n            <p className={cn(footerTextVariants({ variant }))}>\n              {footerText ||\n                \"Pre-negotiated discounts are available to early-stage startups and nonprofits.\"}\n            </p>\n            <Button\n              className=\"bg-secondary hover:bg-secondary/80 text-secondary-foreground px-6\"\n              onClick={onFooterButtonClick}\n            >\n              {footerButtonText || \"Apply now\"}\n            </Button>\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/billingsdk/pricing-table-three.tsx"
    },
    {
      "path": "src/registry/billingsdk/demo/pricing-table-three-demo.tsx",
      "content": "\"use client\";\n\nimport { PricingTableThree } from \"@/components/billingsdk/pricing-table-three\";\nimport { plans } from \"@/lib/billingsdk-config\";\n\nexport function PricingTableThreeDemo() {\n  return (\n    <PricingTableThree\n      plans={plans}\n      onPlanSelect={(planId) => console.log(\"Selected plan:\", planId)}\n      className={\"mx-auto w-full max-w-4xl\"}\n      variant=\"small\"\n      showFooter={true}\n      footerText=\"Pre-negotiated discounts are available to early-stage startups and nonprofits.\"\n      footerButtonText=\"Apply now\"\n      onFooterButtonClick={() => console.log(\"Footer button clicked\")}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "components/pricing-table-three-demo.tsx"
    },
    {
      "path": "src/registry/lib/billingsdk-config.ts",
      "content": "export interface Plan {\n  id: string;\n  title: string;\n  description: string;\n  highlight?: boolean;\n  type?: \"monthly\" | \"yearly\";\n  currency?: string;\n  monthlyPrice: string;\n  yearlyPrice: string;\n  buttonText: string;\n  badge?: string;\n  features: {\n    name: string;\n    icon: string;\n    iconColor?: string;\n  }[];\n}\n\nexport interface CurrentPlan {\n  plan: Plan;\n  type: \"monthly\" | \"yearly\" | \"custom\";\n  price?: string;\n  nextBillingDate: string;\n  paymentMethod: string;\n  status: \"active\" | \"inactive\" | \"past_due\" | \"cancelled\";\n}\n\nexport const plans: Plan[] = [\n  {\n    id: \"starter\",\n    title: \"Starter\",\n    description: \"For developers testing out Liveblocks locally.\",\n    currency: \"$\",\n    monthlyPrice: \"0\",\n    yearlyPrice: \"0\",\n    buttonText: \"Start today for free\",\n    features: [\n      {\n        name: \"Presence\",\n        icon: \"check\",\n        iconColor: \"text-green-500\",\n      },\n      {\n        name: \"Comments\",\n        icon: \"check\",\n        iconColor: \"text-orange-500\",\n      },\n      {\n        name: \"Notifications\",\n        icon: \"check\",\n        iconColor: \"text-teal-500\",\n      },\n      {\n        name: \"Text Editor\",\n        icon: \"check\",\n        iconColor: \"text-blue-500\",\n      },\n      {\n        name: \"Sync Datastore\",\n        icon: \"check\",\n        iconColor: \"text-zinc-500\",\n      },\n    ],\n  },\n  {\n    id: \"pro\",\n    title: \"Pro\",\n    description: \"For companies adding collaboration in production.\",\n    currency: \"$\",\n    monthlyPrice: \"20\",\n    yearlyPrice: \"199\",\n    buttonText: \"Sign up\",\n    badge: \"Most popular\",\n    highlight: true,\n    features: [\n      {\n        name: \"Presence\",\n        icon: \"check\",\n        iconColor: \"text-green-500\",\n      },\n      {\n        name: \"Comments\",\n        icon: \"check\",\n        iconColor: \"text-orange-500\",\n      },\n      {\n        name: \"Notifications\",\n        icon: \"check\",\n        iconColor: \"text-teal-500\",\n      },\n      {\n        name: \"Text Editor\",\n        icon: \"check\",\n        iconColor: \"text-blue-500\",\n      },\n      {\n        name: \"Sync Datastore\",\n        icon: \"check\",\n        iconColor: \"text-zinc-500\",\n      },\n    ],\n  },\n  {\n    id: \"enterprise\",\n    title: \"Enterprise\",\n    description:\n      \"For organizations that need more support and compliance features.\",\n    currency: \"$\",\n    monthlyPrice: \"Custom\",\n    yearlyPrice: \"Custom\",\n    buttonText: \"Contact sales\",\n    features: [\n      {\n        name: \"Presence\",\n        icon: \"check\",\n        iconColor: \"text-green-500\",\n      },\n      {\n        name: \"Comments\",\n        icon: \"check\",\n        iconColor: \"text-orange-500\",\n      },\n      {\n        name: \"Notifications\",\n        icon: \"check\",\n        iconColor: \"text-teal-500\",\n      },\n      {\n        name: \"Text Editor\",\n        icon: \"check\",\n        iconColor: \"text-blue-500\",\n      },\n      {\n        name: \"Sync Datastore\",\n        icon: \"check\",\n        iconColor: \"text-zinc-500\",\n      },\n    ],\n  },\n];\n",
      "type": "registry:lib",
      "target": "lib/billingsdk-config.ts"
    }
  ],
  "type": "registry:block"
}
