{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tilt-card",
  "title": "Tilt Card",
  "description": "Interactive 3D tilt card with perspective hover effect and spotlight.",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/spell-ui/tilt-card.tsx",
      "content": "\"use client\"\n\nimport { useRef, useState, useCallback } from \"react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface TiltCardProps {\n  /** Maximum tilt angle in degrees */\n  tiltLimit?: number\n  /** Scale factor on hover */\n  scale?: number\n  /** Perspective distance in pixels */\n  perspective?: number\n  /** Tilt direction: \"gravitate\" follows cursor, \"evade\" tilts away */\n  effect?: \"gravitate\" | \"evade\"\n  /** Show a spotlight that follows the cursor on hover */\n  spotlight?: boolean\n  /** Additional class name */\n  className?: string\n  /** Additional inline styles */\n  style?: React.CSSProperties\n  /** Card content */\n  children?: React.ReactNode\n}\n\nexport function TiltCard({\n  tiltLimit = 15,\n  scale = 1.05,\n  perspective = 1200,\n  effect = \"evade\",\n  spotlight = true,\n  className,\n  style,\n  children,\n}: TiltCardProps) {\n  const cardRef = useRef<HTMLDivElement>(null)\n  const [transform, setTransform] = useState(\n    `perspective(${perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`\n  )\n  const [spotlightPos, setSpotlightPos] = useState({ x: 50, y: 50 })\n  const [isHovered, setIsHovered] = useState(false)\n\n  const dir = effect === \"evade\" ? -1 : 1\n\n  const handlePointerMove = useCallback(\n    (e: React.PointerEvent) => {\n      const el = cardRef.current\n      if (!el) return\n      const rect = el.getBoundingClientRect()\n      const px = (e.clientX - rect.left) / rect.width\n      const py = (e.clientY - rect.top) / rect.height\n      const xRot = (py - 0.5) * (tiltLimit * 2) * dir\n      const yRot = (px - 0.5) * -(tiltLimit * 2) * dir\n      setTransform(\n        `perspective(${perspective}px) rotateX(${xRot}deg) rotateY(${yRot}deg) scale3d(${scale}, ${scale}, ${scale})`\n      )\n      if (spotlight) {\n        setSpotlightPos({ x: px * 100, y: py * 100 })\n      }\n    },\n    [tiltLimit, scale, perspective, dir, spotlight]\n  )\n\n  const handlePointerEnter = useCallback(() => {\n    setIsHovered(true)\n  }, [])\n\n  const handlePointerLeave = useCallback(() => {\n    setTransform(\n      `perspective(${perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`\n    )\n    setIsHovered(false)\n  }, [perspective])\n\n  return (\n    <div\n      ref={cardRef}\n      onPointerEnter={handlePointerEnter}\n      onPointerMove={handlePointerMove}\n      onPointerLeave={handlePointerLeave}\n      className={cn(\"will-change-transform relative overflow-hidden\", className)}\n      style={{\n        transform,\n        transition: \"transform 0.2s ease-out\",\n        transformStyle: \"preserve-3d\",\n        ...style,\n      }}\n    >\n      {children}\n      {spotlight && (\n        <div\n          className=\"pointer-events-none absolute inset-0 z-10 overflow-hidden\"\n          style={{ opacity: isHovered ? 1 : 0, transition: \"opacity 0.3s\" }}\n        >\n          <div\n            className=\"absolute w-[200%] h-[200%] rounded-full opacity-100 dark:opacity-50\"\n            style={{\n              left: `${spotlightPos.x}%`,\n              top: `${spotlightPos.y}%`,\n              transform: \"translate(-50%, -50%)\",\n              background:\n                \"radial-gradient(circle, rgba(255,255,255,0.15) 0%, transparent 40%)\",\n            }}\n          />\n        </div>\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}