{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "card-hover-effect",
  "title": "Card Hover Effect",
  "description": "Link-card grid with a highlight that glides to whichever card you hover or focus.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/velora/card-hover-effect.tsx",
      "content": "\"use client\";\n\nimport { useId, useState } from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface HoverCardItem {\n  /** Card heading */\n  title: string;\n  /** One or two sentences under the title */\n  description: string;\n  /** Link target; the whole card is the link */\n  href: string;\n  /** Optional leading icon, e.g. a lucide-react element */\n  icon?: React.ReactNode;\n}\n\ninterface HoverCardsProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** The link cards, in order */\n  items: HoverCardItem[];\n  /** Column count once the container is wide enough (container queries) */\n  columns?: 1 | 2 | 3 | 4;\n}\n\nconst COLUMNS = {\n  1: \"\",\n  2: \"@lg:grid-cols-2\",\n  3: \"@lg:grid-cols-2 @3xl:grid-cols-3\",\n  4: \"@lg:grid-cols-2 @3xl:grid-cols-4\",\n};\n\n/**\n * Grid of link cards with a soft brand highlight that glides to whichever\n * card is hovered or focused. Columns respond to the container's width, not\n * the viewport's, so it fits sidebars and full-width sections alike.\n */\nexport function HoverCards({\n  items,\n  columns = 3,\n  className,\n  onMouseLeave,\n  onBlur,\n  ...props\n}: HoverCardsProps) {\n  const id = useId();\n  const reducedMotion = useReducedMotion();\n  const [active, setActive] = useState<number | null>(null);\n\n  return (\n    <div\n      data-slot=\"hover-cards\"\n      className={cn(\"@container w-full\", className)}\n      onMouseLeave={(event) => {\n        onMouseLeave?.(event);\n        setActive(null);\n      }}\n      onBlur={(event) => {\n        onBlur?.(event);\n        if (!event.currentTarget.contains(event.relatedTarget)) setActive(null);\n      }}\n      {...props}\n    >\n      <ul className={cn(\"grid\", COLUMNS[columns])}>\n        {items.map((item, index) => (\n          <li key={item.href + item.title} className=\"relative p-2\">\n            <AnimatePresence>\n              {active === index && (\n                <motion.span\n                  aria-hidden\n                  layoutId={`hover-cards-${id}`}\n                  className=\"absolute inset-0 block rounded-[1.5rem] bg-linear-to-br from-brand-from/20 via-brand-via/10 to-brand-to/20 shadow-lg shadow-brand/10 ring-1 ring-brand/25\"\n                  initial={reducedMotion ? false : { opacity: 0 }}\n                  animate={{ opacity: 1 }}\n                  exit={{ opacity: 0, transition: { duration: reducedMotion ? 0 : 0.2 } }}\n                  transition={\n                    reducedMotion\n                      ? { duration: 0 }\n                      : { type: \"spring\", bounce: 0.18, duration: 0.45 }\n                  }\n                />\n              )}\n            </AnimatePresence>\n            <a\n              href={item.href}\n              onMouseEnter={() => setActive(index)}\n              onFocus={() => setActive(index)}\n              className=\"group/hover-card relative z-10 flex h-full flex-col gap-4 rounded-2xl border bg-card/60 p-6 shadow-xs transition-colors hover:border-brand/25 focus-visible:border-brand/25 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n            >\n              <span className=\"flex items-start justify-between gap-4\">\n                {item.icon ? (\n                  <span\n                    aria-hidden\n                    className=\"flex size-10 items-center justify-center rounded-xl border bg-muted text-muted-foreground transition-colors group-hover/hover-card:text-primary group-focus-visible/hover-card:text-primary [&_svg]:size-5\"\n                  >\n                    {item.icon}\n                  </span>\n                ) : (\n                  <span className=\"font-semibold tracking-tight\">{item.title}</span>\n                )}\n                <svg\n                  aria-hidden\n                  viewBox=\"0 0 24 24\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  strokeWidth={2}\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  className=\"size-4 shrink-0 text-muted-foreground/60 transition group-hover/hover-card:text-foreground group-focus-visible/hover-card:text-foreground motion-safe:group-hover/hover-card:translate-x-0.5 motion-safe:group-hover/hover-card:-translate-y-0.5\"\n                >\n                  <path d=\"M7 17 17 7M8 7h9v9\" />\n                </svg>\n              </span>\n              <span className=\"space-y-1.5\">\n                {item.icon && (\n                  <span className=\"block font-semibold tracking-tight\">{item.title}</span>\n                )}\n                <span className=\"block text-sm leading-relaxed text-muted-foreground\">\n                  {item.description}\n                </span>\n              </span>\n            </a>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/velora/card-hover-effect.tsx"
    }
  ],
  "type": "registry:ui"
}