{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-hover-card",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-hover-card.tsx",
      "content": "\"use client\";\n\nimport { Popover as PopoverPrimitive } from \"@base-ui/react/popover\";\nimport { motion } from \"motion/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n// ---------------------------------------------------------------------------\n// Design tokens\n// ---------------------------------------------------------------------------\n\nconst controlCornerClassName =\n  \"rounded-lg supports-[corner-shape:squircle]:corner-squircle supports-[corner-shape:squircle]:rounded-[11px]\";\n\nconst surfaceCornerClassName =\n  \"rounded-lg supports-[corner-shape:squircle]:corner-squircle supports-[corner-shape:squircle]:rounded-[12px]\";\n\nconst hoverCardThemeClassName =\n  \"[--hc-surface:#ffffff] [--hc-foreground:#111111] [--hc-border:#e3e7ec] [--hc-ring:rgba(17,17,17,0.16)] dark:[--hc-surface:#111111] dark:[--hc-foreground:#f6f3ec] dark:[--hc-border:#2b2a25] dark:[--hc-ring:rgba(246,243,236,0.18)]\";\n\nconst hoverCardPanelClassName = cn(\n  surfaceCornerClassName,\n  \"relative z-50 w-72 transform-gpu border border-[color:var(--hc-border)] bg-[color:var(--hc-surface)] p-4 text-[color:var(--hc-foreground)] shadow-none outline-none\"\n);\n\nconst hoverCardTriggerClassName = cn(\n  controlCornerClassName,\n  \"inline-flex min-h-9 cursor-pointer items-center px-1.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:color-mix(in_oklch,var(--hc-ring),transparent_40%)] focus-visible:ring-offset-2 focus-visible:ring-offset-[color:var(--hc-surface)]\"\n);\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Side = \"top\" | \"right\" | \"bottom\" | \"left\";\ntype Align = \"start\" | \"center\" | \"end\";\ntype CollisionPadding = React.ComponentPropsWithoutRef<\n  typeof PopoverPrimitive.Positioner\n>[\"collisionPadding\"];\n\ntype HoverCardContextValue = {\n  actionsRef: React.RefObject<PopoverPrimitive.Root.Actions | null>;\n  open: boolean;\n  contentId: string;\n  triggerId: string;\n  getTriggerNode: () => HTMLElement | null;\n  setContentNode: (node: HTMLDivElement | null) => void;\n  setTriggerNode: (node: HTMLElement | null) => void;\n  handleFocusEnd: (event: React.FocusEvent<HTMLElement>) => void;\n  handleFocusStart: () => void;\n  handleHoverEnd: (event: React.PointerEvent<HTMLElement>) => void;\n  handleHoverStart: (event: React.PointerEvent<HTMLElement>) => void;\n};\n\n// Base UI popup render function receives its own props shape\ntype PopupRenderProps = React.HTMLAttributes<HTMLDivElement> & {\n  children?: React.ReactNode;\n  className?: string;\n  ref?: React.Ref<HTMLDivElement>;\n  style?: React.CSSProperties;\n};\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nconst HoverCardContext = React.createContext<HoverCardContextValue | null>(\n  null\n);\n\n// ---------------------------------------------------------------------------\n// Motion config\n// ---------------------------------------------------------------------------\n\nconst FLUID_EASE = [0.16, 1, 0.3, 1] as const;\nconst HOVER_CARD_EXIT_EASE = [0.4, 0, 0.6, 1] as const;\n\nconst HOVER_CARD_SPRING = {\n  type: \"spring\" as const,\n  stiffness: 260,\n  damping: 18,\n  mass: 0.65,\n};\n\nfunction getSideMotionOffset(side: Side) {\n  switch (side) {\n    case \"top\":\n      return { x: 0, y: 6 };\n    case \"right\":\n      return { x: -6, y: 0 };\n    case \"left\":\n      return { x: 6, y: 0 };\n    default:\n      return { x: 0, y: -6 };\n  }\n}\n\nfunction getHoverCardMotion(side: Side) {\n  const offset = getSideMotionOffset(side);\n\n  return {\n    animate: { opacity: 1, scale: 1, x: 0, y: 0, filter: \"blur(0px)\" },\n    closed: { opacity: 0, scale: 0.95, filter: \"blur(8px)\", ...offset },\n    initial: { opacity: 0, scale: 0.95, filter: \"blur(8px)\", ...offset },\n    openTransition: {\n      opacity: { duration: 0.32, ease: FLUID_EASE },\n      scale: HOVER_CARD_SPRING,\n      x: HOVER_CARD_SPRING,\n      y: HOVER_CARD_SPRING,\n      filter: { duration: 0.28, ease: [0, 0, 0.2, 1] as const },\n    },\n    closedTransition: {\n      opacity: { duration: 0.18, ease: HOVER_CARD_EXIT_EASE },\n      scale: { duration: 0.18, ease: HOVER_CARD_EXIT_EASE },\n      x: { duration: 0.18, ease: HOVER_CARD_EXIT_EASE },\n      y: { duration: 0.18, ease: HOVER_CARD_EXIT_EASE },\n      filter: { duration: 0.18, ease: HOVER_CARD_EXIT_EASE },\n    },\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Hover bridge — invisible hit-area between trigger and panel so the card\n// does not close while the cursor travels across the gap.\n// ---------------------------------------------------------------------------\n\nconst hoverBridgeStyles: Record<Side, string> = {\n  top: \"before:pointer-events-auto before:absolute before:right-0 before:bottom-[calc(var(--hover-card-bridge-size)*-1)] before:left-0 before:h-[var(--hover-card-bridge-size)] before:bg-transparent before:content-['']\",\n  right:\n    \"before:pointer-events-auto before:absolute before:top-0 before:bottom-0 before:left-[calc(var(--hover-card-bridge-size)*-1)] before:w-[var(--hover-card-bridge-size)] before:bg-transparent before:content-['']\",\n  bottom:\n    \"before:pointer-events-auto before:absolute before:top-[calc(var(--hover-card-bridge-size)*-1)] before:right-0 before:left-0 before:h-[var(--hover-card-bridge-size)] before:bg-transparent before:content-['']\",\n  left: \"before:pointer-events-auto before:absolute before:top-0 before:right-[calc(var(--hover-card-bridge-size)*-1)] before:bottom-0 before:w-[var(--hover-card-bridge-size)] before:bg-transparent before:content-['']\",\n};\n\n// ---------------------------------------------------------------------------\n// Ref utilities\n// ---------------------------------------------------------------------------\n\nconst assignRef = <T,>(ref: React.ForwardedRef<T>, value: T | null) => {\n  if (typeof ref === \"function\") {\n    ref(value);\n    return;\n  }\n\n  if (ref) {\n    ref.current = value;\n  }\n};\n\nconst getElementRef = <T,>(element: React.ReactElement) =>\n  (\n    element as React.ReactElement & {\n      ref?: React.Ref<T>;\n      props: { ref?: React.Ref<T> };\n    }\n  ).ref ??\n  (\n    element as React.ReactElement & {\n      ref?: React.Ref<T>;\n      props: { ref?: React.Ref<T> };\n    }\n  ).props.ref;\n\nconst composeRefs =\n  <T,>(...refs: Array<React.Ref<T> | undefined>) =>\n  (value: T | null) => {\n    for (const ref of refs) {\n      if (typeof ref === \"function\") {\n        ref(value);\n        continue;\n      }\n\n      if (ref) {\n        (ref as React.MutableRefObject<T | null>).current = value;\n      }\n    }\n  };\n\nfunction setRef<T>(ref: React.Ref<T> | undefined, value: T | null) {\n  if (typeof ref === \"function\") {\n    ref(value);\n    return;\n  }\n\n  if (ref) {\n    (ref as React.MutableRefObject<T | null>).current = value;\n  }\n}\n\n// Strips Base UI–owned props from popup render-props so we don't forward them\n// twice (Base UI already applies them to the outer wrapper).\nfunction resolvePopupProps(popupProps: PopupRenderProps) {\n  const {\n    children: _popupChildren,\n    className: popupClassName,\n    onAnimationEnd: _onAnimationEnd,\n    onAnimationIteration: _onAnimationIteration,\n    onAnimationStart: _onAnimationStart,\n    onDrag: _onDrag,\n    onDragEnd: _onDragEnd,\n    onDragEnter: _onDragEnter,\n    onDragExit: _onDragExit,\n    onDragLeave: _onDragLeave,\n    onDragOver: _onDragOver,\n    onDragStart: _onDragStart,\n    onDrop: _onDrop,\n    ref: popupRef,\n    style: popupStyle,\n    ...resolvedPopupProps\n  } = popupProps;\n\n  return {\n    popupClassName,\n    popupRef,\n    popupStyle,\n    resolvedPopupProps,\n  };\n}\n\n// ---------------------------------------------------------------------------\n// useHoverCard hook\n// ---------------------------------------------------------------------------\n\nconst useHoverCard = () => {\n  const context = React.useContext(HoverCardContext);\n\n  if (!context) {\n    throw new Error(\"HoverCard components must be used inside <HoverCard>\");\n  }\n\n  return context;\n};\n\n// ---------------------------------------------------------------------------\n// HoverCard — root\n// ---------------------------------------------------------------------------\n\ntype HoverCardProps = {\n  className?: string;\n  openDelay?: number;\n  closeDelay?: number;\n  children?: React.ReactNode;\n  /** Controlled open state. */\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** Uncontrolled initial open state. */\n  defaultOpen?: boolean;\n};\n\nconst HoverCard = ({\n  className,\n  openDelay = 80,\n  closeDelay = 120,\n  children,\n  open: openProp,\n  onOpenChange,\n  defaultOpen = false,\n}: HoverCardProps) => {\n  const actionsRef = React.useRef<PopoverPrimitive.Root.Actions | null>(null);\n  const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n  const isControlled = openProp !== undefined;\n  const open = isControlled ? openProp : internalOpen;\n\n  const setOpen = React.useCallback(\n    (nextOpen: boolean) => {\n      if (!isControlled) {\n        setInternalOpen(nextOpen);\n      }\n      onOpenChange?.(nextOpen);\n    },\n    [isControlled, onOpenChange]\n  );\n\n  const timeoutRef = React.useRef<number | null>(null);\n  const triggerRef = React.useRef<HTMLElement | null>(null);\n  const contentRef = React.useRef<HTMLDivElement | null>(null);\n  const isHoveringRef = React.useRef(false);\n  const reactId = React.useId();\n\n  const clearTimer = React.useCallback(() => {\n    if (timeoutRef.current !== null) {\n      window.clearTimeout(timeoutRef.current);\n      timeoutRef.current = null;\n    }\n  }, []);\n\n  // Clear any pending timer when the component unmounts.\n  React.useEffect(() => clearTimer, [clearTimer]);\n\n  const containsInteractiveNode = React.useCallback((node: Node | null) => {\n    return Boolean(\n      node &&\n        (triggerRef.current?.contains(node) ||\n          contentRef.current?.contains(node))\n    );\n  }, []);\n\n  const hasFocusWithin = React.useCallback(() => {\n    const activeElement = document.activeElement;\n    return (\n      activeElement instanceof Node && containsInteractiveNode(activeElement)\n    );\n  }, [containsInteractiveNode]);\n\n  const scheduleOpen = React.useCallback(() => {\n    clearTimer();\n    timeoutRef.current = window.setTimeout(() => setOpen(true), openDelay);\n  }, [clearTimer, openDelay, setOpen]);\n\n  const scheduleClose = React.useCallback(() => {\n    clearTimer();\n    timeoutRef.current = window.setTimeout(() => setOpen(false), closeDelay);\n  }, [clearTimer, closeDelay, setOpen]);\n\n  const handleHoverStart = React.useCallback(\n    (event: React.PointerEvent<HTMLElement>) => {\n      if (event.pointerType === \"touch\") return;\n      isHoveringRef.current = true;\n\n      if (open) {\n        // Already open — cancel any scheduled close.\n        clearTimer();\n        return;\n      }\n\n      scheduleOpen();\n    },\n    [clearTimer, open, scheduleOpen]\n  );\n\n  const handleHoverEnd = React.useCallback(\n    (event: React.PointerEvent<HTMLElement>) => {\n      if (event.pointerType === \"touch\") return;\n\n      const nextTarget = event.relatedTarget;\n\n      // Cursor moved into the other interactive zone (trigger ↔ content).\n      if (nextTarget instanceof Node && containsInteractiveNode(nextTarget)) {\n        return;\n      }\n\n      isHoveringRef.current = false;\n\n      // Keep open if focus is still inside.\n      if (hasFocusWithin()) return;\n\n      scheduleClose();\n    },\n    [containsInteractiveNode, hasFocusWithin, scheduleClose]\n  );\n\n  const handleFocusStart = React.useCallback(() => {\n    clearTimer();\n    setOpen(true);\n  }, [clearTimer, setOpen]);\n\n  const handleFocusEnd = React.useCallback(\n    (event: React.FocusEvent<HTMLElement>) => {\n      const nextTarget = event.relatedTarget;\n\n      // Focus moved to another interactive zone — keep open.\n      if (nextTarget instanceof Node && containsInteractiveNode(nextTarget)) {\n        return;\n      }\n\n      // Cursor is still hovering — keep open (e.g. user clicked static text).\n      if (isHoveringRef.current) return;\n\n      clearTimer();\n      setOpen(false);\n    },\n    [clearTimer, containsInteractiveNode, setOpen]\n  );\n\n  return (\n    <HoverCardContext.Provider\n      value={{\n        actionsRef,\n        open,\n        contentId: `${reactId}-content`,\n        triggerId: `${reactId}-trigger`,\n        getTriggerNode: () => triggerRef.current,\n        setContentNode: (node) => {\n          contentRef.current = node;\n        },\n        setTriggerNode: (node) => {\n          triggerRef.current = node;\n        },\n        handleFocusEnd,\n        handleFocusStart,\n        handleHoverEnd,\n        handleHoverStart,\n      }}\n    >\n      <PopoverPrimitive.Root\n        actionsRef={actionsRef}\n        modal={false}\n        onOpenChange={(nextOpen, eventDetails) => {\n          clearTimer();\n\n          if (!eventDetails.isCanceled) {\n            setOpen(nextOpen);\n          }\n        }}\n        open={open}\n      >\n        <span\n          className={cn(\n            hoverCardThemeClassName,\n            \"inline-flex w-fit\",\n            className\n          )}\n        >\n          {children}\n        </span>\n      </PopoverPrimitive.Root>\n    </HoverCardContext.Provider>\n  );\n};\n\n// ---------------------------------------------------------------------------\n// HoverCardTrigger\n// ---------------------------------------------------------------------------\n\ntype HoverCardTriggerProps = React.ComponentPropsWithoutRef<\"button\"> & {\n  asChild?: boolean;\n};\n\ntype HoverCardTriggerElementProps = React.HTMLAttributes<HTMLElement> & {\n  className?: string;\n  \"data-state\"?: \"closed\" | \"open\";\n};\n\ntype HoverCardTriggerChildElement = React.ReactElement<\n  HoverCardTriggerElementProps & React.RefAttributes<HTMLElement>\n>;\n\nconst HoverCardTrigger = React.forwardRef<\n  HTMLButtonElement,\n  HoverCardTriggerProps\n>(\n  (\n    {\n      asChild,\n      children,\n      className,\n      onBlur,\n      onFocus,\n      onPointerEnter,\n      onPointerLeave,\n      type,\n      ...props\n    },\n    ref\n  ) => {\n    const {\n      open,\n      contentId,\n      triggerId,\n      setTriggerNode,\n      handleFocusEnd,\n      handleFocusStart,\n      handleHoverEnd,\n      handleHoverStart,\n    } = useHoverCard();\n\n    const triggerClassName = cn(\n      asChild\n        ? \"inline-flex cursor-pointer items-center focus-visible:outline-none\"\n        : hoverCardTriggerClassName,\n      className\n    );\n\n    const handleBlurEvent = (event: React.FocusEvent<HTMLElement>) => {\n      onBlur?.(event as React.FocusEvent<HTMLButtonElement>);\n      if (event.defaultPrevented) return;\n      handleFocusEnd(event);\n    };\n\n    const handleFocusEvent = (event: React.FocusEvent<HTMLElement>) => {\n      onFocus?.(event as React.FocusEvent<HTMLButtonElement>);\n      if (event.defaultPrevented) return;\n      handleFocusStart();\n    };\n\n    const handlePointerEnterEvent = (\n      event: React.PointerEvent<HTMLElement>\n    ) => {\n      onPointerEnter?.(event as React.PointerEvent<HTMLButtonElement>);\n      if (event.defaultPrevented) return;\n      handleHoverStart(event);\n    };\n\n    const handlePointerLeaveEvent = (\n      event: React.PointerEvent<HTMLElement>\n    ) => {\n      onPointerLeave?.(event as React.PointerEvent<HTMLButtonElement>);\n      if (event.defaultPrevented) return;\n      handleHoverEnd(event);\n    };\n\n    if (asChild) {\n      if (!React.isValidElement<HoverCardTriggerElementProps>(children)) {\n        throw new Error(\n          \"HoverCardTrigger with asChild expects a single React element child.\"\n        );\n      }\n\n      const child = React.Children.only(\n        children\n      ) as HoverCardTriggerChildElement;\n      const childRef = getElementRef<HTMLElement>(child);\n\n      return React.cloneElement(child, {\n        ...props,\n        \"aria-controls\": contentId,\n        \"aria-expanded\": open,\n        \"aria-haspopup\": \"dialog\",\n        className: cn(triggerClassName, child.props.className),\n        \"data-state\": open ? \"open\" : \"closed\",\n        id: triggerId,\n        onBlur: (event: React.FocusEvent<HTMLElement>) => {\n          child.props.onBlur?.(event);\n          handleBlurEvent(event);\n        },\n        onFocus: (event: React.FocusEvent<HTMLElement>) => {\n          child.props.onFocus?.(event);\n          handleFocusEvent(event);\n        },\n        onPointerEnter: (event: React.PointerEvent<HTMLElement>) => {\n          child.props.onPointerEnter?.(event);\n          handlePointerEnterEvent(event);\n        },\n        onPointerLeave: (event: React.PointerEvent<HTMLElement>) => {\n          child.props.onPointerLeave?.(event);\n          handlePointerLeaveEvent(event);\n        },\n        ref: composeRefs<HTMLElement>(\n          childRef,\n          (node) => setTriggerNode(node),\n          ref as React.Ref<HTMLElement>\n        ),\n      } as HoverCardTriggerElementProps & React.RefAttributes<HTMLElement>);\n    }\n\n    return (\n      <button\n        aria-controls={contentId}\n        aria-expanded={open}\n        aria-haspopup=\"dialog\"\n        className={triggerClassName}\n        data-state={open ? \"open\" : \"closed\"}\n        id={triggerId}\n        onBlur={handleBlurEvent}\n        onFocus={handleFocusEvent}\n        onPointerEnter={handlePointerEnterEvent}\n        onPointerLeave={handlePointerLeaveEvent}\n        ref={(node) => {\n          setTriggerNode(node);\n          assignRef(ref, node);\n        }}\n        type={type ?? \"button\"}\n        {...props}\n      >\n        {children}\n      </button>\n    );\n  }\n);\nHoverCardTrigger.displayName = \"HoverCardTrigger\";\n\n// ---------------------------------------------------------------------------\n// HoverCardContent types\n// ---------------------------------------------------------------------------\n\ntype HoverCardContentProps = Omit<\n  React.ComponentPropsWithoutRef<typeof motion.div>,\n  \"initial\" | \"animate\" | \"exit\" | \"transition\"\n> & {\n  align?: Align;\n  alignOffset?: number;\n  avoidCollisions?: boolean;\n  collisionPadding?: CollisionPadding;\n  side?: Side;\n  sideOffset?: number;\n};\n\n// ---------------------------------------------------------------------------\n// HoverCardContentBody — the real rendered panel (used inside presence guard)\n// ---------------------------------------------------------------------------\n\nconst HoverCardContentBody = React.forwardRef<\n  HTMLDivElement,\n  HoverCardContentProps\n>(\n  (\n    {\n      className,\n      children,\n      onAnimationComplete,\n      style,\n      onBlur,\n      onFocus,\n      onPointerEnter,\n      onPointerLeave,\n      align = \"center\",\n      alignOffset = 0,\n      avoidCollisions = true,\n      collisionPadding = 12,\n      side = \"bottom\",\n      sideOffset = 12,\n      ...props\n    },\n    ref\n  ) => {\n    const {\n      actionsRef,\n      contentId,\n      triggerId,\n      open,\n      getTriggerNode,\n      setContentNode,\n      handleFocusEnd,\n      handleFocusStart,\n      handleHoverEnd,\n      handleHoverStart,\n    } = useHoverCard();\n\n    return (\n      <PopoverPrimitive.Portal>\n        <PopoverPrimitive.Positioner\n          align={align}\n          alignOffset={alignOffset}\n          anchor={getTriggerNode()}\n          className=\"z-50 outline-none\"\n          collisionAvoidance={\n            avoidCollisions\n              ? undefined\n              : { side: \"none\", align: \"none\", fallbackAxisSide: \"none\" }\n          }\n          collisionPadding={collisionPadding}\n          side={side}\n          sideOffset={sideOffset}\n        >\n          <PopoverPrimitive.Popup\n            finalFocus={false}\n            initialFocus={false}\n            render={(popupProps, popupState) => {\n              const {\n                popupClassName,\n                popupRef,\n                popupStyle,\n                resolvedPopupProps,\n              } = resolvePopupProps(popupProps);\n\n              // Use the side that Base UI's collision-detection resolved (may\n              // differ from the prop when the card flips due to viewport edges).\n              const resolvedSide = (popupState.side as Side) ?? side;\n              const cardMotion = getHoverCardMotion(resolvedSide);\n\n              // Only play the enter animation on the very first mount of this\n              // popup instance. If defaultOpen was true the content is already\n              // visible — skip the animation entirely by passing false.\n              const isFirstMount = popupState.transitionStatus === \"starting\";\n\n              return (\n                <div\n                  {...resolvedPopupProps}\n                  // role=\"presentation\" is intentionally omitted so screen\n                  // readers can reach the dialog role on the inner panel.\n                  ref={(node: HTMLDivElement | null) => {\n                    setRef(popupRef, node);\n                  }}\n                  style={\n                    {\n                      \"--hover-card-bridge-size\": `${sideOffset}px`,\n                      transformOrigin: \"var(--transform-origin)\",\n                      ...popupStyle,\n                    } as React.CSSProperties\n                  }\n                >\n                  <motion.div\n                    {...props}\n                    animate={open ? cardMotion.animate : cardMotion.closed}\n                    aria-labelledby={triggerId}\n                    className={cn(\n                      hoverCardThemeClassName,\n                      hoverCardPanelClassName,\n                      hoverBridgeStyles[resolvedSide],\n                      \"transform-gpu\",\n                      popupClassName,\n                      className\n                    )}\n                    data-side={resolvedSide}\n                    data-state={open ? \"open\" : \"closed\"}\n                    id={contentId}\n                    initial={isFirstMount ? cardMotion.initial : false}\n                    onAnimationComplete={(definition) => {\n                      onAnimationComplete?.(definition);\n\n                      if (!open) {\n                        // Tell Base UI to unmount the portal after exit is done.\n                        actionsRef.current?.unmount();\n                      }\n                    }}\n                    onBlur={(event) => {\n                      onBlur?.(event);\n                      if (event.defaultPrevented) return;\n                      handleFocusEnd(event);\n                    }}\n                    onFocus={(event) => {\n                      onFocus?.(event);\n                      if (event.defaultPrevented) return;\n                      handleFocusStart();\n                    }}\n                    onPointerEnter={(event) => {\n                      onPointerEnter?.(event);\n                      if (event.defaultPrevented) return;\n                      handleHoverStart(event);\n                    }}\n                    onPointerLeave={(event) => {\n                      onPointerLeave?.(event);\n                      if (event.defaultPrevented) return;\n                      handleHoverEnd(event);\n                    }}\n                    ref={(node: HTMLDivElement | null) => {\n                      // Assign both the shared content node (for focus tracking)\n                      // and the forwarded consumer ref.\n                      setContentNode(node);\n                      assignRef(ref, node);\n                    }}\n                    role=\"dialog\"\n                    style={{\n                      // Disable pointer events while the exit animation plays so\n                      // the cursor can't re-trigger a hover while the card fades.\n                      pointerEvents: open ? undefined : \"none\",\n                      transformOrigin: \"var(--transform-origin)\",\n                      ...style,\n                    }}\n                    transition={\n                      open\n                        ? cardMotion.openTransition\n                        : cardMotion.closedTransition\n                    }\n                  >\n                    {children}\n                  </motion.div>\n                </div>\n              );\n            }}\n          />\n        </PopoverPrimitive.Positioner>\n      </PopoverPrimitive.Portal>\n    );\n  }\n);\nHoverCardContentBody.displayName = \"HoverCardContentBody\";\n\n// ---------------------------------------------------------------------------\n// HoverCardContent — presence guard so Base UI keeps the popup in the DOM\n// long enough for the exit animation to finish.\n// ---------------------------------------------------------------------------\n\nconst HoverCardContent = React.forwardRef<\n  HTMLDivElement,\n  HoverCardContentProps\n>((props, ref) => {\n  const { open } = useHoverCard();\n  const [present, setPresent] = React.useState(open);\n\n  React.useEffect(() => {\n    if (open) {\n      setPresent(true);\n    }\n  }, [open]);\n\n  if (!present) {\n    return null;\n  }\n\n  return (\n    <HoverCardContentBody\n      {...props}\n      onAnimationComplete={(definition) => {\n        props.onAnimationComplete?.(definition);\n\n        if (!open) {\n          setPresent(false);\n        }\n      }}\n      ref={ref}\n    />\n  );\n});\nHoverCardContent.displayName = \"HoverCardContent\";\n\nexport { HoverCard, HoverCardTrigger, HoverCardContent };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Hover Card (Base UI)",
  "description": "Hover card with the same Iconiq API layered over Base UI popover primitives, preserving delayed hover reveal, instant focus open, collision-aware placement, and spring-driven content motion."
}
