{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "card",
  "title": "Card",
  "description": "Card parts cascade in, interactive cards lift on hover, and an optional light follows the pointer.",
  "dependencies": [],
  "registryDependencies": [
    "@keyframery/motion"
  ],
  "files": [
    {
      "path": "registry/components/keyframery/primitives/card.tsx",
      "content": "\"use client\"\n\n/* Animated card. UI SDK has no card, so these are plain elements with the shadcn part names\n   (Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter): swap the import\n   and keep your markup. This file adds motion only. Colours, radii, shadows and spacing stay yours. */\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Cascade: the card fades up out of a light blur, then its parts follow one after another,\n   and inside the header the title leads the description. Keyframes with a backwards fill, so a\n   part is hidden on its first frame and the delay never slows anything later (hover, press). */\nconst keyframes =\n  \"@keyframes kf-card-in{from{opacity:0;scale:.97;filter:blur(6px)}}\" +\n  \"@keyframes kf-card-part{from{opacity:0;translate:0 12px;filter:blur(5px)}}\"\n\nfunction Card({\n  className,\n  interactive,\n  spotlight,\n  onPointerEnter,\n  onPointerLeave,\n  onPointerMove,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  /** Lifts 2px on hover on a spring, presses down with a pop. */\n  interactive?: boolean\n  /** A soft light that follows the pointer. */\n  spotlight?: boolean\n}) {\n  /* Hover is mirrored in `data-hovered`, so pointer events from a script or a touch get the\n     same lift and light as a real :hover. Pass `data-hovered` / `data-pressed` yourself to drive it. */\n  const [hovered, setHovered] = React.useState(false)\n  const tracks = interactive || spotlight\n\n  return (\n    <>\n      <style href=\"kf-card\" precedence=\"default\">\n        {keyframes}\n      </style>\n      <div\n        data-slot=\"card\"\n        data-interactive={interactive || undefined}\n        data-spotlight={spotlight || undefined}\n        data-hovered={(tracks && hovered) || undefined}\n        onPointerEnter={(e) => {\n          if (tracks) setHovered(true)\n          onPointerEnter?.(e)\n        }}\n        onPointerLeave={(e) => {\n          if (tracks) setHovered(false)\n          onPointerLeave?.(e)\n        }}\n        onPointerMove={(e) => {\n          /* Spotlight: the pointer position goes into two CSS variables; no animation loop. */\n          if (spotlight) {\n            const r = e.currentTarget.getBoundingClientRect()\n            e.currentTarget.style.setProperty(\"--kf-x\", `${e.clientX - r.left}px`)\n            e.currentTarget.style.setProperty(\"--kf-y\", `${e.clientY - r.top}px`)\n          }\n          onPointerMove?.(e)\n        }}\n        className={cn(\n          \"group/card relative isolate\",\n          \"animate-[kf-card-in_calc(520ms*var(--kf-speed,1))_var(--ease-spring)_backwards] motion-reduce:animate-none!\",\n          \"[&>*]:animate-[kf-card-part_calc(560ms*var(--kf-speed,1))_var(--ease-spring)_backwards] motion-reduce:[&>*]:animate-none\",\n          \"[&>*]:[animation-delay:calc(calc(70ms*var(--kf-speed,1))+(min(sibling-index(),10)-1)*calc(70ms*var(--kf-speed,1)))]\",\n          /* Lift and press. Translate and shadow settle on a spring; the press scale pops back. */\n          \"[transition:translate_calc(420ms*var(--kf-speed,1))_var(--ease-spring),box-shadow_calc(320ms*var(--kf-speed,1))_var(--ease-house),scale_calc(280ms*var(--kf-speed,1))_var(--ease-pop)]\",\n          \"[&[data-interactive]:is(:hover,[data-hovered])]:-translate-y-0.5\",\n          \"[&[data-interactive]:is(:hover,[data-hovered]):is(:active,[data-pressed])]:translate-y-0 [&[data-interactive]:is(:active,[data-pressed])]:scale-[0.985]\",\n          \"motion-reduce:[transition:none]!\",\n          /* The light: a radial wash under the content, centred on the pointer. It follows the text\n             colour by default; set --kf-spotlight to change it. */\n          \"data-spotlight:before:pointer-events-none data-spotlight:before:absolute data-spotlight:before:inset-0 data-spotlight:before:-z-10 data-spotlight:before:rounded-[inherit]\",\n          \"data-spotlight:before:bg-[radial-gradient(300px_circle_at_var(--kf-x,50%)_var(--kf-y,0%),var(--kf-spotlight,color-mix(in_oklch,currentColor_13%,transparent)),transparent_70%)]\",\n          \"data-spotlight:before:opacity-0 data-spotlight:before:transition-opacity data-spotlight:before:duration-[calc(320ms*var(--kf-speed,1))] data-spotlight:before:ease-house\",\n          \"data-spotlight:[&:is(:hover,[data-hovered])]:before:opacity-100 motion-reduce:before:transition-none!\",\n          className,\n        )}\n        {...props}\n      />\n    </>\n  )\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-header\"\n      className={cn(\n        \"[&>*]:animate-[kf-card-part_calc(560ms*var(--kf-speed,1))_var(--ease-spring)_backwards] motion-reduce:[&>*]:animate-none\",\n        \"[&>*]:[animation-delay:calc(calc(110ms*var(--kf-speed,1))+(min(sibling-index(),10)-1)*calc(60ms*var(--kf-speed,1)))]\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CardTitle(props: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"card-title\" {...props} />\n}\n\nfunction CardDescription(props: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"card-description\" {...props} />\n}\n\nfunction CardAction(props: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"card-action\" {...props} />\n}\n\nfunction CardContent(props: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"card-content\" {...props} />\n}\n\nfunction CardFooter(props: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"card-footer\" {...props} />\n}\n\nexport { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }\n",
      "type": "registry:component",
      "target": "components/keyframery/primitives/card.tsx"
    },
    {
      "path": "registry/components/keyframery/card.tsx",
      "content": "\"use client\"\n\n/* Styled card: the animated primitive plus the Keyframery look. Same parts as shadcn/ui's card. */\n\nimport * as React from \"react\"\n\nimport {\n  Card as CardPrimitive,\n  CardAction as CardActionPrimitive,\n  CardContent as CardContentPrimitive,\n  CardDescription as CardDescriptionPrimitive,\n  CardFooter as CardFooterPrimitive,\n  CardHeader as CardHeaderPrimitive,\n  CardTitle as CardTitlePrimitive,\n} from \"@/components/keyframery/primitives/card\"\nimport { cn } from \"@/lib/utils\"\n\n/* A sheet that sits on the page: hairline, a short contact shadow and a soft one below.\n   An interactive card's shadow grows as it lifts and tightens again while pressed. */\nfunction Card({ className, ...props }: React.ComponentProps<typeof CardPrimitive>) {\n  return (\n    <CardPrimitive\n      className={cn(\n        \"flex flex-col gap-5 rounded-[16px] bg-card py-5 text-card-foreground\",\n        \"shadow-[0_0_0_1px_rgb(0_0_0/0.07),0_1px_2px_rgb(0_0_0/0.04),0_4px_12px_-6px_rgb(0_0_0/0.08)]\",\n        \"dark:shadow-[0_0_0_1px_rgb(255_255_255/0.08),0_1px_2px_rgb(0_0_0/0.4),0_6px_16px_-6px_rgb(0_0_0/0.6)]\",\n        \"data-interactive:cursor-pointer outline-none\",\n        \"[&[data-interactive]:is(:hover,[data-hovered])]:shadow-[0_0_0_1px_rgb(0_0_0/0.09),0_2px_4px_rgb(0_0_0/0.05),0_16px_32px_-12px_rgb(0_0_0/0.24)]\",\n        \"dark:[&[data-interactive]:is(:hover,[data-hovered])]:shadow-[0_0_0_1px_rgb(255_255_255/0.12),0_2px_4px_rgb(0_0_0/0.4),0_18px_36px_-12px_rgb(0_0_0/0.8)]\",\n        \"[&[data-interactive]:is(:hover,[data-hovered]):is(:active,[data-pressed])]:shadow-[0_0_0_1px_rgb(0_0_0/0.1),0_1px_2px_rgb(0_0_0/0.06),0_4px_10px_-6px_rgb(0_0_0/0.14)]\",\n        \"dark:[&[data-interactive]:is(:hover,[data-hovered]):is(:active,[data-pressed])]:shadow-[0_0_0_1px_rgb(255_255_255/0.12),0_1px_2px_rgb(0_0_0/0.5),0_4px_10px_-6px_rgb(0_0_0/0.7)]\",\n        \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<typeof CardHeaderPrimitive>) {\n  return (\n    <CardHeaderPrimitive\n      className={cn(\n        \"grid auto-rows-min grid-rows-[auto_auto] items-start gap-1 px-5\",\n        \"has-data-[slot=card-action]:grid-cols-[1fr_auto]\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<typeof CardTitlePrimitive>) {\n  return (\n    <CardTitlePrimitive\n      className={cn(\"text-[15px] leading-5 font-semibold tracking-tight\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<typeof CardDescriptionPrimitive>) {\n  return (\n    <CardDescriptionPrimitive\n      className={cn(\"text-[13px] leading-5 text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<typeof CardActionPrimitive>) {\n  return (\n    <CardActionPrimitive\n      className={cn(\"col-start-2 row-span-2 row-start-1 self-start justify-self-end\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<typeof CardContentPrimitive>) {\n  return <CardContentPrimitive className={cn(\"px-5 text-[13px]\", className)} {...props} />\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<typeof CardFooterPrimitive>) {\n  return <CardFooterPrimitive className={cn(\"flex items-center gap-2 px-5\", className)} {...props} />\n}\n\nexport { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }\n",
      "type": "registry:component",
      "target": "components/keyframery/card.tsx"
    }
  ],
  "type": "registry:component"
}