{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "kinetic-headline",
  "type": "registry:component",
  "title": "KineticHeadline",
  "description": "Splits a headline into per-character spans and animates them in with a 3D stagger (rise + rotateX + blur). No paid plugins.",
  "dependencies": [
    "gsap"
  ],
  "registryDependencies": [],
  "tier": "free",
  "files": [
    {
      "path": "components/prism/KineticHeadline.tsx",
      "content": "\"use client\";\n\nimport { createElement, useEffect, useRef, type ElementType } from \"react\";\nimport gsap from \"gsap\";\n\n/**\n * KineticHeadline — splits a headline into per-character spans and animates them\n * in with a 3D stagger (rise + rotateX + blur). No paid plugins: the split is\n * done in React, GSAP just choreographs. Honors prefers-reduced-motion.\n */\nexport function KineticHeadline({\n  text,\n  className = \"\",\n  as = \"h1\",\n  delay = 0.1,\n}: {\n  text: string;\n  className?: string;\n  as?: \"h1\" | \"h2\" | \"h3\" | \"p\";\n  delay?: number;\n}) {\n  const ref = useRef<HTMLElement>(null);\n\n  useEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n    const chars = el.querySelectorAll<HTMLElement>(\"[data-char]\");\n    const ctx = gsap.context(() => {\n      gsap.from(chars, {\n        yPercent: 120,\n        opacity: 0,\n        rotateX: -80,\n        filter: \"blur(8px)\",\n        stagger: 0.022,\n        duration: 0.9,\n        ease: \"expo.out\",\n        delay,\n      });\n    }, el);\n    return () => ctx.revert();\n  }, [text, delay]);\n\n  const words = text.split(\" \");\n  const Tag = as as ElementType;\n\n  return createElement(\n    Tag,\n    { ref, className, style: { perspective: 600 }, \"aria-label\": text },\n    words.map((word, wi) => (\n      <span key={wi} aria-hidden style={{ display: \"inline-block\", whiteSpace: \"nowrap\" }}>\n        {Array.from(word).map((ch, ci) => (\n          <span\n            key={ci}\n            data-char\n            style={{ display: \"inline-block\", transformStyle: \"preserve-3d\", willChange: \"transform\" }}\n          >\n            {ch}\n          </span>\n        ))}\n        {wi < words.length - 1 && (\n          <span style={{ display: \"inline-block\", width: \"0.28em\" }}> </span>\n        )}\n      </span>\n    ))\n  );\n}\n",
      "type": "registry:component",
      "target": "components/prism/KineticHeadline.tsx"
    }
  ]
}