{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "motion-faqs-accordion",
  "title": "Motion FAQs Accordion",
  "description": "A clean Motion-powered FAQ accordion with spring-animated content reveal.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/components/unlumen/motion-faqs-accordion/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface MotionAccordionItem {\n  question: React.ReactNode;\n  answer: React.ReactNode;\n}\n\nexport interface MotionAccordionProps {\n  items: MotionAccordionItem[];\n  /** @default 10 */\n  gap?: number;\n  className?: string;\n}\n\nfunction AccordionItem({\n  item,\n  isOpen,\n  onToggle,\n  itemId,\n  panelId,\n}: {\n  item: MotionAccordionItem;\n  isOpen: boolean;\n  onToggle: () => void;\n  itemId: string;\n  panelId: string;\n}) {\n  const contentRef = React.useRef<HTMLDivElement>(null);\n  const [contentH, setContentH] = React.useState(0);\n\n  React.useEffect(() => {\n    const el = contentRef.current;\n    if (!el) return;\n    const ro = new ResizeObserver(() => setContentH(el.scrollHeight));\n    ro.observe(el);\n    setContentH(el.scrollHeight);\n    return () => ro.disconnect();\n  }, []);\n\n  return (\n    <motion.div\n      layout\n      className={cn(\n        \"overflow-hidden rounded-[30px] bg-surface text-foreground shadow-xs\",\n        isOpen && \" \",\n      )}\n      transition={{ type: \"spring\", stiffness: 280, damping: 28, mass: 0.9 }}\n      animate={{ scale: isOpen ? 1 : 0.985 }}\n      initial={false}\n      style={{ originX: 0.5, originY: 0 }}\n    >\n      <button\n        id={itemId}\n        type=\"button\"\n        aria-controls={panelId}\n        aria-expanded={isOpen}\n        onClick={onToggle}\n        className=\"flex w-full cursor-pointer select-none items-center justify-between gap-4 px-7 py-5 text-left\"\n      >\n        <span className=\"text-[clamp(1.2rem,1.6vw,1.3rem)] font-medium tracking-tight leading-snug\">\n          {item.question}\n        </span>\n\n        <motion.span\n          aria-hidden=\"true\"\n          initial={false}\n          animate={{\n            rotate: isOpen ? 180 : 0,\n            scale: isOpen ? 1.05 : 1,\n          }}\n          transition={{ type: \"spring\", stiffness: 480, damping: 28 }}\n          className=\"inline-flex size-12 shrink-0 items-center justify-center text-foreground\"\n        >\n          {isOpen ? (\n            <svg\n              width=\"14\"\n              height=\"14\"\n              viewBox=\"0 0 14 2\"\n              fill=\"none\"\n              aria-hidden\n            >\n              <path\n                d=\"M1 1h12\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.75\"\n                strokeLinecap=\"round\"\n              />\n            </svg>\n          ) : (\n            <svg\n              width=\"14\"\n              height=\"14\"\n              viewBox=\"0 0 14 14\"\n              fill=\"none\"\n              aria-hidden\n            >\n              <path\n                d=\"M7 1v12M1 7h12\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.75\"\n                strokeLinecap=\"round\"\n              />\n            </svg>\n          )}\n        </motion.span>\n      </button>\n\n      <motion.div\n        id={panelId}\n        role=\"region\"\n        aria-labelledby={itemId}\n        animate={{\n          height: isOpen ? contentH : 0,\n          opacity: isOpen ? 1 : 0,\n        }}\n        initial={false}\n        transition={{\n          height: { type: \"spring\", stiffness: 340, damping: 34, mass: 0.9 },\n          opacity: { duration: 0.2, ease: \"easeOut\" },\n        }}\n        style={{ overflow: \"hidden\" }}\n      >\n        <motion.div\n          ref={contentRef}\n          animate={{ y: isOpen ? 0 : -8 }}\n          transition={{\n            type: \"spring\",\n            stiffness: 360,\n            damping: 30,\n            mass: 0.8,\n          }}\n          className=\"px-7 pb-7\"\n        >\n          <p className=\"text-lg leading-8 font-base tracking-normal text-foreground/75\">\n            {item.answer}\n          </p>\n        </motion.div>\n      </motion.div>\n    </motion.div>\n  );\n}\n\nexport function MotionAccordion({\n  items,\n  gap = 10,\n  className,\n}: MotionAccordionProps) {\n  const rawId = React.useId();\n  const baseId = `accordion-${rawId.replace(/:/g, \"\")}`;\n\n  const [openIndex, setOpenIndex] = React.useState<number | null>(null);\n\n  const toggle = (i: number) => setOpenIndex((prev) => (prev === i ? null : i));\n\n  return (\n    <div className={cn(\"w-full\", className)}>\n      <div className=\"flex flex-col rounded-[34px] p-3 \" style={{ gap }}>\n        {items.map((item, i) => (\n          <AccordionItem\n            key={i}\n            item={item}\n            isOpen={openIndex === i}\n            onToggle={() => toggle(i)}\n            itemId={`${baseId}-trigger-${i}`}\n            panelId={`${baseId}-panel-${i}`}\n          />\n        ))}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/motion-faqs-accordion.tsx"
    }
  ],
  "type": "registry:ui"
}