{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pricing-table-four",
  "title": "Pricing Table Four",
  "description": "A modern pricing table component with gradient design",
  "dependencies": ["lucide-react", "class-variance-authority", "motion"],
  "registryDependencies": ["button", "label", "radio-group", "badge", "utils"],
  "files": [
    {
      "path": "src/registry/billingsdk/pricing-table-four.tsx",
      "content": "\"use client\";\n\nimport { Check, Package, Award, Building2 } from \"lucide-react\";\nimport { useState, useId } from \"react\";\nimport { motion, AnimatePresence } from \"motion/react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { type Plan } from \"@/lib/billingsdk-config\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Label } from \"@/components/ui/label\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport { Badge } from \"@/components/ui/badge\";\n\nconst sectionVariants = cva(\"py-32 relative overflow-hidden\", {\n  variants: {\n    size: {\n      small: \"py-12\",\n      medium: \"py-20\",\n      large: \"py-32\",\n    },\n    theme: {\n      minimal: \"bg-background\",\n      classic: \"bg-gradient-to-b from-background to-muted/20\",\n    },\n  },\n  defaultVariants: {\n    size: \"medium\",\n    theme: \"minimal\",\n  },\n});\n\nconst titleVariants = cva(\"font-bold mb-4 text-foreground\", {\n  variants: {\n    size: {\n      small: \"text-3xl lg:text-4xl\",\n      medium: \"text-4xl lg:text-5xl\",\n      large: \"text-4xl lg:text-6xl\",\n    },\n    theme: {\n      minimal: \"\",\n      classic:\n        \"bg-gradient-to-r from-foreground to-muted-foreground bg-clip-text text-transparent pb-1\",\n    },\n  },\n  defaultVariants: {\n    size: \"medium\",\n    theme: \"minimal\",\n  },\n});\n\nconst descriptionVariants = cva(\n  \"text-muted-foreground max-w-3xl mx-auto mb-2\",\n  {\n    variants: {\n      size: {\n        small: \"text-base lg:text-lg\",\n        medium: \"text-lg lg:text-xl\",\n        large: \"lg:text-xl\",\n      },\n      theme: {\n        minimal: \"\",\n        classic: \"\",\n      },\n    },\n    defaultVariants: {\n      size: \"medium\",\n      theme: \"minimal\",\n    },\n  },\n);\n\nconst cardVariants = cva(\n  \"relative h-full transition-all duration-300 rounded-lg border bg-card text-card-foreground\",\n  {\n    variants: {\n      size: {\n        small: \"p-4\",\n        medium: \"p-5\",\n        large: \"p-6\",\n      },\n      theme: {\n        minimal: \"hover:bg-muted/30\",\n        classic: \"hover:shadow-xl backdrop-blur-sm bg-card/50 border-border/50\",\n      },\n      highlight: {\n        true: \"\",\n        false: \"\",\n      },\n    },\n    compoundVariants: [\n      {\n        theme: \"classic\",\n        highlight: true,\n        className:\n          \"ring-2 ring-primary/20 border-primary/30 bg-gradient-to-b from-primary/5 to-transparent relative overflow-hidden shadow-xl\",\n      },\n      {\n        theme: \"minimal\",\n        highlight: true,\n        className: \"bg-muted/50 border-primary/20\",\n      },\n    ],\n    defaultVariants: {\n      size: \"large\",\n      theme: \"minimal\",\n      highlight: false,\n    },\n  },\n);\n\nconst toggleVariants = cva(\n  \"flex h-11 w-fit shrink-0 items-center rounded-md p-1 text-lg\",\n  {\n    variants: {\n      theme: {\n        minimal: \"bg-muted\",\n        classic:\n          \"bg-muted/50 backdrop-blur-sm border border-border/50 shadow-lg\",\n      },\n    },\n    defaultVariants: {\n      theme: \"minimal\",\n    },\n  },\n);\n\nconst priceTextVariants = cva(\"font-medium\", {\n  variants: {\n    size: {\n      small: \"text-2xl\",\n      medium: \"text-3xl\",\n      large: \"text-4xl\",\n    },\n    theme: {\n      minimal: \"\",\n      classic:\n        \"font-extrabold bg-gradient-to-r from-foreground to-muted-foreground bg-clip-text text-transparent\",\n    },\n  },\n  defaultVariants: {\n    size: \"large\",\n    theme: \"minimal\",\n  },\n});\n\nconst buttonVariants = cva(\n  \"w-full transition-all duration-300 hover:cursor-pointer\",\n  {\n    variants: {\n      theme: {\n        minimal:\n          \"shadow hover:bg-primary/90 h-9 py-2 group bg-primary text-primary-foreground ring-primary before:from-primary-foreground/20 after:from-primary-foreground/10 relative isolate inline-flex w-full items-center justify-center overflow-hidden rounded-md px-3 text-left text-sm font-medium ring-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\",\n        classic:\n          \"relative overflow-hidden bg-gradient-to-r from-primary to-primary/80 text-primary-foreground font-semibold py-3 px-6 rounded-lg hover:shadow-xl active:scale-95 border border-primary/20\",\n      },\n    },\n    defaultVariants: {\n      theme: \"minimal\",\n    },\n  },\n);\n\nconst featureIconVariants = cva(\"flex-none h-[1lh]\", {\n  variants: {\n    size: {\n      small: \"size-3\",\n      medium: \"size-4\",\n      large: \"size-4\",\n    },\n    theme: {\n      minimal: \"text-primary\",\n      classic: \"text-emerald-500\",\n    },\n  },\n  defaultVariants: {\n    size: \"large\",\n    theme: \"minimal\",\n  },\n});\n\nexport interface PricingTableFourProps extends VariantProps<\n  typeof sectionVariants\n> {\n  plans: Plan[];\n  title?: string;\n  description?: string;\n  subtitle?: string;\n  onPlanSelect?: (planId: string) => void;\n  className?: string;\n  showBillingToggle?: boolean;\n  billingToggleLabels?: {\n    monthly: string;\n    yearly: string;\n  };\n}\n\nconst defaultIcons = {\n  starter: <Package className=\"h-4 w-4\" />,\n  pro: <Award className=\"h-4 w-4\" />,\n  enterprise: <Building2 className=\"h-4 w-4\" />,\n};\n\nexport function PricingTableFour({\n  plans,\n  title = \"Choose Your Perfect Plan\",\n  description = \"Transform your project with our comprehensive pricing options designed for every need.\",\n  subtitle,\n  onPlanSelect,\n  className,\n  size = \"medium\",\n  theme = \"minimal\",\n  showBillingToggle = true,\n  billingToggleLabels = {\n    monthly: \"Monthly\",\n    yearly: \"Yearly\",\n  },\n}: PricingTableFourProps) {\n  const [isAnnually, setIsAnnually] = useState(false);\n  const uniqueId = useId();\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  const handlePlanSelect = (planId: string) => {\n    onPlanSelect?.(planId);\n  };\n\n  const getPlanIcon = (planId: string) => {\n    return (\n      defaultIcons[planId as keyof typeof defaultIcons] || (\n        <Package className=\"h-5 w-5\" />\n      )\n    );\n  };\n\n  return (\n    <section className={cn(sectionVariants({ size, theme }), className)}>\n      {/* Classic theme background elements */}\n      {theme === \"classic\" && (\n        <>\n          <div className=\"bg-grid-pattern absolute inset-0 opacity-5\" />\n          <div className=\"bg-primary/5 absolute top-1/2 left-1/2 h-96 w-96 -translate-x-1/2 -translate-y-1/2 rounded-full blur-3xl\" />\n          <div className=\"bg-secondary/5 absolute top-1/4 right-1/4 h-64 w-64 rounded-full blur-2xl\" />\n        </>\n      )}\n\n      <div className=\"relative container mx-auto max-w-7xl\">\n        {/* Header */}\n        <div className=\"mb-12 text-center\">\n          {subtitle && (\n            <p className=\"text-primary mb-3 text-sm font-medium tracking-wide uppercase\">\n              {subtitle}\n            </p>\n          )}\n          <h2 className={cn(titleVariants({ size, theme }))}>{title}</h2>\n          <p className={cn(descriptionVariants({ size, theme }))}>\n            {description}\n          </p>\n\n          {showBillingToggle && (\n            <div\n              className={cn(\n                \"mx-auto mt-8 flex justify-center\",\n                toggleVariants({ theme }),\n              )}\n            >\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={`${uniqueId}-monthly`}\n                    className=\"peer sr-only\"\n                  />\n                  <Label\n                    htmlFor={`${uniqueId}-monthly`}\n                    className=\"text-muted-foreground peer-data-[state=checked]:text-primary hover:text-foreground flex h-full cursor-pointer items-center justify-center px-2 font-semibold transition-all md:px-7\"\n                  >\n                    {billingToggleLabels.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={`${uniqueId}-annually`}\n                    className=\"peer sr-only\"\n                  />\n                  <Label\n                    htmlFor={`${uniqueId}-annually`}\n                    className=\"text-muted-foreground peer-data-[state=checked]:text-primary hover:text-foreground flex h-full cursor-pointer items-center justify-center gap-1 px-2 font-semibold transition-all md:px-7\"\n                  >\n                    {billingToggleLabels.yearly}\n                    {yearlyPriceDiscount > 0 && (\n                      <span className=\"bg-primary/10 text-primary border-primary/20 ml-1 rounded border px-2 py-0.5 text-xs font-medium\">\n                        Save {yearlyPriceDiscount}%\n                      </span>\n                    )}\n                  </Label>\n                </div>\n              </RadioGroup>\n            </div>\n          )}\n        </div>\n\n        <div\n          className={cn(\n            \"grid gap-6\",\n            plans.length === 1 && \"mx-auto max-w-md grid-cols-1\",\n            plans.length === 2 &&\n              \"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 &&\n              \"grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n          )}\n        >\n          {plans.map((plan, index) => (\n            <motion.div\n              key={plan.id}\n              className=\"group relative h-full\"\n              initial={{ opacity: 0, y: 20 }}\n              animate={{ opacity: 1, y: 0 }}\n              transition={{\n                duration: 0.4,\n                delay: index * 0.1,\n                ease: \"easeOut\",\n              }}\n            >\n              {/* Popular badge */}\n              {plan.badge && (\n                <Badge\n                  className={cn(\n                    \"absolute -top-3 left-1/2 z-20 -translate-x-1/2 transform\",\n                    theme === \"classic\"\n                      ? \"from-primary to-primary/80 text-primary-foreground border-primary/20 bg-gradient-to-r shadow-lg\"\n                      : \"bg-primary text-primary-foreground\",\n                  )}\n                >\n                  {plan.badge}\n                </Badge>\n              )}\n\n              {/* Classic theme highlight effect */}\n              {theme === \"classic\" && plan.highlight && (\n                <div className=\"via-primary absolute -top-px left-1/2 h-px w-32 -translate-x-1/2 bg-gradient-to-r from-transparent to-transparent\" />\n              )}\n\n              <div\n                className={cn(\n                  cardVariants({ size, theme, highlight: plan.highlight }),\n                )}\n              >\n                <div className=\"flex h-full flex-col\">\n                  {/* Icon and Title */}\n                  <div className=\"mb-4 flex items-start gap-4\">\n                    <div className=\"flex-1\">\n                      <h3\n                        className={cn(\n                          \"mb-1 text-xl font-bold\",\n                          theme === \"classic\" ? \"text-lg\" : \"\",\n                        )}\n                      >\n                        {plan.title}\n                      </h3>\n                      <p\n                        className={cn(\n                          \"text-muted-foreground text-sm\",\n                          theme === \"classic\" && \"text-foreground/80\",\n                        )}\n                      >\n                        {plan.description}\n                      </p>\n                    </div>\n                    <div\n                      className={cn(\n                        \"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg\",\n                        theme === \"classic\"\n                          ? \"bg-primary/10 text-primary border-primary/20 border\"\n                          : \"bg-muted text-foreground border-border border\",\n                      )}\n                    >\n                      {getPlanIcon(plan.id)}\n                    </div>\n                  </div>\n\n                  {/* Price */}\n                  <div className=\"mb-6\">\n                    <AnimatePresence mode=\"wait\">\n                      <motion.div\n                        key={isAnnually ? \"year\" : \"month\"}\n                        initial={{ opacity: 0, y: 10 }}\n                        animate={{ opacity: 1, y: 0 }}\n                        exit={{ opacity: 0, y: -10 }}\n                        transition={{ duration: 0.2 }}\n                      >\n                        {isAnnually ? (\n                          <div className=\"flex items-baseline gap-1\">\n                            <span\n                              className={cn(priceTextVariants({ size, theme }))}\n                            >\n                              {parseFloat(plan.yearlyPrice) >= 0 &&\n                                plan.yearlyPrice.toLowerCase() !== \"custom\" && (\n                                  <>{plan.currency}</>\n                                )}\n                              {plan.yearlyPrice}\n                            </span>\n                            <span className=\"text-muted-foreground text-sm\">\n                              /year\n                            </span>\n                            {calculateDiscount(\n                              plan.monthlyPrice,\n                              plan.yearlyPrice,\n                            ) > 0 && (\n                              <span\n                                className={cn(\n                                  \"ml-2 text-xs\",\n                                  theme === \"classic\"\n                                    ? \"font-semibold text-emerald-500\"\n                                    : \"text-primary font-medium\",\n                                )}\n                              >\n                                {calculateDiscount(\n                                  plan.monthlyPrice,\n                                  plan.yearlyPrice,\n                                )}\n                                % off\n                              </span>\n                            )}\n                          </div>\n                        ) : (\n                          <div className=\"flex items-baseline gap-1\">\n                            <span\n                              className={cn(priceTextVariants({ size, theme }))}\n                            >\n                              {parseFloat(plan.monthlyPrice) >= 0 &&\n                                plan.monthlyPrice.toLowerCase() !==\n                                  \"custom\" && <>{plan.currency}</>}\n                              {plan.monthlyPrice}\n                            </span>\n                            <span className=\"text-muted-foreground text-sm\">\n                              /month\n                            </span>\n                          </div>\n                        )}\n                      </motion.div>\n                    </AnimatePresence>\n                  </div>\n\n                  {/* CTA Button */}\n                  <div className=\"mb-6\">\n                    <Button\n                      onClick={() => handlePlanSelect(plan.id)}\n                      className={cn(\n                        buttonVariants({ theme }),\n                        !plan.highlight &&\n                          theme === \"minimal\" &&\n                          \"bg-secondary hover:bg-secondary/80 text-secondary-foreground\",\n                      )}\n                      variant={plan.highlight ? \"default\" : \"secondary\"}\n                    >\n                      {plan.buttonText}\n                      {theme === \"classic\" && plan.highlight && (\n                        <div className=\"absolute inset-0 translate-x-[-100%] bg-gradient-to-r from-white/0 via-white/10 to-white/0 transition-transform duration-700 hover:translate-x-[100%]\" />\n                      )}\n                    </Button>\n                  </div>\n\n                  {/* Features */}\n                  <div className=\"flex-1\">\n                    <ul className=\"space-y-3\">\n                      {plan.features.map((feature, featureIndex) => (\n                        <motion.li\n                          key={featureIndex}\n                          className=\"flex items-start gap-3\"\n                          initial={{ opacity: 0, x: -10 }}\n                          animate={{ opacity: 1, x: 0 }}\n                          transition={{\n                            duration: 0.3,\n                            delay: featureIndex * 0.05,\n                          }}\n                        >\n                          <Check\n                            className={cn(featureIconVariants({ size, theme }))}\n                          />\n                          <span\n                            className={cn(\n                              \"text-sm\",\n                              theme === \"classic\"\n                                ? \"text-foreground/90\"\n                                : \"text-muted-foreground\",\n                            )}\n                          >\n                            {feature.name}\n                          </span>\n                        </motion.li>\n                      ))}\n                    </ul>\n                  </div>\n                </div>\n              </div>\n            </motion.div>\n          ))}\n        </div>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/billingsdk/pricing-table-four.tsx"
    },
    {
      "path": "src/registry/billingsdk/demo/pricing-table-four-demo.tsx",
      "content": "\"use client\";\n\nimport { PricingTableFour } from \"@/components/billingsdk/pricing-table-four\";\nimport { plans } from \"@/lib/billingsdk-config\";\n\nexport function PricingTableFourDemo() {\n  return (\n    <PricingTableFour\n      plans={plans}\n      title=\"Choose Your Perfect Plan\"\n      theme=\"classic\"\n      description=\"Transform your project with our comprehensive pricing options designed for every need.\"\n      subtitle=\"Simple Pricing\"\n      onPlanSelect={(planId: string) => console.log(\"Selected plan:\", planId)}\n      size=\"medium\"\n      className=\"w-full\"\n      showBillingToggle={true}\n      billingToggleLabels={{\n        monthly: \"Monthly\",\n        yearly: \"Yearly\",\n      }}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "components/pricing-table-four-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"
}
