{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "magic-card",
  "type": "registry:ui",
  "title": "Magic Card",
  "description": "A spotlight effect that follows your mouse cursor and highlights borders on hover.",
  "dependencies": [
    "motion",
    "next-themes"
  ],
  "files": [
    {
      "path": "registry/magicui/magic-card.tsx",
      "content": "\"use client\"\n\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport {\n  motion,\n  useMotionTemplate,\n  useMotionValue,\n  useSpring,\n} from \"motion/react\"\nimport { useTheme } from \"next-themes\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface MagicCardBaseProps {\n  children?: React.ReactNode\n  className?: string\n  gradientSize?: number\n  gradientFrom?: string\n  gradientTo?: string\n}\n\ninterface MagicCardGradientProps extends MagicCardBaseProps {\n  mode?: \"gradient\"\n\n  gradientColor?: string\n  gradientOpacity?: number\n\n  glowFrom?: never\n  glowTo?: never\n  glowAngle?: never\n  glowSize?: never\n  glowBlur?: never\n  glowOpacity?: never\n}\n\ninterface MagicCardOrbProps extends MagicCardBaseProps {\n  mode: \"orb\"\n\n  glowFrom?: string\n  glowTo?: string\n  glowAngle?: number\n  glowSize?: number\n  glowBlur?: number\n  glowOpacity?: number\n\n  gradientColor?: never\n  gradientOpacity?: never\n}\n\ntype MagicCardProps = MagicCardGradientProps | MagicCardOrbProps\ntype ResetReason = \"enter\" | \"leave\" | \"global\" | \"init\"\n\nfunction isOrbMode(props: MagicCardProps): props is MagicCardOrbProps {\n  return props.mode === \"orb\"\n}\n\nexport function MagicCard(props: MagicCardProps) {\n  const {\n    children,\n    className,\n    gradientSize = 200,\n    gradientColor = \"#262626\",\n    gradientOpacity = 0.8,\n    gradientFrom = \"#9E7AFF\",\n    gradientTo = \"#FE8BBB\",\n    mode = \"gradient\",\n  } = props\n\n  const glowFrom = isOrbMode(props) ? (props.glowFrom ?? \"#ee4f27\") : \"#ee4f27\"\n  const glowTo = isOrbMode(props) ? (props.glowTo ?? \"#6b21ef\") : \"#6b21ef\"\n  const glowAngle = isOrbMode(props) ? (props.glowAngle ?? 90) : 90\n  const glowSize = isOrbMode(props) ? (props.glowSize ?? 420) : 420\n  const glowBlur = isOrbMode(props) ? (props.glowBlur ?? 60) : 60\n  const glowOpacity = isOrbMode(props) ? (props.glowOpacity ?? 0.9) : 0.9\n  const { theme, systemTheme } = useTheme()\n  const [mounted, setMounted] = useState(false)\n\n  useEffect(() => setMounted(true), [])\n\n  const isDarkTheme = useMemo(() => {\n    if (!mounted) return true\n    const currentTheme = theme === \"system\" ? systemTheme : theme\n    return currentTheme === \"dark\"\n  }, [theme, systemTheme, mounted])\n\n  const mouseX = useMotionValue(-gradientSize)\n  const mouseY = useMotionValue(-gradientSize)\n\n  const orbX = useSpring(mouseX, { stiffness: 250, damping: 30, mass: 0.6 })\n  const orbY = useSpring(mouseY, { stiffness: 250, damping: 30, mass: 0.6 })\n  const orbVisible = useSpring(0, { stiffness: 300, damping: 35 })\n\n  const modeRef = useRef(mode)\n  const glowOpacityRef = useRef(glowOpacity)\n  const gradientSizeRef = useRef(gradientSize)\n\n  useEffect(() => {\n    modeRef.current = mode\n  }, [mode])\n\n  useEffect(() => {\n    glowOpacityRef.current = glowOpacity\n  }, [glowOpacity])\n\n  useEffect(() => {\n    gradientSizeRef.current = gradientSize\n  }, [gradientSize])\n\n  const reset = useCallback(\n    (reason: ResetReason = \"leave\") => {\n      const currentMode = modeRef.current\n\n      if (currentMode === \"orb\") {\n        if (reason === \"enter\") orbVisible.set(glowOpacityRef.current)\n        else orbVisible.set(0)\n        return\n      }\n\n      const off = -gradientSizeRef.current\n      mouseX.set(off)\n      mouseY.set(off)\n    },\n    [mouseX, mouseY, orbVisible]\n  )\n\n  const handlePointerMove = useCallback(\n    (e: React.PointerEvent<HTMLDivElement>) => {\n      const rect = e.currentTarget.getBoundingClientRect()\n      mouseX.set(e.clientX - rect.left)\n      mouseY.set(e.clientY - rect.top)\n    },\n    [mouseX, mouseY]\n  )\n\n  useEffect(() => {\n    reset(\"init\")\n  }, [reset])\n\n  useEffect(() => {\n    const handleGlobalPointerOut = (e: PointerEvent) => {\n      if (!e.relatedTarget) reset(\"global\")\n    }\n    const handleBlur = () => reset(\"global\")\n    const handleVisibility = () => {\n      if (document.visibilityState !== \"visible\") reset(\"global\")\n    }\n\n    window.addEventListener(\"pointerout\", handleGlobalPointerOut)\n    window.addEventListener(\"blur\", handleBlur)\n    document.addEventListener(\"visibilitychange\", handleVisibility)\n\n    return () => {\n      window.removeEventListener(\"pointerout\", handleGlobalPointerOut)\n      window.removeEventListener(\"blur\", handleBlur)\n      document.removeEventListener(\"visibilitychange\", handleVisibility)\n    }\n  }, [reset])\n\n  return (\n    <motion.div\n      className={cn(\n        \"group relative isolate overflow-hidden rounded-[inherit] border border-transparent\",\n        className\n      )}\n      onPointerMove={handlePointerMove}\n      onPointerLeave={() => reset(\"leave\")}\n      onPointerEnter={() => reset(\"enter\")}\n      style={{\n        background: useMotionTemplate`\n          linear-gradient(var(--color-background) 0 0) padding-box,\n          radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px,\n            ${gradientFrom},\n            ${gradientTo},\n            var(--color-border) 100%\n          ) border-box\n        `,\n      }}\n    >\n      <div className=\"bg-background absolute inset-px z-20 rounded-[inherit]\" />\n\n      {mode === \"gradient\" && (\n        <motion.div\n          suppressHydrationWarning\n          className=\"pointer-events-none absolute inset-px z-30 rounded-[inherit] opacity-0 transition-opacity duration-300 group-hover:opacity-100\"\n          style={{\n            background: useMotionTemplate`\n              radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px,\n                ${gradientColor},\n                transparent 100%\n              )\n            `,\n            opacity: gradientOpacity,\n          }}\n        />\n      )}\n\n      {mode === \"orb\" && (\n        <motion.div\n          suppressHydrationWarning\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute z-30\"\n          style={{\n            width: glowSize,\n            height: glowSize,\n            x: orbX,\n            y: orbY,\n            translateX: \"-50%\",\n            translateY: \"-50%\",\n            borderRadius: 9999,\n            filter: `blur(${glowBlur}px)`,\n            opacity: orbVisible,\n            background: `linear-gradient(${glowAngle}deg, ${glowFrom}, ${glowTo})`,\n\n            mixBlendMode: isDarkTheme ? \"screen\" : \"multiply\",\n            willChange: \"transform, opacity\",\n          }}\n        />\n      )}\n      <div className=\"relative z-40\">{children}</div>\n    </motion.div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}