{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "accordion",
  "type": "registry:ui",
  "title": "Accordion",
  "description": "Vertical rounded-blade disclosure component with single or multiple open modes, arbitrary in-blade content, Motion-powered layout and presence choreography, keyboard navigation, and reduced-motion-safe state.",
  "dependencies": [
    "motion@^12.43.0"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://components.dethink.co.uk/r/dethink-base.json"
  ],
  "files": [
    {
      "path": "packages/components/src/components/accordion/accordion.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/accordion/accordion.tsx",
      "content": "import {\n  Children,\n  createContext,\n  forwardRef,\n  isValidElement,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n  type ButtonHTMLAttributes,\n  type HTMLAttributes,\n  type KeyboardEvent,\n  type ReactNode,\n} from \"react\";\nimport {\n  MotionConfig,\n  motion,\n  useReducedMotion,\n  type Transition,\n} from \"motion/react\";\nimport { cn } from \"../../utils/cn\";\nimport { useHydrated } from \"../../utils/use-hydrated\";\n\ndeclare const process:\n  | {\n      env?: {\n        NODE_ENV?: string;\n      };\n    }\n  | undefined;\n\nexport type AccordionType = \"single\" | \"multiple\";\nexport type AccordionValue = string | undefined;\nexport type AccordionMultipleValue = string[];\nexport type AccordionMotionPreset =\n  \"none\" | \"subtle\" | \"standard\" | \"expressive\";\nexport type AccordionBladeIconPosition = \"start\" | \"end\";\n\ntype AccordionBaseProps = Omit<\n  HTMLAttributes<HTMLDivElement>,\n  \"defaultValue\"\n> & {\n  collapsible?: boolean;\n  disabled?: boolean;\n  motionPreset?: AccordionMotionPreset;\n};\n\nexport type AccordionSingleProps = AccordionBaseProps & {\n  type?: \"single\";\n  value?: AccordionValue;\n  defaultValue?: AccordionValue;\n  onValueChange?: (value: AccordionValue) => void;\n};\n\nexport type AccordionMultipleProps = AccordionBaseProps & {\n  type: \"multiple\";\n  value?: AccordionMultipleValue;\n  defaultValue?: AccordionMultipleValue;\n  onValueChange?: (value: AccordionMultipleValue) => void;\n};\n\ntype MotionBackedDragProps =\n  | \"onAnimationEnd\"\n  | \"onAnimationEndCapture\"\n  | \"onAnimationIteration\"\n  | \"onAnimationIterationCapture\"\n  | \"onAnimationStart\"\n  | \"onAnimationStartCapture\"\n  | \"onDrag\"\n  | \"onDragCapture\"\n  | \"onDragEnd\"\n  | \"onDragEndCapture\"\n  | \"onDragEnter\"\n  | \"onDragEnterCapture\"\n  | \"onDragExit\"\n  | \"onDragExitCapture\"\n  | \"onDragLeave\"\n  | \"onDragLeaveCapture\"\n  | \"onDragOver\"\n  | \"onDragOverCapture\"\n  | \"onDragStart\"\n  | \"onDragStartCapture\";\n\nexport type AccordionProps = AccordionSingleProps | AccordionMultipleProps;\n\nexport interface AccordionItemProps extends Omit<\n  HTMLAttributes<HTMLDivElement>,\n  MotionBackedDragProps\n> {\n  value: string;\n  disabled?: boolean;\n}\n\nexport interface AccordionBladeProps extends Omit<\n  ButtonHTMLAttributes<HTMLButtonElement>,\n  MotionBackedDragProps\n> {\n  iconPosition?: AccordionBladeIconPosition;\n}\n\nexport type AccordionBladeIconProps = Omit<\n  HTMLAttributes<HTMLSpanElement>,\n  MotionBackedDragProps\n>;\nexport type AccordionBladeTextProps = HTMLAttributes<HTMLSpanElement>;\n\nexport interface AccordionContentProps extends Omit<\n  HTMLAttributes<HTMLDivElement>,\n  MotionBackedDragProps\n> {\n  forceMount?: boolean;\n}\n\ntype AccordionMotionConfig = {\n  contentEnabled: boolean;\n  itemTransition: Transition;\n  contentEnterTransition: Transition;\n  contentExitTransition: Transition;\n  iconTransition: Transition;\n  hoverScale: number;\n  tapScale: number;\n};\n\ntype BladeRegistryEntry = {\n  disabled: boolean;\n};\n\ntype AccordionContextValue = {\n  disabled: boolean;\n  isOpen: (value: string) => boolean;\n  motionConfig: AccordionMotionConfig;\n  motionPreset: AccordionMotionPreset;\n  reducedMotion: boolean;\n  registerBlade: (value: string, disabled: boolean) => () => void;\n  setFocusedValue: (value: string) => void;\n  toggleValue: (value: string) => void;\n};\n\ntype ItemContextValue = {\n  bladeId: string;\n  contentId: string;\n  disabled: boolean;\n  open: boolean;\n  value: string;\n};\n\ntype AccordionPartName = \"Item\" | \"Blade\" | \"Content\";\ntype MarkedAccordionPart = {\n  [ACCORDION_PART]?: AccordionPartName;\n};\n\nconst ACCORDION_PART = Symbol.for(\"@dethink/accordion.part\");\nconst AccordionContext = createContext<AccordionContextValue | null>(null);\nconst ItemContext = createContext<ItemContextValue | null>(null);\n\nconst accordionRootClasses =\n  \"w-full space-y-[var(--dt-space-3)] text-foreground\";\n\nconst accordionItemClasses =\n  \"group/accordion-item overflow-hidden rounded-xl border border-border bg-background shadow-sm outline-none data-[disabled]:opacity-60\";\n\nconst accordionBladeClasses =\n  \"relative flex min-h-density-control w-full cursor-pointer items-center justify-between gap-[var(--dt-space-3)] bg-background px-[var(--dt-space-5)] py-[var(--dt-space-4)] text-start text-foreground outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-60 data-[state=open]:bg-muted/70\";\n\nconst accordionBladeContentClasses =\n  \"pointer-events-none inline-flex min-w-0 flex-1 items-center gap-[var(--dt-space-3)]\";\n\nconst accordionBladeIconClasses =\n  \"pointer-events-none inline-flex shrink-0 items-center justify-center text-muted-foreground [&>svg]:size-4\";\n\nconst accordionBladeTextClasses =\n  \"pointer-events-none min-w-0 flex-1 text-sm font-semibold leading-6 text-foreground\";\n\nconst accordionContentClasses =\n  \"overflow-hidden border-t border-border bg-muted/35\";\n\nconst accordionContentInnerClasses =\n  \"px-[var(--dt-space-5)] py-[var(--dt-space-4)] text-sm leading-6 text-foreground\";\n\nconst accordionMotionSettings: Record<\n  AccordionMotionPreset,\n  {\n    duration: number;\n    offset: number;\n    hoverScale: number;\n    tapScale: number;\n  }\n> = {\n  none: { duration: 0, offset: 0, hoverScale: 1, tapScale: 1 },\n  subtle: { duration: 0.18, offset: 6, hoverScale: 1.002, tapScale: 0.998 },\n  standard: { duration: 0.24, offset: 10, hoverScale: 1.004, tapScale: 0.996 },\n  expressive: {\n    duration: 0.34,\n    offset: 14,\n    hoverScale: 1.006,\n    tapScale: 0.994,\n  },\n};\n\nexport function accordionClassNames({\n  className,\n}: Pick<AccordionProps, \"className\"> = {}) {\n  return cn(accordionRootClasses, className);\n}\n\nexport function accordionItemClassNames({\n  className,\n}: Pick<AccordionItemProps, \"className\"> = {}) {\n  return cn(accordionItemClasses, className);\n}\n\nexport function accordionBladeClassNames({\n  className,\n}: Pick<AccordionBladeProps, \"className\"> = {}) {\n  return cn(accordionBladeClasses, className);\n}\n\nexport function accordionBladeIconClassNames({\n  className,\n}: Pick<AccordionBladeIconProps, \"className\"> = {}) {\n  return cn(accordionBladeIconClasses, className);\n}\n\nexport function accordionBladeTextClassNames({\n  className,\n}: Pick<AccordionBladeTextProps, \"className\"> = {}) {\n  return cn(accordionBladeTextClasses, className);\n}\n\nexport function accordionContentClassNames({\n  className,\n}: Pick<AccordionContentProps, \"className\"> = {}) {\n  return cn(accordionContentClasses, className);\n}\n\nfunction useAccordionContext(component: string) {\n  const context = useContext(AccordionContext);\n\n  if (!context) {\n    throw new Error(`${component} must be used within Accordion.`);\n  }\n\n  return context;\n}\n\nfunction useItemContext(component: string) {\n  const context = useContext(ItemContext);\n\n  if (!context) {\n    throw new Error(`${component} must be used within Accordion.Item.`);\n  }\n\n  return context;\n}\n\nfunction isDevelopment() {\n  return (\n    typeof process === \"undefined\" || process.env?.NODE_ENV !== \"production\"\n  );\n}\n\nfunction getAccordionPartName(type: unknown) {\n  if (\n    typeof type === \"function\" ||\n    (typeof type === \"object\" && type !== null)\n  ) {\n    return (type as MarkedAccordionPart)[ACCORDION_PART];\n  }\n\n  return undefined;\n}\n\nfunction countDirectChildrenOfPart(\n  children: ReactNode,\n  partName: AccordionPartName,\n  type: unknown,\n) {\n  let count = 0;\n\n  Children.forEach(children, (child) => {\n    if (\n      isValidElement(child) &&\n      (child.type === type || getAccordionPartName(child.type) === partName)\n    ) {\n      count += 1;\n    }\n  });\n\n  return count;\n}\n\nfunction getDirectItemValues(children: ReactNode) {\n  const values: string[] = [];\n\n  Children.forEach(children, (child) => {\n    if (\n      isValidElement(child) &&\n      (child.type === AccordionItem ||\n        getAccordionPartName(child.type) === \"Item\")\n    ) {\n      values.push((child.props as AccordionItemProps).value);\n    }\n  });\n\n  return values;\n}\n\nfunction validateRootComposition(children: ReactNode) {\n  if (!isDevelopment()) {\n    return;\n  }\n\n  const seen = new Set<string>();\n\n  for (const itemValue of getDirectItemValues(children)) {\n    if (seen.has(itemValue)) {\n      throw new Error(`Duplicate Accordion.Item value \"${itemValue}\".`);\n    }\n\n    seen.add(itemValue);\n  }\n}\n\nfunction validateItemComposition(children: ReactNode, value: string) {\n  if (!isDevelopment()) {\n    return;\n  }\n\n  const bladeCount = countDirectChildrenOfPart(\n    children,\n    \"Blade\",\n    AccordionBlade,\n  );\n  const contentCount = countDirectChildrenOfPart(\n    children,\n    \"Content\",\n    AccordionContent,\n  );\n\n  if (bladeCount !== 1 || contentCount !== 1) {\n    throw new Error(\n      `Accordion.Item \"${value}\" must contain exactly one Blade and one Content.`,\n    );\n  }\n}\n\nfunction markAccordionPart(component: unknown, partName: AccordionPartName) {\n  Object.defineProperty(component, ACCORDION_PART, {\n    configurable: true,\n    value: partName,\n  });\n}\n\nfunction normalizeMultipleValue(value: unknown): string[] {\n  return Array.isArray(value) ? value : [];\n}\n\nfunction createMotionConfig(\n  motionPreset: AccordionMotionPreset,\n  reducedMotion: boolean,\n): AccordionMotionConfig {\n  const settings = reducedMotion\n    ? accordionMotionSettings.none\n    : accordionMotionSettings[motionPreset];\n  const duration = settings.duration;\n  const ease = [0.2, 0, 0, 1] as [number, number, number, number];\n  const springTransition: Transition =\n    duration === 0\n      ? { duration: 0 }\n      : {\n          type: \"spring\",\n          visualDuration: duration,\n          bounce: motionPreset === \"expressive\" ? 0.14 : 0.04,\n        };\n  // Collapsing feels crisper than expanding, so the exit runs a touch faster.\n  const exitDuration = duration * 0.85;\n\n  return {\n    contentEnabled: duration > 0,\n    itemTransition: springTransition,\n    contentEnterTransition: {\n      height: { duration, ease },\n      // Fade the content in before the panel finishes opening so text is\n      // fully legible as it settles.\n      opacity: { duration: Math.max(duration * 0.6, 0.01), ease },\n    },\n    contentExitTransition: {\n      height: { duration: exitDuration, ease },\n      // Fade out early so the panel reads as empty before it fully collapses.\n      opacity: { duration: Math.max(exitDuration * 0.5, 0.01), ease },\n    },\n    iconTransition: {\n      duration,\n      ease,\n    },\n    hoverScale: settings.hoverScale,\n    tapScale: settings.tapScale,\n  };\n}\n\nfunction renderBladeChildren(children: ReactNode) {\n  return Children.map(children, (child) => {\n    if (typeof child === \"string\" || typeof child === \"number\") {\n      return (\n        <span\n          className={accordionBladeTextClasses}\n          data-slot=\"accordion-blade-text\"\n        >\n          {child}\n        </span>\n      );\n    }\n\n    return child;\n  });\n}\n\nfunction getEnabledBladeButtons(bladeId: string) {\n  if (typeof document === \"undefined\") {\n    return [];\n  }\n\n  const root = document\n    .getElementById(bladeId)\n    ?.closest('[data-slot=\"accordion\"]');\n\n  return Array.from(\n    root?.querySelectorAll<HTMLButtonElement>(\n      'button[data-slot=\"accordion-blade\"]:not(:disabled)',\n    ) ?? [],\n  );\n}\n\nfunction focusBladeAtOffset(bladeId: string, offset: number) {\n  const blades = getEnabledBladeButtons(bladeId);\n  const currentIndex = blades.findIndex((blade) => blade.id === bladeId);\n\n  if (currentIndex === -1 || blades.length === 0) {\n    return;\n  }\n\n  const nextIndex = (currentIndex + offset + blades.length) % blades.length;\n\n  blades[nextIndex]?.focus();\n}\n\nfunction focusBladeAtIndex(bladeId: string, index: number) {\n  const blades = getEnabledBladeButtons(bladeId);\n  const targetIndex = index < 0 ? blades.length + index : index;\n\n  blades[targetIndex]?.focus();\n}\n\nconst AccordionRoot = forwardRef<HTMLDivElement, AccordionProps>(\n  (props, ref) => {\n    const {\n      type = \"single\",\n      value,\n      defaultValue,\n      onValueChange,\n      collapsible = true,\n      disabled = false,\n      motionPreset = \"standard\",\n      className,\n      children,\n      ...rootProps\n    } = props;\n    const controlled = Object.hasOwn(props, \"value\");\n    const hydrated = useHydrated();\n    const prefersReducedMotion = useReducedMotion();\n    const reducedMotion =\n      motionPreset === \"none\" || (hydrated && prefersReducedMotion === true);\n    const [uncontrolledValue, setUncontrolledValue] = useState<\n      AccordionValue | AccordionMultipleValue\n    >(\n      defaultValue ??\n        (type === \"multiple\" ? ([] as AccordionMultipleValue) : undefined),\n    );\n    const currentValue = controlled ? value : uncontrolledValue;\n    const itemValues = useMemo(() => getDirectItemValues(children), [children]);\n    const [, setFocusedValue] = useState<AccordionValue>();\n    const [bladeRegistry, setBladeRegistry] = useState(\n      () => new Map<string, BladeRegistryEntry>(),\n    );\n    const motionConfig = useMemo(\n      () => createMotionConfig(motionPreset, reducedMotion),\n      [motionPreset, reducedMotion],\n    );\n\n    validateRootComposition(children);\n\n    const registerBlade = useCallback((value: string, disabled: boolean) => {\n      setBladeRegistry((current) => {\n        if (current.get(value)?.disabled === disabled) return current;\n        return new Map(current).set(value, { disabled });\n      });\n      return () =>\n        setBladeRegistry((current) => {\n          if (!current.has(value)) return current;\n          const next = new Map(current);\n          next.delete(value);\n          return next;\n        });\n    }, []);\n\n    const enabledItemValues = useMemo(\n      () =>\n        itemValues.filter(\n          (itemValue) => !bladeRegistry.get(itemValue)?.disabled,\n        ),\n      [bladeRegistry, itemValues],\n    );\n\n    const isOpen = useCallback(\n      (itemValue: string) =>\n        type === \"multiple\"\n          ? normalizeMultipleValue(currentValue).includes(itemValue)\n          : currentValue === itemValue,\n      [currentValue, type],\n    );\n\n    const toggleValue = useCallback(\n      (itemValue: string) => {\n        if (disabled || !enabledItemValues.includes(itemValue)) {\n          return;\n        }\n\n        if (type === \"multiple\") {\n          const current = normalizeMultipleValue(currentValue);\n          const next = current.includes(itemValue)\n            ? current.filter((valueItem) => valueItem !== itemValue)\n            : [...current, itemValue];\n\n          if (!controlled) {\n            setUncontrolledValue(next);\n          }\n\n          (\n            onValueChange as\n              ((value: AccordionMultipleValue) => void) | undefined\n          )?.(next);\n          return;\n        }\n\n        const next =\n          currentValue === itemValue\n            ? collapsible\n              ? undefined\n              : currentValue\n            : itemValue;\n\n        if (next === currentValue) {\n          return;\n        }\n\n        if (!controlled) {\n          setUncontrolledValue(next);\n        }\n\n        (onValueChange as ((value: AccordionValue) => void) | undefined)?.(\n          next,\n        );\n      },\n      [\n        collapsible,\n        controlled,\n        currentValue,\n        disabled,\n        enabledItemValues,\n        onValueChange,\n        type,\n      ],\n    );\n\n    const contextValue = useMemo<AccordionContextValue>(\n      () => ({\n        disabled,\n        isOpen,\n        motionConfig,\n        motionPreset,\n        reducedMotion,\n        registerBlade,\n        setFocusedValue,\n        toggleValue,\n      }),\n      [\n        disabled,\n        isOpen,\n        motionConfig,\n        motionPreset,\n        reducedMotion,\n        registerBlade,\n        toggleValue,\n      ],\n    );\n\n    return (\n      <AccordionContext.Provider value={contextValue}>\n        <MotionConfig reducedMotion={reducedMotion ? \"always\" : \"user\"}>\n          <div\n            {...rootProps}\n            ref={ref}\n            data-slot=\"accordion\"\n            data-orientation=\"vertical\"\n            data-motion-preset={motionPreset}\n            data-reduced-motion={reducedMotion ? \"true\" : undefined}\n            data-disabled={disabled ? \"\" : undefined}\n            className={accordionClassNames({ className })}\n          >\n            {children}\n          </div>\n        </MotionConfig>\n      </AccordionContext.Provider>\n    );\n  },\n);\n\nAccordionRoot.displayName = \"Accordion\";\n\nexport const AccordionItem = forwardRef<HTMLDivElement, AccordionItemProps>(\n  ({ value, disabled = false, className, children, ...props }, ref) => {\n    const {\n      disabled: rootDisabled,\n      isOpen,\n      motionPreset,\n    } = useAccordionContext(\"Accordion.Item\");\n    const generatedId = useId();\n    const open = isOpen(value);\n    const resolvedDisabled = rootDisabled || disabled;\n    const bladeId = `accordion-blade-${generatedId}`;\n    const contentId = `accordion-content-${generatedId}`;\n    const itemContextValue = useMemo<ItemContextValue>(\n      () => ({\n        bladeId,\n        contentId,\n        disabled: resolvedDisabled,\n        open,\n        value,\n      }),\n      [bladeId, contentId, open, resolvedDisabled, value],\n    );\n\n    validateItemComposition(children, value);\n\n    return (\n      <ItemContext.Provider value={itemContextValue}>\n        <motion.div\n          {...props}\n          ref={ref}\n          data-slot=\"accordion-item\"\n          data-state={open ? \"open\" : \"closed\"}\n          data-disabled={resolvedDisabled ? \"\" : undefined}\n          data-motion-preset={motionPreset}\n          data-orientation=\"vertical\"\n          className={accordionItemClassNames({ className })}\n        >\n          {children}\n        </motion.div>\n      </ItemContext.Provider>\n    );\n  },\n);\n\nAccordionItem.displayName = \"AccordionItem\";\n\nexport const AccordionBlade = forwardRef<\n  HTMLButtonElement,\n  AccordionBladeProps\n>(\n  (\n    {\n      className,\n      children,\n      disabled = false,\n      iconPosition = \"start\",\n      onClick,\n      onFocus,\n      onKeyDown,\n      type = \"button\",\n      ...props\n    },\n    ref,\n  ) => {\n    const {\n      motionConfig,\n      motionPreset,\n      registerBlade,\n      setFocusedValue,\n      toggleValue,\n    } = useAccordionContext(\"Accordion.Blade\");\n    const {\n      bladeId,\n      contentId,\n      disabled: itemDisabled,\n      open,\n      value,\n    } = useItemContext(\"Accordion.Blade\");\n    const resolvedDisabled = itemDisabled || disabled;\n\n    useEffect(\n      () => registerBlade(value, resolvedDisabled),\n      [registerBlade, resolvedDisabled, value],\n    );\n\n    const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n      onKeyDown?.(event);\n\n      if (event.defaultPrevented) {\n        return;\n      }\n\n      if (event.key === \"ArrowUp\") {\n        event.preventDefault();\n        focusBladeAtOffset(bladeId, -1);\n        return;\n      }\n\n      if (event.key === \"ArrowDown\") {\n        event.preventDefault();\n        focusBladeAtOffset(bladeId, 1);\n        return;\n      }\n\n      if (event.key === \"Home\") {\n        event.preventDefault();\n        focusBladeAtIndex(bladeId, 0);\n        return;\n      }\n\n      if (event.key === \"End\") {\n        event.preventDefault();\n        focusBladeAtIndex(bladeId, -1);\n      }\n    };\n\n    return (\n      <motion.button\n        {...props}\n        ref={ref}\n        id={bladeId}\n        aria-controls={contentId}\n        aria-expanded={open}\n        data-slot=\"accordion-blade\"\n        data-state={open ? \"open\" : \"closed\"}\n        data-disabled={resolvedDisabled ? \"\" : undefined}\n        data-icon-position={iconPosition}\n        data-motion-preset={motionPreset}\n        data-orientation=\"vertical\"\n        className={accordionBladeClassNames({ className })}\n        disabled={resolvedDisabled}\n        type={type}\n        whileHover={{ scale: motionConfig.hoverScale }}\n        whileTap={{ scale: motionConfig.tapScale }}\n        transition={motionConfig.itemTransition}\n        onClick={(event) => {\n          onClick?.(event);\n\n          if (!event.defaultPrevented) {\n            setFocusedValue(value);\n            toggleValue(value);\n          }\n        }}\n        onFocus={(event) => {\n          onFocus?.(event);\n\n          if (!event.defaultPrevented) {\n            setFocusedValue(value);\n          }\n        }}\n        onKeyDown={handleKeyDown}\n      >\n        <span className={accordionBladeContentClasses}>\n          {renderBladeChildren(children)}\n        </span>\n      </motion.button>\n    );\n  },\n);\n\nAccordionBlade.displayName = \"AccordionBlade\";\n\nexport const AccordionBladeIcon = forwardRef<\n  HTMLSpanElement,\n  AccordionBladeIconProps\n>(({ className, ...props }, ref) => {\n  const { motionConfig, motionPreset } = useAccordionContext(\n    \"Accordion.BladeIcon\",\n  );\n  const { open } = useItemContext(\"Accordion.BladeIcon\");\n\n  return (\n    <motion.span\n      {...props}\n      ref={ref}\n      aria-hidden={props[\"aria-hidden\"] ?? true}\n      animate={{ rotate: open ? 90 : 0 }}\n      data-slot=\"accordion-blade-icon\"\n      data-state={open ? \"open\" : \"closed\"}\n      data-motion-preset={motionPreset}\n      data-orientation=\"vertical\"\n      className={accordionBladeIconClassNames({ className })}\n      transition={motionConfig.iconTransition}\n    />\n  );\n});\n\nAccordionBladeIcon.displayName = \"AccordionBladeIcon\";\n\nexport const AccordionBladeText = forwardRef<\n  HTMLSpanElement,\n  AccordionBladeTextProps\n>(({ className, ...props }, ref) => {\n  const { motionPreset } = useAccordionContext(\"Accordion.BladeText\");\n  const { open } = useItemContext(\"Accordion.BladeText\");\n\n  return (\n    <span\n      {...props}\n      ref={ref}\n      data-slot=\"accordion-blade-text\"\n      data-state={open ? \"open\" : \"closed\"}\n      data-motion-preset={motionPreset}\n      data-orientation=\"vertical\"\n      className={accordionBladeTextClassNames({ className })}\n    />\n  );\n});\n\nAccordionBladeText.displayName = \"AccordionBladeText\";\n\nexport const AccordionContent = forwardRef<\n  HTMLDivElement,\n  AccordionContentProps\n>(({ className, children, forceMount = false, ...props }, ref) => {\n  const { motionConfig, motionPreset } =\n    useAccordionContext(\"Accordion.Content\");\n  const { bladeId, contentId, disabled, open } =\n    useItemContext(\"Accordion.Content\");\n  const contentRef = useRef<HTMLDivElement | null>(null);\n  const [previousOpen, setPreviousOpen] = useState(open);\n  const shouldReturnFocusRef = useRef(false);\n  const [contentSettled, setContentSettled] = useState(!open);\n  const { contentEnabled, contentEnterTransition, contentExitTransition } =\n    motionConfig;\n  const mergedRef = useCallback(\n    (node: HTMLDivElement | null) => {\n      contentRef.current = node;\n\n      if (typeof ref === \"function\") {\n        ref(node);\n      } else if (ref) {\n        ref.current = node;\n      }\n    },\n    [ref],\n  );\n\n  if (previousOpen !== open) {\n    setPreviousOpen(open);\n    if (open) setContentSettled(false);\n  }\n\n  useLayoutEffect(() => {\n    if (shouldReturnFocusRef.current && !open) {\n      document.getElementById(bladeId)?.focus();\n      shouldReturnFocusRef.current = false;\n    }\n  }, [bladeId, open]);\n\n  const hidden = contentEnabled ? !open && contentSettled : !open;\n\n  // Static path (reduced motion / motionPreset=\"none\"): non-force-mounted\n  // content is removed from the DOM when closed, matching the previous\n  // behavior and the component's documented contract.\n  const inner =\n    contentEnabled || open || forceMount ? (\n      <div\n        data-slot=\"accordion-content-inner\"\n        className={accordionContentInnerClasses}\n      >\n        {children}\n      </div>\n    ) : null;\n\n  // Animate the region's height from 0 to its natural size with overflow\n  // clipping, which reveals the static inner content without scaling or\n  // reflow jumps. Opacity is layered on for a soft fade.\n  const animationProps = contentEnabled\n    ? {\n        initial: false,\n        animate: open ? \"open\" : \"closed\",\n        variants: {\n          open: {\n            height: \"auto\",\n            opacity: 1,\n            transition: contentEnterTransition,\n          },\n          closed: {\n            height: 0,\n            opacity: 0,\n            transition: contentExitTransition,\n          },\n        },\n        onAnimationComplete: (definition: unknown) => {\n          if (definition === \"closed\") {\n            setContentSettled(true);\n          }\n        },\n      }\n    : {};\n\n  return (\n    <motion.div\n      {...props}\n      ref={mergedRef}\n      onFocusCapture={(event) => {\n        shouldReturnFocusRef.current = true;\n        props.onFocusCapture?.(event);\n      }}\n      onBlurCapture={(event) => {\n        shouldReturnFocusRef.current = event.currentTarget.contains(\n          event.relatedTarget,\n        );\n        props.onBlurCapture?.(event);\n      }}\n      id={contentId}\n      role=\"region\"\n      aria-labelledby={bladeId}\n      data-slot=\"accordion-content\"\n      data-state={open ? \"open\" : \"closed\"}\n      data-disabled={disabled ? \"\" : undefined}\n      data-motion-preset={motionPreset}\n      data-orientation=\"vertical\"\n      className={accordionContentClassNames({ className })}\n      hidden={hidden}\n      {...animationProps}\n    >\n      {inner}\n    </motion.div>\n  );\n});\n\nAccordionContent.displayName = \"AccordionContent\";\n\nmarkAccordionPart(AccordionItem, \"Item\");\nmarkAccordionPart(AccordionBlade, \"Blade\");\nmarkAccordionPart(AccordionContent, \"Content\");\n\ntype AccordionComponent = typeof AccordionRoot & {\n  Item: typeof AccordionItem;\n  Blade: typeof AccordionBlade;\n  BladeIcon: typeof AccordionBladeIcon;\n  BladeText: typeof AccordionBladeText;\n  Content: typeof AccordionContent;\n};\n\nexport const Accordion = Object.assign(AccordionRoot, {\n  Item: AccordionItem,\n  Blade: AccordionBlade,\n  BladeIcon: AccordionBladeIcon,\n  BladeText: AccordionBladeText,\n  Content: AccordionContent,\n}) as AccordionComponent;\n"
    },
    {
      "path": "packages/components/src/components/accordion/index.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/accordion/index.ts",
      "content": "export {\n  Accordion,\n  AccordionBlade,\n  AccordionBladeIcon,\n  AccordionBladeText,\n  AccordionContent,\n  AccordionItem,\n  accordionBladeClassNames,\n  accordionBladeIconClassNames,\n  accordionBladeTextClassNames,\n  accordionClassNames,\n  accordionContentClassNames,\n  accordionItemClassNames,\n  type AccordionBladeIconPosition,\n  type AccordionBladeIconProps,\n  type AccordionBladeProps,\n  type AccordionBladeTextProps,\n  type AccordionContentProps,\n  type AccordionItemProps,\n  type AccordionMotionPreset,\n  type AccordionMultipleProps,\n  type AccordionMultipleValue,\n  type AccordionProps,\n  type AccordionSingleProps,\n  type AccordionType,\n  type AccordionValue,\n} from \"./accordion\";\n"
    },
    {
      "path": "packages/components/src/utils/cn.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/cn.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"
    }
  ]
}
