{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "banner",
  "title": "Top Banner",
  "description": "A banner component",
  "dependencies": ["lucide-react", "motion"],
  "registryDependencies": ["button", "utils"],
  "files": [
    {
      "path": "src/registry/billingsdk/banner.tsx",
      "content": "\"use client\";\n\nimport type React from \"react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport { X } from \"lucide-react\";\nimport { useState, useEffect } from \"react\";\nimport { motion, AnimatePresence } from \"motion/react\";\n\nexport interface BannerProps {\n  variant?:\n    | \"default\"\n    | \"minimal\"\n    | \"popup\"\n    | \"destructive\"\n    | \"warning\"\n    | \"success\"\n    | \"info\"\n    | \"announcement\";\n  title: string;\n  description?: string;\n  buttonText?: string;\n  buttonIcon?: React.ReactNode;\n  buttonLink?: string;\n  dismissable?: boolean;\n  className?: string;\n  autoDismiss?: number; // in ms\n  onDismiss?: () => void;\n  gradientColors?: string[];\n}\n\nexport function Banner({\n  variant = \"default\",\n  title,\n  description,\n  buttonText,\n  buttonIcon,\n  buttonLink,\n  dismissable = true,\n  className,\n  autoDismiss,\n  onDismiss,\n  gradientColors,\n}: BannerProps) {\n  const [isVisible, setIsVisible] = useState(true);\n\n  useEffect(() => {\n    if (isVisible && autoDismiss) {\n      const timer = setTimeout(() => handleDismiss(), autoDismiss);\n      return () => clearTimeout(timer);\n    }\n  }, [isVisible, autoDismiss]);\n\n  const handleDismiss = () => {\n    setIsVisible(false);\n    onDismiss?.();\n  };\n\n  const getVariantStyles = () => {\n    const hasGradient = gradientColors && gradientColors.length > 0;\n\n    switch (variant) {\n      case \"minimal\":\n        return {\n          container: hasGradient\n            ? \"sticky top-0 z-50 w-full border-b backdrop-blur supports-[backdrop-filter]:bg-card/60\"\n            : \"sticky top-0 z-50 w-full border-b bg-card/95 backdrop-blur supports-[backdrop-filter]:bg-card/60\",\n          wrapper:\n            \"relative container mx-auto flex flex-col sm:flex-row md:items-start items-center justify-center px-3 sm:px-4 py-2 gap-2 sm:gap-4 max-w-2xl\",\n          content:\n            \"flex flex-col sm:flex-row items-start sm:items-center gap-1 sm:gap-2\",\n          title: \"text-sm font-medium text-card-foreground leading-tight\",\n          description: \"text-xs text-muted-foreground sm:ml-2\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto\",\n        };\n      case \"popup\":\n        return {\n          container: hasGradient\n            ? \"fixed top-4 left-1/2 -translate-x-1/2 z-50 max-w-sm sm:max-w-md w-[90%] sm:w-auto border border-border rounded-lg shadow-lg backdrop-blur\"\n            : \"fixed top-4 left-1/2 -translate-x-1/2 z-50 max-w-sm sm:max-w-md w-[90%] sm:w-auto bg-popover border border-border rounded-lg shadow-lg backdrop-blur\",\n          wrapper:\n            \"relative flex flex-col sm:flex-row items-start sm:items-center justify-between px-3 sm:px-4 py-3 gap-3 sm:gap-4\",\n          content:\n            \"flex flex-col sm:flex-row items-start sm:items-center gap-2 flex-1\",\n          title: \"text-sm font-medium text-popover-foreground leading-snug\",\n          description: \"text-xs text-muted-foreground\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto flex-shrink-0 pr-12\",\n        };\n      case \"destructive\":\n        return {\n          container: hasGradient\n            ? \"sticky top-0 z-50 w-full border-b border-destructive/20 backdrop-blur supports-[backdrop-filter]:bg-destructive/10\"\n            : \"sticky top-0 z-50 w-full border-b bg-destructive text-destructive-foreground shadow-sm backdrop-blur\",\n          wrapper:\n            \"relative container mx-auto flex flex-col sm:flex-row md:items-start items-center justify-between px-3 sm:px-4 py-2 sm:py-3 gap-2 sm:gap-4\",\n          content:\n            \"flex flex-col sm:flex-row md:items-start items-center gap-2 w-full\",\n          title: hasGradient\n            ? \"text-sm font-medium text-destructive leading-tight\"\n            : \"text-sm font-medium text-destructive-foreground leading-tight\",\n          description: hasGradient\n            ? \"text-xs text-destructive/80\"\n            : \"text-xs text-destructive-foreground/80\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto pr-12\",\n        };\n      case \"warning\":\n        return {\n          container: hasGradient\n            ? \"sticky top-0 z-50 w-full border-b border-yellow-500/20 backdrop-blur supports-[backdrop-filter]:bg-yellow-50/60 dark:supports-[backdrop-filter]:bg-yellow-950/60\"\n            : \"sticky top-0 z-50 w-full border-b bg-yellow-50 dark:bg-yellow-950 border-yellow-200 dark:border-yellow-800 shadow-sm backdrop-blur\",\n          wrapper:\n            \"relative container mx-auto flex flex-col sm:flex-row md:items-start items-center justify-between px-3 sm:px-4 py-2 sm:py-3 gap-2 sm:gap-4\",\n          content:\n            \"flex flex-col sm:flex-row md:items-start items-center gap-2 w-full\",\n          title: hasGradient\n            ? \"text-sm font-medium text-yellow-800 dark:text-yellow-200 leading-tight\"\n            : \"text-sm font-medium text-yellow-800 dark:text-yellow-200 leading-tight\",\n          description: hasGradient\n            ? \"text-xs text-yellow-700 dark:text-yellow-300\"\n            : \"text-xs text-yellow-700 dark:text-yellow-300\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto pr-12\",\n        };\n      case \"success\":\n        return {\n          container: hasGradient\n            ? \"sticky top-0 z-50 w-full border-b border-green-500/20 backdrop-blur supports-[backdrop-filter]:bg-green-50/60 dark:supports-[backdrop-filter]:bg-green-950/60\"\n            : \"sticky top-0 z-50 w-full border-b bg-green-50 dark:bg-green-950 border-green-200 dark:border-green-800 shadow-sm backdrop-blur\",\n          wrapper:\n            \"relative container mx-auto flex flex-col sm:flex-row md:items-start items-center justify-between px-3 sm:px-4 py-2 sm:py-3 gap-2 sm:gap-4\",\n          content:\n            \"flex flex-col sm:flex-row md:items-start items-center gap-2 w-full\",\n          title: hasGradient\n            ? \"text-sm font-medium text-green-800 dark:text-green-200 leading-tight\"\n            : \"text-sm font-medium text-green-800 dark:text-green-200 leading-tight\",\n          description: hasGradient\n            ? \"text-xs text-green-700 dark:text-green-300\"\n            : \"text-xs text-green-700 dark:text-green-300\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto pr-12\",\n        };\n      case \"info\":\n        return {\n          container: hasGradient\n            ? \"sticky top-0 z-50 w-full border-b border-blue-500/20 backdrop-blur supports-[backdrop-filter]:bg-blue-50/60 dark:supports-[backdrop-filter]:bg-blue-950/60\"\n            : \"sticky top-0 z-50 w-full border-b bg-blue-50 dark:bg-blue-950 border-blue-200 dark:border-blue-800 shadow-sm backdrop-blur\",\n          wrapper:\n            \"relative container mx-auto flex flex-col sm:flex-row md:items-start items-center justify-between px-3 sm:px-4 py-2 sm:py-3 gap-2 sm:gap-4\",\n          content:\n            \"flex flex-col sm:flex-row md:items-start items-center gap-2 w-full\",\n          title: hasGradient\n            ? \"text-sm font-medium text-blue-800 dark:text-blue-200 leading-tight\"\n            : \"text-sm font-medium text-blue-800 dark:text-blue-200 leading-tight\",\n          description: hasGradient\n            ? \"text-xs text-blue-700 dark:text-blue-300\"\n            : \"text-xs text-blue-700 dark:text-blue-300\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto pr-12\",\n        };\n      case \"announcement\":\n        return {\n          container: hasGradient\n            ? \"sticky top-0 z-50 w-full border-b border-purple-500/20 backdrop-blur supports-[backdrop-filter]:bg-purple-50/60 dark:supports-[backdrop-filter]:bg-purple-950/60\"\n            : \"sticky top-0 z-50 w-full border-b bg-purple-50 dark:bg-purple-950 border-purple-200 dark:border-purple-800 shadow-sm backdrop-blur\",\n          wrapper:\n            \"relative container mx-auto flex flex-col sm:flex-row md:items-start items-center justify-between px-3 sm:px-4 py-2 sm:py-3 gap-2 sm:gap-4\",\n          content:\n            \"flex flex-col sm:flex-row md:items-start items-center gap-2 w-full\",\n          title: hasGradient\n            ? \"text-sm font-medium text-purple-800 dark:text-purple-200 leading-tight\"\n            : \"text-sm font-medium text-purple-800 dark:text-purple-200 leading-tight\",\n          description: hasGradient\n            ? \"text-xs text-purple-700 dark:text-purple-300\"\n            : \"text-xs text-purple-700 dark:text-purple-300\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto pr-12\",\n        };\n      default:\n        return {\n          container: hasGradient\n            ? \"sticky top-0 z-50 w-full border-b text-primary-foreground shadow-sm text-left backdrop-blur\"\n            : \"sticky top-0 z-50 w-full border-b bg-primary text-primary-foreground shadow-sm text-left backdrop-blur\",\n          wrapper:\n            \"relative container mx-auto flex flex-col sm:flex-row md:items-start items-center justify-between px-3 sm:px-4 py-2 sm:py-3 gap-2 sm:gap-4\",\n          content:\n            \"flex flex-col text-center sm:text-left sm:flex-row md:items-start items-center gap-2 w-full\",\n          title: hasGradient\n            ? \"text-sm font-medium text-foreground leading-tight\"\n            : \"text-sm font-medium text-primary-foreground leading-tight\",\n          description: hasGradient\n            ? \"text-xs text-foreground/80\"\n            : \"text-xs text-primary-foreground/80\",\n          actions:\n            \"flex items-center justify-center md:justify-start gap-2 sm:self-auto pr-12\",\n        };\n    }\n  };\n\n  const styles = getVariantStyles();\n\n  const getGradientBackground = () => {\n    if (!gradientColors || gradientColors.length === 0) return null;\n\n    // Use the exact gradient from the example or custom colors with proper spacing\n    let gradientStops;\n    if (gradientColors.length === 4) {\n      // Match the original example exactly\n      gradientStops = `${gradientColors[0]} 0%, ${gradientColors[1]} 12.5%, ${gradientColors[2]} 25%, ${gradientColors[3]} 37.5%, ${gradientColors[0]} 50%`;\n    } else {\n      // For other numbers of colors, use equal spacing\n      gradientStops = gradientColors\n        .map((color, index) => {\n          const percentage = (index / gradientColors.length) * 100;\n          return `${color} ${percentage}%`;\n        })\n        .join(\", \");\n    }\n\n    // Use consistent filter for better visibility\n    const filterValue = \"saturate(1.8) brightness(1.2)\";\n\n    return (\n      <div\n        className=\"absolute inset-0 z-[-1]\"\n        style={{\n          maskImage:\n            \"linear-gradient(to bottom, white, transparent), radial-gradient(circle at top center, white, transparent)\",\n          maskComposite: \"intersect\",\n          animation: \"fd-moving-banner 30s linear infinite\",\n          backgroundImage: `repeating-linear-gradient(70deg, ${gradientStops})`,\n          backgroundSize: \"200% 100%\",\n          filter: filterValue,\n        }}\n      />\n    );\n  };\n\n  const getAnimationProps = () => {\n    switch (variant) {\n      case \"popup\":\n        return {\n          initial: { opacity: 0, scale: 0.95, y: -20 },\n          animate: { opacity: 1, scale: 1, y: 0 },\n          exit: { opacity: 0, scale: 0.95, y: -20 },\n        };\n      default:\n        return {\n          initial: { opacity: 0, y: -12 },\n          animate: { opacity: 1, y: 0 },\n          exit: { opacity: 0, y: -12 },\n        };\n    }\n  };\n\n  return (\n    <AnimatePresence>\n      {isVisible && (\n        <motion.div\n          {...getAnimationProps()}\n          transition={{ duration: 0.15, ease: \"easeOut\" }}\n          className={cn(styles.container, className)}\n        >\n          {getGradientBackground()}\n          <div className={styles.wrapper}>\n            {/* Content */}\n            <div className={styles.content}>\n              <div\n                className={\n                  variant === \"minimal\"\n                    ? \"flex flex-col gap-0.5 md:flex-row md:gap-2\"\n                    : \"\"\n                }\n              >\n                <p className={styles.title}>{title}</p>\n                {description && (\n                  <p className={styles.description}>{description}</p>\n                )}\n              </div>\n            </div>\n\n            {/* Actions */}\n            <div className={styles.actions}>\n              {buttonText && variant !== \"minimal\" && (\n                <Button\n                  variant={variant === \"default\" ? \"secondary\" : \"default\"}\n                  size=\"sm\"\n                  onClick={() => window.open(buttonLink, \"_blank\")}\n                  className={\"h-8\"}\n                >\n                  {buttonIcon && (\n                    <div className=\"mr-1 flex-shrink-0\">{buttonIcon}</div>\n                  )}\n                  {buttonText}\n                </Button>\n              )}\n            </div>\n\n            {/* Close button (always top-right) */}\n            {dismissable && (\n              <Button\n                variant=\"ghost\"\n                size=\"icon\"\n                onClick={handleDismiss}\n                className={cn(\n                  \"absolute top-2 right-1 h-8 w-8 sm:top-1/2 sm:right-2 sm:-translate-y-1/2\",\n                  gradientColors && gradientColors.length > 0\n                    ? \"hover:bg-foreground/20 text-foreground\"\n                    : variant === \"default\" &&\n                        \"hover:bg-primary-foreground/20 text-primary-foreground\",\n                  variant === \"popup\" &&\n                    !gradientColors &&\n                    \"hover:bg-accent text-popover-foreground\",\n                  variant === \"minimal\" &&\n                    !gradientColors &&\n                    \"hover:bg-accent text-card-foreground\",\n                  variant === \"warning\" &&\n                    !gradientColors &&\n                    \"text-yellow-800 hover:bg-yellow-200 dark:text-yellow-200 dark:hover:bg-yellow-800\",\n                  variant === \"success\" &&\n                    !gradientColors &&\n                    \"text-green-800 hover:bg-green-200 dark:text-green-200 dark:hover:bg-green-800\",\n                  variant === \"info\" &&\n                    !gradientColors &&\n                    \"text-blue-800 hover:bg-blue-200 dark:text-blue-200 dark:hover:bg-blue-800\",\n                  variant === \"announcement\" &&\n                    !gradientColors &&\n                    \"text-purple-800 hover:bg-purple-200 dark:text-purple-200 dark:hover:bg-purple-800\",\n                )}\n              >\n                <X className=\"h-4 w-4\" />\n                <span className=\"sr-only\">Dismiss</span>\n              </Button>\n            )}\n          </div>\n        </motion.div>\n      )}\n    </AnimatePresence>\n  );\n}\n\n// Add CSS keyframes for the moving banner animation\nif (typeof document !== \"undefined\") {\n  const styleSheet = document.createElement(\"style\");\n  styleSheet.textContent = `\n    @keyframes fd-moving-banner {\n      0% {\n        background-position: 0% 50%;\n      }\n      50% {\n        background-position: 100% 50%;\n      }\n      100% {\n        background-position: 0% 50%;\n      }\n    }\n  `;\n  document.head.appendChild(styleSheet);\n}\n",
      "type": "registry:component",
      "target": "components/billingsdk/banner.tsx"
    },
    {
      "path": "src/registry/billingsdk/demo/banner-demo.tsx",
      "content": "import { Banner } from \"@/components/billingsdk/banner\";\n\nexport default function FreeTrialBannerDemo() {\n  return (\n    <div className=\"bg-background-secondary flex h-full min-h-[500px] w-full flex-col gap-6 overflow-hidden rounded-lg border-2\">\n      <Banner\n        title=\"🎉 Start your free trial today!\"\n        description=\"Get 30 days free access to all premium features\"\n        buttonText=\"Start Free Trial\"\n        buttonLink=\"https://example.com/signup\"\n        variant=\"default\" // default, minimal, popup\n      />\n\n      {/* minimal hero example */}\n      <section className=\"flex flex-col items-center justify-center gap-4 py-16 text-center\">\n        <h1 className=\"text-foreground-secondary text-3xl font-bold tracking-tight\">\n          Create next-generation digital products\n        </h1>\n        <div className=\"flex flex-col gap-2\">\n          <p className=\"text-muted-foreground max-w-md\">\n            Build faster with our platform\n          </p>\n          <a className=\"hover:text-primary underline underline-offset-4 transition\">\n            Get Started →\n          </a>\n        </div>\n      </section>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/banner-demo.tsx"
    }
  ],
  "type": "registry:block"
}
