{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "card-swipe",
  "type": "registry:component",
  "title": "Card Swipe",
  "description": "A tactile, gesture-driven card stack that supports velocity-based swiping and smooth pagination.",
  "dependencies": [
    "@hugeicons/core-free-icons",
    "@hugeicons/react",
    "motion",
    "next-themes"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "components/watermelon/card-swipe.tsx",
      "type": "registry:component",
      "content": "'use client';\n\nimport React, { useState, useEffect, type ReactNode } from 'react';\nimport {\n  motion,\n  useMotionValue,\n  useTransform,\n  type PanInfo,\n  type Transition,\n} from 'motion/react';\nimport { HugeiconsIcon } from '@hugeicons/react';\nimport { useTheme } from 'next-themes';\nimport {\n  Book02Icon,\n  Brain02Icon,\n  DropletFreeIcons,\n  RunningShoesIcon,\n  SwimmingIcon,\n} from '@hugeicons/core-free-icons';\nimport { cn } from '@/lib/utils';\n\nexport interface CardItem {\n  id: number;\n  title: string;\n  description: string;\n  icon: (theme: 'light' | 'dark') => ReactNode;\n}\n\ninterface CardCarouselProps {\n  items?: CardItem[];\n}\n\nconst DEFAULT_CARDS: CardItem[] = [\n  {\n    id: 1,\n    title: 'Reading',\n    description: 'Sharpen your mind & escape to new adventures.',\n    icon: (theme) => (\n      <HugeiconsIcon\n        icon={Book02Icon}\n        size={52}\n        color={theme === 'light' ? '#000000' : '#ffffff'}\n        strokeWidth={1.5}\n      />\n    ),\n  },\n  {\n    id: 2,\n    title: 'Drink Water',\n    description: 'Stay hydrated & energized. Your body will thank you!',\n    icon: (theme) => (\n      <HugeiconsIcon\n        icon={DropletFreeIcons}\n        size={52}\n        color={theme === 'light' ? '#000000' : '#ffffff'}\n        strokeWidth={1.5}\n      />\n    ),\n  },\n  {\n    id: 3,\n    title: 'Running',\n    description: 'Feel the endorphins! Get a quick energy boost.',\n    icon: (theme) => (\n      <HugeiconsIcon\n        icon={RunningShoesIcon}\n        size={52}\n        color={theme === 'light' ? '#000000' : '#ffffff'}\n        strokeWidth={1.5}\n      />\n    ),\n  },\n  {\n    id: 4,\n    title: 'Swimming',\n    description: 'Low-impact workout. Refreshing & invigorating.',\n    icon: (theme) => (\n      <HugeiconsIcon\n        icon={SwimmingIcon}\n        size={52}\n        color={theme === 'light' ? '#000000' : '#ffffff'}\n        strokeWidth={1.5}\n      />\n    ),\n  },\n  {\n    id: 5,\n    title: 'Meditation',\n    description: 'Find inner peace. Just 5 minutes can de-stress.',\n    icon: (theme) => (\n      <HugeiconsIcon\n        icon={Brain02Icon}\n        size={52}\n        color={theme === 'light' ? '#000000' : '#ffffff'}\n        strokeWidth={1.5}\n      />\n    ),\n  },\n];\n\nconst ITEM_WIDTH = 320;\nconst GAP = 16;\nconst CONTAINER_WIDTH = ITEM_WIDTH + GAP;\nconst DRAG_BUFFER = 50;\nconst VELOCITY_THRESHOLD = 500;\n\nconst SPRING_OPTIONS: Transition = {\n  type: 'spring',\n  stiffness: 330,\n  damping: 30,\n};\n\n\ninterface CarouselCardProps {\n  item: CardItem;\n  index: number;\n  x: ReturnType<typeof useMotionValue<number>>;\n  itemCount: number;\n  currentTheme: 'light' | 'dark';\n}\n\nconst CarouselCard: React.FC<CarouselCardProps> = ({\n  item,\n  index,\n  x,\n  itemCount,\n  currentTheme,\n}) => {\n  const nextIndex = Math.min(index + 1, itemCount - 1);\n  const prevIndex = Math.max(index - 1, 0);\n\n  const range = [\n    (-100 * (index + 1) * CONTAINER_WIDTH) / 100,\n    (-100 * index * CONTAINER_WIDTH) / 100,\n    (-100 * (index - 1) * CONTAINER_WIDTH) / 100,\n  ];\n  const outputRange = [nextIndex ? 90 : 90, 0, prevIndex ? -90 : -90];\n\n  const rotateY = useTransform(x, range, outputRange, { clamp: false });\n\n  return (\n    <motion.div\n      style={{\n        width: ITEM_WIDTH,\n        height: 420,\n        rotateY,\n        flexShrink: 0,\n      }}\n      transition={SPRING_OPTIONS}\n      className=\"flex cursor-grab flex-col items-start rounded-[40px] border-[1.6px] border-[#ECECEC] bg-[#FEFEFE] p-8 transition-colors active:cursor-grabbing sm:p-10 dark:border-zinc-800 dark:bg-zinc-900\"\n    >\n      <div className=\"mb-6 flex h-20 w-20 items-center justify-center rounded-[20px] border-[1.6px] border-[#ECECEC] bg-[#FEFEFE] shadow-[0_6px_20px_rgba(0,0,0,0.08)] transition-colors sm:mb-10 sm:h-24 sm:w-24 sm:rounded-[24px] dark:border-zinc-800 dark:bg-zinc-900\">\n        {item.icon(currentTheme)}\n      </div>\n\n      <h2 className=\"mb-2 text-2xl font-bold text-[#010101] sm:text-[32px] dark:text-zinc-100\">\n        {item.title}\n      </h2>\n\n      <p className=\"mb-5 text-lg text-[#77767B] sm:text-[22px] dark:text-zinc-400\">\n        {item.description}\n      </p>\n\n      <motion.button\n        whileHover={{ scale: 1.02 }}\n        whileTap={{ scale: 0.98 }}\n        className=\"rounded-full bg-[#262626] px-6 py-2.5 text-sm text-[#F2F2F2] shadow-sm sm:px-7 sm:py-3 sm:text-base dark:bg-zinc-100 dark:text-zinc-900\"\n      >\n        Get Started\n      </motion.button>\n    </motion.div>\n  );\n};\n\nexport const CardSwipe: React.FC<CardCarouselProps> = ({\n  items = DEFAULT_CARDS,\n}) => {\n  const [currentIndex, setCurrentIndex] = useState(0);\n  const [mounted, setMounted] = useState(false);\n  const { resolvedTheme } = useTheme();\n\n  const x = useMotionValue(0);\n\n  useEffect(() => {\n    requestAnimationFrame(() => setMounted(true));\n  }, []);\n\n  if (!mounted) return null;\n\n  const currentTheme = resolvedTheme === 'dark' ? 'dark' : 'light';\n\n  const handleDragEnd = (_: unknown, info: PanInfo) => {\n    const offset = info.offset.x;\n    const velocity = info.velocity.x;\n\n    if (offset < -DRAG_BUFFER || velocity < -VELOCITY_THRESHOLD) {\n      setCurrentIndex((prev) => Math.min(prev + 1, items.length - 1));\n    } else if (offset > DRAG_BUFFER || velocity > VELOCITY_THRESHOLD) {\n      setCurrentIndex((prev) => Math.max(prev - 1, 0));\n    }\n  };\n\n  const leftConstraint = -((ITEM_WIDTH + GAP) * (items.length - 1));\n\n  return (\n    <div className=\"flex flex-col items-center justify-center\">\n      <div\n        className=\"relative overflow-hidden\"\n        style={{ width: ITEM_WIDTH, height: 420 }}\n      >\n        <motion.div\n          className=\"flex\"\n          drag=\"x\"\n          dragConstraints={{ left: leftConstraint, right: 0 }}\n          style={{\n            gap: GAP,\n            perspective: 1000,\n            perspectiveOrigin: currentIndex * ITEM_WIDTH + ITEM_WIDTH / 2,\n            x,\n          }}\n          onDragEnd={handleDragEnd}\n          animate={{ x: -(currentIndex * CONTAINER_WIDTH) }}\n          transition={SPRING_OPTIONS}\n        >\n          {items.map((item, index) => (\n            <CarouselCard\n              key={item.id}\n              item={item}\n              index={index}\n              x={x}\n              itemCount={items.length}\n              currentTheme={currentTheme}\n            />\n          ))}\n        </motion.div>\n      </div>\n\n      <div className=\"mt-4 flex gap-3 sm:mt-6\">\n        {items.map((_, i) => (\n          <div\n            key={i}\n            className={cn(\n              'h-2 w-2 cursor-pointer rounded-full bg-zinc-200 transition-colors duration-200',\n              currentIndex === i && 'bg-zinc-400',\n            )}\n            onClick={() => setCurrentIndex(i)}\n          />\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default CardSwipe;\n"
    }
  ]
}
