{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "credit-card",
  "title": "Credit Card",
  "description": "Interactive credit-card form with visual card preview and validations.",
  "dependencies": [
    "framer-motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "card",
    "input",
    "select"
  ],
  "files": [
    {
      "path": "components/ui/credit-card.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { useState, useRef, useEffect } from 'react'\nimport { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'\nimport { cn } from '@/lib/utils'\nimport { Input } from '@/components/ui/input'\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select'\nimport { Card } from '@/components/ui/card'\nimport { CreditCard as CreditCardIcon, Lock } from 'lucide-react'\n\n// Enhanced Card vendor SVG icons with better styling\nconst CardIcons = {\n  visa: (\n    <svg viewBox=\"0 0 40 24\" className=\"w-10 h-6\">\n      <rect width=\"40\" height=\"24\" rx=\"4\" fill=\"#1A1F71\" />\n      <text\n        x=\"20\"\n        y=\"15\"\n        textAnchor=\"middle\"\n        fill=\"white\"\n        fontSize=\"8\"\n        fontWeight=\"bold\"\n        fontFamily=\"Arial, sans-serif\"\n      >\n        VISA\n      </text>\n    </svg>\n  ),\n  mastercard: (\n    <svg viewBox=\"0 0 40 24\" className=\"w-10 h-6\">\n      <rect width=\"40\" height=\"24\" rx=\"4\" fill=\"#000\" />\n      <circle cx=\"15\" cy=\"12\" r=\"7\" fill=\"#EB001B\" />\n      <circle cx=\"25\" cy=\"12\" r=\"7\" fill=\"#FF5F00\" />\n    </svg>\n  ),\n  amex: (\n    <svg viewBox=\"0 0 40 24\" className=\"w-10 h-6\">\n      <rect width=\"40\" height=\"24\" rx=\"4\" fill=\"#006FCF\" />\n      <text\n        x=\"20\"\n        y=\"15\"\n        textAnchor=\"middle\"\n        fill=\"white\"\n        fontSize=\"6\"\n        fontWeight=\"bold\"\n        fontFamily=\"Arial, sans-serif\"\n      >\n        AMEX\n      </text>\n    </svg>\n  ),\n  discover: (\n    <svg viewBox=\"0 0 40 24\" className=\"w-10 h-6\">\n      <rect width=\"40\" height=\"24\" rx=\"4\" fill=\"#FF6000\" />\n      <text\n        x=\"20\"\n        y=\"15\"\n        textAnchor=\"middle\"\n        fill=\"white\"\n        fontSize=\"5\"\n        fontWeight=\"bold\"\n        fontFamily=\"Arial, sans-serif\"\n      >\n        DISCOVER\n      </text>\n    </svg>\n  ),\n  generic: <CreditCardIcon className=\"w-10 h-6 opacity-60\" />,\n}\n\n// Card style variants with improved base style\nexport type CardStyle =\n  | 'base'\n  | 'shiny-silver'\n  | 'amex-green'\n  | 'amex-black'\n  | 'metal'\n\nconst cardStyles: Record<CardStyle, string> = {\n  // Base style matching shadcn card (white/clean)\n  base: 'bg-card border text-card-foreground shadow-sm',\n  'shiny-silver':\n    'bg-gradient-to-br from-gray-300 via-gray-100 to-gray-300 border-gray-400 text-gray-800 shadow-2xl',\n  'amex-green':\n    'bg-gradient-to-br from-green-700 via-green-600 to-green-800 border-green-500 text-white shadow-xl',\n  'amex-black':\n    'bg-gradient-to-br from-gray-900 via-black to-gray-800 border-gray-600 text-white shadow-2xl',\n  metal:\n    'bg-gradient-to-br from-slate-600 via-slate-500 to-slate-700 border-slate-400 text-white shadow-2xl backdrop-blur-sm',\n}\n\nconst cardBackStyles: Record<CardStyle, string> = {\n  base: 'bg-muted border text-muted-foreground shadow-sm',\n  'shiny-silver':\n    'bg-gradient-to-br from-gray-400 via-gray-200 to-gray-400 border-gray-500 text-gray-800',\n  'amex-green':\n    'bg-gradient-to-br from-green-800 via-green-700 to-green-900 border-green-600 text-white',\n  'amex-black':\n    'bg-gradient-to-br from-black via-gray-900 to-black border-gray-700 text-white',\n  metal:\n    'bg-gradient-to-br from-slate-700 via-slate-600 to-slate-800 border-slate-500 text-white',\n}\n\nexport interface CreditCardValue {\n  cardholderName: string\n  cardNumber: string\n  expiryMonth: string\n  expiryYear: string\n  cvv: string\n}\n\nexport interface CreditCardProps {\n  value?: CreditCardValue\n  onChange?: (value: CreditCardValue) => void\n  onValidationChange?: (isValid: boolean, errors: ValidationErrors) => void\n  className?: string\n  ref?: React.RefObject<CreditCardRef>\n  cvvLabel?: 'CCV' | 'CVC'\n  cardStyle?: CardStyle\n  showVendor?: boolean\n}\n\nexport interface CreditCardRef {\n  validate: () => boolean\n  isValid: () => boolean\n  focus: () => void\n  reset: () => void\n  getErrors: () => ValidationErrors\n}\n\ninterface ValidationErrors {\n  cardholderName?: string\n  cardNumber?: string\n  expiryMonth?: string\n  expiryYear?: string\n  cvv?: string\n  general?: string\n}\n\nconst formatCardNumber = (value: string) => {\n  const v = value.replace(/\\s+/g, '').replace(/[^0-9]/gi, '')\n  const matches = v.match(/\\d{4,16}/g)\n  const match = (matches && matches[0]) || ''\n  const parts: string[] = []\n\n  for (let i = 0, len = match.length; i < len; i += 4) {\n    parts.push(match.substring(i, i + 4))\n  }\n\n  if (parts.length) {\n    return parts.join(' ')\n  } else {\n    return v\n  }\n}\n\nconst getCardType = (number: string): keyof typeof CardIcons => {\n  const cleanNumber = number.replace(/\\s/g, '')\n\n  // Visa: starts with 4\n  if (cleanNumber.startsWith('4')) return 'visa'\n\n  // Mastercard: starts with 5 or 2221-2720\n  if (\n    cleanNumber.startsWith('5') ||\n    (cleanNumber.startsWith('2') &&\n      parseInt(cleanNumber.substring(0, 4)) >= 2221 &&\n      parseInt(cleanNumber.substring(0, 4)) <= 2720)\n  ) {\n    return 'mastercard'\n  }\n\n  // American Express: starts with 34 or 37\n  if (cleanNumber.startsWith('34') || cleanNumber.startsWith('37'))\n    return 'amex'\n\n  // Discover: starts with 6011, 622126-622925, 644-649, 65\n  if (\n    cleanNumber.startsWith('6011') ||\n    cleanNumber.startsWith('65') ||\n    cleanNumber.startsWith('644') ||\n    cleanNumber.startsWith('645') ||\n    cleanNumber.startsWith('646') ||\n    cleanNumber.startsWith('647') ||\n    cleanNumber.startsWith('648') ||\n    cleanNumber.startsWith('649')\n  ) {\n    return 'discover'\n  }\n\n  return 'generic'\n}\n\nconst validateCreditCard = (\n  value: CreditCardValue,\n  cvvLabel: string,\n): ValidationErrors => {\n  const errors: ValidationErrors = {}\n\n  // Validate cardholder name\n  if (!value.cardholderName?.trim()) {\n    errors.cardholderName = 'Cardholder name is required'\n  } else if (value.cardholderName.trim().length < 2) {\n    errors.cardholderName = 'Name must be at least 2 characters'\n  }\n\n  // Validate card number\n  const cleanCardNumber = value.cardNumber?.replace(/\\s/g, '') || ''\n  if (!cleanCardNumber) {\n    errors.cardNumber = 'Card number is required'\n  } else if (cleanCardNumber.length < 13 || cleanCardNumber.length > 19) {\n    errors.cardNumber = 'Invalid card number length'\n  } else if (!/^\\d+$/.test(cleanCardNumber)) {\n    errors.cardNumber = 'Card number must contain only digits'\n  }\n\n  // Validate expiry month\n  if (!value.expiryMonth?.trim()) {\n    errors.expiryMonth = 'Expiry month is required'\n  }\n\n  // Validate expiry year\n  if (!value.expiryYear?.trim()) {\n    errors.expiryYear = 'Expiry year is required'\n  } else if (value.expiryMonth && value.expiryYear) {\n    const currentDate = new Date()\n    const currentYear = currentDate.getFullYear()\n    const currentMonth = currentDate.getMonth() + 1\n    const expiryYear = parseInt(value.expiryYear)\n    const expiryMonth = parseInt(value.expiryMonth)\n\n    if (\n      expiryYear < currentYear ||\n      (expiryYear === currentYear && expiryMonth < currentMonth)\n    ) {\n      errors.expiryYear = 'Card has expired'\n    }\n  }\n\n  // Validate CVV\n  const cardType = getCardType(value.cardNumber || '')\n  const expectedCvvLength = cardType === 'amex' ? 4 : 3\n  if (!value.cvv?.trim()) {\n    errors.cvv = `${cvvLabel} is required`\n  } else if (value.cvv.length !== expectedCvvLength) {\n    errors.cvv = `${cvvLabel} must be ${expectedCvvLength} digits`\n  } else if (!/^\\d+$/.test(value.cvv)) {\n    errors.cvv = `${cvvLabel} must contain only digits`\n  }\n\n  return errors\n}\n\nfunction CreditCard({\n  value,\n  onChange,\n  onValidationChange,\n  className,\n  ref,\n  cvvLabel = 'CVC',\n  cardStyle = 'base',\n  showVendor = true,\n}: CreditCardProps) {\n  const [isFlipped, setIsFlipped] = useState(false)\n  const [focusedField, setFocusedField] = useState<string | null>(null)\n  const [errors, setErrors] = useState<ValidationErrors>({})\n\n  // 3D hover effect using framer-motion\n  const x = useMotionValue(0)\n  const y = useMotionValue(0)\n  const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [15, -15]))\n  const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-15, 15]))\n\n  // Internal refs for DOM elements\n  const containerRef = useRef<HTMLDivElement>(null)\n  const cardholderInputRef = useRef<HTMLInputElement>(null)\n  const cardNumberInputRef = useRef<HTMLInputElement>(null)\n  const cvvInputRef = useRef<HTMLInputElement>(null)\n\n  const currentValue = value || {\n    cardholderName: '',\n    cardNumber: '',\n    expiryMonth: '',\n    expiryYear: '',\n    cvv: '',\n  }\n\n  const validateAndUpdate = (newValue: CreditCardValue) => {\n    const validationErrors = validateCreditCard(newValue, cvvLabel)\n    setErrors(validationErrors)\n\n    const isValid = Object.keys(validationErrors).length === 0\n    onValidationChange?.(isValid, validationErrors)\n\n    return isValid\n  }\n\n  const handleInputChange = (\n    field: keyof CreditCardValue,\n    newValue: string,\n  ) => {\n    const updatedValue = { ...currentValue, [field]: newValue }\n    onChange?.(updatedValue)\n    validateAndUpdate(updatedValue)\n  }\n\n  const handleCardNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const formatted = formatCardNumber(e.target.value)\n    if (formatted.replace(/\\s/g, '').length <= 19) {\n      handleInputChange('cardNumber', formatted)\n    }\n  }\n\n  const handleCvvFocus = () => {\n    setIsFlipped(true)\n    setFocusedField('cvv')\n  }\n\n  const handleCvvBlur = () => {\n    setIsFlipped(false)\n    setFocusedField(null)\n  }\n\n  const handleFieldFocus = (fieldName: string) => {\n    setFocusedField(fieldName)\n  }\n\n  const handleFieldBlur = () => {\n    setFocusedField(null)\n  }\n\n  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n    if (!containerRef.current) return\n    const rect = containerRef.current.getBoundingClientRect()\n    const centerX = rect.left + rect.width / 2\n    const centerY = rect.top + rect.height / 2\n    x.set((e.clientX - centerX) / rect.width)\n    y.set((e.clientY - centerY) / rect.height)\n  }\n\n  const handleMouseLeave = () => {\n    x.set(0)\n    y.set(0)\n  }\n\n  const handleValidate = () => {\n    const isValid = validateAndUpdate(currentValue)\n\n    if (!isValid) {\n      if (errors.cardholderName) {\n        cardholderInputRef.current?.focus()\n      } else if (errors.cardNumber) {\n        cardNumberInputRef.current?.focus()\n      } else if (errors.cvv) {\n        cvvInputRef.current?.focus()\n      }\n    }\n\n    return isValid\n  }\n\n  const handleReset = () => {\n    setErrors({})\n    setFocusedField(null)\n    setIsFlipped(false)\n    onChange?.({\n      cardholderName: '',\n      cardNumber: '',\n      expiryMonth: '',\n      expiryYear: '',\n      cvv: '',\n    })\n  }\n\n  const handleFocus = () => {\n    cardholderInputRef.current?.focus()\n  }\n\n  const getErrors = () => errors\n\n  // React 19: Expose imperative methods via ref callback\n  useEffect(() => {\n    if (ref && 'current' in ref) {\n      ref.current = {\n        validate: handleValidate,\n        isValid: () =>\n          Object.keys(validateCreditCard(currentValue, cvvLabel)).length === 0,\n        focus: handleFocus,\n        reset: handleReset,\n        getErrors,\n      }\n    }\n  }, [ref, currentValue, errors, handleValidate, handleReset, getErrors])\n\n  const cardType = getCardType(currentValue.cardNumber)\n  const currentYear = new Date().getFullYear()\n  const years = Array.from({ length: 20 }, (_, i) => currentYear + i)\n  const months = Array.from({ length: 12 }, (_, i) => {\n    const month = i + 1\n    return {\n      value: month.toString().padStart(2, '0'),\n      label: month.toString().padStart(2, '0'),\n    }\n  })\n\n  // Get chip color based on card style\n  const getChipColor = () => {\n    switch (cardStyle) {\n      case 'base':\n        return 'bg-yellow-500'\n      case 'shiny-silver':\n        return 'bg-yellow-600'\n      case 'amex-green':\n      case 'amex-black':\n        return 'bg-yellow-400'\n      case 'metal':\n        return 'bg-yellow-300'\n      default:\n        return 'bg-yellow-400'\n    }\n  }\n\n  return (\n    <div ref={containerRef} className={cn('w-full max-w-sm py-2', className)}>\n      {/* Card Container with 3D effects using Tailwind CSS utilities */}\n      <div className=\"relative h-56 mb-6 [perspective:1000px]\">\n        <motion.div\n          className=\"relative w-full h-full [transform-style:preserve-3d]\"\n          animate={{ rotateY: isFlipped ? 180 : 0 }}\n          style={{\n            rotateX: rotateX,\n            rotateY: isFlipped ? 180 : rotateY,\n          }}\n          transition={{ duration: 0.6, ease: 'easeInOut' }}\n          onMouseMove={handleMouseMove}\n          onMouseLeave={handleMouseLeave}\n        >\n          {/* Front of Card */}\n          <Card\n            className={cn(\n              'absolute inset-0 w-full h-full p-6 flex flex-col justify-between [backface-visibility:hidden] shadow-xl',\n              cardStyles[cardStyle],\n            )}\n          >\n            <div className=\"flex justify-between items-start\">\n              <div\n                className={cn('w-12 h-8 rounded shadow-md', getChipColor())}\n              ></div>\n              {/* Vendor logo moved to top right for now, will be repositioned */}\n            </div>\n\n            <div className=\"space-y-4\">\n              <div className=\"text-xl font-mono tracking-wider font-bold\">\n                {currentValue.cardNumber || '•••• •••• •••• ••••'}\n              </div>\n\n              {/* Bottom row: cardholder - expires - vendor logo */}\n              <div className=\"flex justify-between items-end\">\n                <div className=\"flex-1\">\n                  <div className=\"text-xs opacity-70 uppercase font-medium\">\n                    Card Holder\n                  </div>\n                  <div className=\"font-bold text-sm\">\n                    {currentValue.cardholderName || 'YOUR NAME'}\n                  </div>\n                </div>\n                <div className=\"flex-1 text-center\">\n                  <div className=\"text-xs opacity-70 uppercase font-medium\">\n                    Expires\n                  </div>\n                  <div className=\"font-bold text-sm\">\n                    {currentValue.expiryMonth && currentValue.expiryYear\n                      ? `${currentValue.expiryMonth}/${currentValue.expiryYear.slice(-2)}`\n                      : 'MM/YY'}\n                  </div>\n                </div>\n                <div className=\"flex-1 flex justify-end\">\n                  {showVendor && CardIcons[cardType]}\n                </div>\n              </div>\n            </div>\n          </Card>\n\n          {/* Back of Card */}\n          <Card\n            className={cn(\n              'absolute inset-0 w-full h-full p-6 flex flex-col justify-between [backface-visibility:hidden] [transform:rotateY(180deg)] shadow-xl',\n              cardBackStyles[cardStyle],\n            )}\n          >\n            <div className=\"w-full h-12 bg-black mt-4 shadow-inner\"></div>\n\n            <div className=\"flex justify-end items-center space-x-4\">\n              <div className=\"text-right\">\n                <div className=\"text-xs opacity-70 uppercase font-medium\">\n                  {cvvLabel}\n                </div>\n                <div className=\"bg-white text-black px-3 py-1 rounded text-center font-mono font-bold\">\n                  {currentValue.cvv || '•••'}\n                </div>\n              </div>\n              <Lock className=\"w-6 h-6 opacity-60\" />\n            </div>\n\n            <div className=\"text-xs opacity-60 text-center font-medium\">\n              This card is protected by advanced security features\n            </div>\n          </Card>\n        </motion.div>\n      </div>\n\n      <div className=\"space-y-4\">\n        <div>\n          <label className=\"block text-sm font-medium mb-2\">\n            Cardholder Name\n          </label>\n          <Input\n            ref={cardholderInputRef}\n            type=\"text\"\n            placeholder=\"John Doe\"\n            value={currentValue.cardholderName}\n            onChange={(e) =>\n              handleInputChange('cardholderName', e.target.value.toUpperCase())\n            }\n            onFocus={() => handleFieldFocus('cardholderName')}\n            onBlur={handleFieldBlur}\n            className={cn(\n              'transition-all duration-200',\n              focusedField === 'cardholderName' && 'ring-2 ring-primary',\n              errors.cardholderName && 'border-destructive',\n            )}\n          />\n          {errors.cardholderName && (\n            <p className=\"text-destructive text-xs mt-1\">\n              {errors.cardholderName}\n            </p>\n          )}\n        </div>\n\n        <div>\n          <label className=\"block text-sm font-medium mb-2\">Card Number</label>\n          <Input\n            ref={cardNumberInputRef}\n            type=\"text\"\n            placeholder=\"1234 5678 9012 3456\"\n            value={currentValue.cardNumber}\n            onChange={handleCardNumberChange}\n            onFocus={() => handleFieldFocus('cardNumber')}\n            onBlur={handleFieldBlur}\n            className={cn(\n              'font-mono transition-all duration-200',\n              focusedField === 'cardNumber' && 'ring-2 ring-primary',\n              errors.cardNumber && 'border-destructive',\n            )}\n            maxLength={19}\n          />\n          {errors.cardNumber && (\n            <p className=\"text-destructive text-xs mt-1\">{errors.cardNumber}</p>\n          )}\n        </div>\n\n        <div className=\"grid grid-cols-3 gap-4\">\n          <div>\n            <label className=\"block text-sm font-medium mb-2\">Month</label>\n            <Select\n              value={currentValue.expiryMonth}\n              onValueChange={(value) => handleInputChange('expiryMonth', value ?? '')}\n            >\n              <SelectTrigger\n                className={cn(\n                  'transition-all duration-200',\n                  focusedField === 'expiryMonth' && 'ring-2 ring-primary',\n                  errors.expiryMonth && 'border-destructive',\n                )}\n              >\n                <SelectValue placeholder=\"MM\" />\n              </SelectTrigger>\n              <SelectContent>\n                {months.map((month) => (\n                  <SelectItem key={month.value} value={month.value}>\n                    {month.label}\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\n            {errors.expiryMonth && (\n              <p className=\"text-destructive text-xs mt-1\">\n                {errors.expiryMonth}\n              </p>\n            )}\n          </div>\n\n          <div>\n            <label className=\"block text-sm font-medium mb-2\">Year</label>\n            <Select\n              value={currentValue.expiryYear}\n              onValueChange={(value) => handleInputChange('expiryYear', value ?? '')}\n            >\n              <SelectTrigger\n                className={cn(\n                  'transition-all duration-200',\n                  focusedField === 'expiryYear' && 'ring-2 ring-primary',\n                  errors.expiryYear && 'border-destructive',\n                )}\n              >\n                <SelectValue placeholder=\"YYYY\" />\n              </SelectTrigger>\n              <SelectContent>\n                {years.map((year) => (\n                  <SelectItem key={year} value={year.toString()}>\n                    {year}\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\n            {errors.expiryYear && (\n              <p className=\"text-destructive text-xs mt-1\">\n                {errors.expiryYear}\n              </p>\n            )}\n          </div>\n\n          <div>\n            <label className=\"block text-sm font-medium mb-2\">{cvvLabel}</label>\n            <Input\n              ref={cvvInputRef}\n              type=\"text\"\n              placeholder=\"123\"\n              value={currentValue.cvv}\n              onChange={(e) => {\n                const value = e.target.value.replace(/\\D/g, '')\n                if (value.length <= (cardType === 'amex' ? 4 : 3)) {\n                  handleInputChange('cvv', value)\n                }\n              }}\n              onFocus={handleCvvFocus}\n              onBlur={handleCvvBlur}\n              className={cn(\n                'font-mono text-center transition-all duration-200',\n                focusedField === 'cvv' && 'ring-2 ring-primary',\n                errors.cvv && 'border-destructive',\n              )}\n              maxLength={cardType === 'amex' ? 4 : 3}\n            />\n            {errors.cvv && (\n              <p className=\"text-destructive text-xs mt-1\">{errors.cvv}</p>\n            )}\n          </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n\nCreditCard.displayName = 'CreditCard'\n\nexport { CreditCard }\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "input",
    "payment"
  ],
  "type": "registry:ui"
}