{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "dependencies": [],
  "description": "Base-UI-shaped interactive preview on native popover + CSS anchors. Mouse-rest hover intent uses a safe geometric bridge, focus opens without delay, and press pins the non-modal dialog for keyboard/touch access. Controlled changes are cancellable; multiple triggers, nested Escape isolation, focus return, lazy presence, collision fallback, component-local RTL logical sides, and exit completion are first-class.",
  "files": [
    {
      "content": "\"use client\";\n\n/**\n * heidi-ui HoverCard — an interactive preview on the native popover top layer.\n *\n * This is the dedicated convenience primitive for Base UI's July 2026\n * Popover.Trigger `openOnHover` interaction model: hover intent uses a safe\n * pointer bridge, focus opens immediately, and an optional press pins the card\n * for keyboard/touch users. Hover never steals focus. Press-open cards use\n * non-modal dialog semantics and restore the invoker when appropriate.\n *\n * Heidi keeps the implementation lean: CSS anchor positioning and\n * [popover=manual] replace a portal/floating-positioning runtime. JavaScript is\n * limited to interaction arbitration, controlled state, focus, and presence.\n *\n * RSC rule: named exports are canonical from Server Components. HoverCard.X is\n * client-component sugar.\n */\n\nimport {\n  type ComponentPropsWithoutRef,\n  type CSSProperties,\n  createContext,\n  type FocusEvent,\n  type KeyboardEvent,\n  type MouseEvent,\n  type PointerEvent,\n  type ReactNode,\n  type Ref,\n  type RefObject,\n  type SyntheticEvent,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useImperativeHandle,\n  useMemo,\n  useRef,\n  useState\n} from \"react\";\nimport { waitForElementAnimations } from \"../_internal/animation-wait\";\nimport { ariaDisabledAttrs, nativeDisabledAttrs } from \"../_internal/disabled\";\nimport { addToOpenStack, isTopmost } from \"../_internal/escape-layer\";\nimport { getHeidiTabbableElements } from \"../_internal/focus-scope\";\nimport {\n  composeHeidiEventHandlers,\n  mergeHeidiRefs,\n  renderHeidiElement,\n  type HeidiIntrinsicHostProps\n} from \"../_internal/render-element\";\nimport {\n  pointInTriangle,\n  type SafeTrianglePoint\n} from \"../_internal/safe-triangle\";\nimport { safeId } from \"../_internal/safe-id\";\nimport { useHeidiLayoutEffect } from \"../_internal/use-layout-effect\";\nimport {\n  type HoverCardAlign,\n  type HoverCardSide\n} from \"./hover-card.anatomy.generated\";\nimport { HOVER_CARD_CLASSES } from \"./hover-card.classes.generated\";\n\nexport type { HoverCardAlign, HoverCardSide };\n\ntype NativeHostProps<Tag extends keyof HTMLElementTagNameMap> = Omit<\n  ComponentPropsWithoutRef<Tag>,\n  \"children\" | \"className\" | \"ref\" | \"style\"\n>;\n\nexport type HoverCardInteractionType =\n  | \"\"\n  | \"keyboard\"\n  | \"mouse\"\n  | \"pen\"\n  | \"touch\";\n\nexport type HoverCardTransitionStatus =\n  | \"ending\"\n  | \"idle\"\n  | \"starting\"\n  | undefined;\n\nexport type HoverCardRootChangeEventReason =\n  | \"close-press\"\n  | \"escape-key\"\n  | \"focus-out\"\n  | \"imperative-action\"\n  | \"none\"\n  | \"outside-press\"\n  | \"trigger-focus\"\n  | \"trigger-hover\"\n  | \"trigger-press\";\n\ntype HoverCardOpenReason =\n  | \"none\"\n  | \"trigger-focus\"\n  | \"trigger-hover\"\n  | \"trigger-press\";\n\nexport type HoverCardRootChangeEventDetails = {\n  /** Opts a nested card's Escape request into its parent. */\n  allowPropagation: () => void;\n  /** Cancels Heidi's requested state change. */\n  cancel: () => void;\n  /** Native event which requested the state change. */\n  event: Event;\n  readonly isCanceled: boolean;\n  readonly isPropagationAllowed: boolean;\n  readonly isUnmountPrevented: boolean;\n  /** Retains closed content after its exit motion has completed. */\n  preventUnmountOnClose: () => void;\n  reason: HoverCardRootChangeEventReason;\n  trigger: Element | undefined;\n};\n\nexport type HoverCardRootActions = {\n  close: () => void;\n  unmount: () => void;\n};\n\nexport type HoverCardFocusTarget =\n  | boolean\n  | RefObject<HTMLElement | null>\n  | ((\n      interactionType: HoverCardInteractionType\n    ) => boolean | HTMLElement | null | undefined | void);\n\ntype FocusDirective =\n  | { kind: \"default\" }\n  | { kind: \"skip\" }\n  | { kind: \"target\"; target: HTMLElement };\n\nfunction createChangeEventDetails(\n  reason: HoverCardRootChangeEventReason,\n  event: Event,\n  trigger?: Element\n): HoverCardRootChangeEventDetails {\n  let canceled = false;\n  let propagationAllowed = false;\n  let unmountPrevented = false;\n  return {\n    allowPropagation: () => {\n      propagationAllowed = true;\n    },\n    cancel: () => {\n      canceled = true;\n    },\n    event,\n    get isCanceled() {\n      return canceled;\n    },\n    get isPropagationAllowed() {\n      return propagationAllowed;\n    },\n    get isUnmountPrevented() {\n      return unmountPrevented;\n    },\n    preventUnmountOnClose: () => {\n      unmountPrevented = true;\n    },\n    reason,\n    trigger\n  };\n}\n\nfunction interactionTypeFromEvent(event: Event): HoverCardInteractionType {\n  if (\"pointerType\" in event && typeof event.pointerType === \"string\") {\n    if (event.pointerType === \"touch\" || event.pointerType === \"pen\") {\n      return event.pointerType;\n    }\n    return \"mouse\";\n  }\n  if (event.type.startsWith(\"key\") || event.type.startsWith(\"focus\")) {\n    return \"keyboard\";\n  }\n  if (event.type.startsWith(\"touch\")) {\n    return \"touch\";\n  }\n  if (\"detail\" in event && typeof event.detail === \"number\") {\n    return event.detail === 0 ? \"keyboard\" : \"mouse\";\n  }\n  return \"\";\n}\n\nfunction resolveFocusDirective(\n  value: HoverCardFocusTarget | undefined,\n  interactionType: HoverCardInteractionType\n): FocusDirective {\n  if (value === undefined || value === true) {\n    return { kind: \"default\" };\n  }\n  if (value === false) {\n    return { kind: \"skip\" };\n  }\n  if (typeof value === \"function\") {\n    const result = value(interactionType);\n    if (result === null || result === true) {\n      return { kind: \"default\" };\n    }\n    if (result === undefined || result === false) {\n      return { kind: \"skip\" };\n    }\n    return { kind: \"target\", target: result };\n  }\n  return value.current\n    ? { kind: \"target\", target: value.current }\n    : { kind: \"default\" };\n}\n\nfunction anchorNameForTrigger(triggerId: string): string {\n  const encoded = Array.from(triggerId, (character) =>\n    character.codePointAt(0)?.toString(16)\n  ).join(\"-\");\n  return `--hui-hover-card-anchor-${encoded || \"0\"}`;\n}\n\nfunction openStateAttributes(open: boolean): Record<string, true | undefined> {\n  return open\n    ? { \"data-closed\": undefined, \"data-open\": true }\n    : { \"data-closed\": true, \"data-open\": undefined };\n}\n\nfunction transitionAttributes(\n  status: HoverCardTransitionStatus\n): Record<string, true | undefined> {\n  return {\n    \"data-ending-style\": status === \"ending\" ? true : undefined,\n    \"data-starting-style\": status === \"starting\" ? true : undefined\n  };\n}\n\n\n\n\n\n\n\ntype Point = SafeTrianglePoint;\n\nfunction pointInRect(point: Point, rect: DOMRect, padding = 0): boolean {\n  return (\n    point.x >= rect.left - padding &&\n    point.x <= rect.right + padding &&\n    point.y >= rect.top - padding &&\n    point.y <= rect.bottom + padding\n  );\n}\n\n/**\n * Small intent corridor from the pointer's leave point to the nearest popup\n * edge. It is recomputed from live rects, so CSS-anchor collision flips remain\n * correct without a positioning observer.\n */\nfunction pointInSafeBridge(\n  point: Point,\n  origin: Point,\n  fromRect: DOMRect,\n  toRect: DOMRect\n): boolean {\n  if (pointInRect(point, fromRect, 1) || pointInRect(point, toRect, 3)) {\n    return true;\n  }\n  const fromCenter = {\n    x: fromRect.left + fromRect.width / 2,\n    y: fromRect.top + fromRect.height / 2\n  };\n  const toCenter = {\n    x: toRect.left + toRect.width / 2,\n    y: toRect.top + toRect.height / 2\n  };\n  const horizontal = Math.abs(toCenter.x - fromCenter.x) > Math.abs(toCenter.y - fromCenter.y);\n  const padding = 5;\n  const originPadding = 2;\n\n  if (horizontal) {\n    const edgeX = toCenter.x >= fromCenter.x ? toRect.left - padding : toRect.right + padding;\n    return (\n      pointInTriangle(\n        point,\n        { x: origin.x, y: origin.y - originPadding },\n        { x: edgeX, y: toRect.top - padding },\n        { x: edgeX, y: toRect.bottom + padding }\n      ) ||\n      pointInTriangle(\n        point,\n        { x: origin.x, y: origin.y + originPadding },\n        { x: edgeX, y: toRect.top - padding },\n        { x: edgeX, y: toRect.bottom + padding }\n      )\n    );\n  }\n\n  const edgeY = toCenter.y >= fromCenter.y ? toRect.top - padding : toRect.bottom + padding;\n  return (\n    pointInTriangle(\n      point,\n      { x: origin.x - originPadding, y: origin.y },\n      { x: toRect.left - padding, y: edgeY },\n      { x: toRect.right + padding, y: edgeY }\n    ) ||\n    pointInTriangle(\n      point,\n      { x: origin.x + originPadding, y: origin.y },\n      { x: toRect.left - padding, y: edgeY },\n      { x: toRect.right + padding, y: edgeY }\n    )\n  );\n}\n\nfunction addRegisteredId(\n  setter: (update: (ids: string[]) => string[]) => void,\n  id: string\n): () => void {\n  setter((ids) => (ids.includes(id) ? ids : [...ids, id]));\n  return () => setter((ids) => ids.filter((candidate) => candidate !== id));\n}\n\ntype HoverCardContextValue = {\n  activeTriggerId: string | null;\n  cancelPendingClose: () => void;\n  completeOpenChange: (open: boolean) => void;\n  contentId: string;\n  contentRef: { current: HTMLDivElement | null };\n  descriptionIds: string[];\n  finishClose: () => void;\n  focusTrigger: (id: string, element: HTMLElement, event: Event) => void;\n  getActiveTrigger: () => HTMLElement | null;\n  getInteractionType: () => HoverCardInteractionType;\n  getOpen: () => boolean;\n  getOpenReason: () => HoverCardOpenReason;\n  open: boolean;\n  openReason: HoverCardOpenReason;\n  pointerDownInside: (event: Event) => void;\n  pointerEnterZone: (element: Element) => void;\n  pointerLeaveZone: (element: Element, event: globalThis.PointerEvent) => void;\n  pressTrigger: (\n    id: string,\n    element: HTMLElement,\n    event: Event,\n    interactionType: HoverCardInteractionType\n  ) => void;\n  registerDescription: (id: string) => () => void;\n  registerNestedOpen: () => () => void;\n  registerUnmount: (callback: () => void) => () => void;\n  registerTitle: (id: string) => () => void;\n  registerTrigger: (id: string, element: HTMLElement) => () => void;\n  requestOpen: (\n    open: boolean,\n    reason: HoverCardRootChangeEventReason,\n    event: Event,\n    trigger?: Element,\n    triggerId?: string,\n    interactionType?: HoverCardInteractionType\n  ) => HoverCardRootChangeEventDetails | null;\n  restoreFocus: (\n    popup: HTMLDivElement,\n    finalFocus: HoverCardFocusTarget | undefined\n  ) => void;\n  scheduleFocusClose: (event: Event) => void;\n  scheduleHoverOpen: (\n    id: string,\n    element: HTMLElement,\n    event: globalThis.PointerEvent,\n    restart: boolean\n  ) => void;\n  shouldRetainClosedContent: () => boolean;\n  titleIds: string[];\n};\n\nconst HoverCardContext = createContext<HoverCardContextValue | null>(null);\n\nfunction useHoverCardContext(part: string): HoverCardContextValue {\n  const context = useContext(HoverCardContext);\n  if (!context) {\n    throw new Error(`HoverCard.${part} must be rendered inside HoverCard.Root.`);\n  }\n  return context;\n}\n\nexport type HoverCardRootProps = {\n  actionsRef?: RefObject<HoverCardRootActions | null>;\n  children?: ReactNode;\n  /** Delay after leaving the safe hover corridor. Base-compatible default: 0 ms. */\n  closeDelay?: number;\n  defaultOpen?: boolean;\n  defaultTriggerId?: string | null;\n  onOpenChange?: (\n    open: boolean,\n    details: HoverCardRootChangeEventDetails\n  ) => void;\n  onOpenChangeComplete?: (open: boolean) => void;\n  open?: boolean;\n  /** Mouse-rest delay before opening. Base-compatible default: 300 ms. */\n  openDelay?: number;\n  triggerId?: string | null;\n};\n\nexport function HoverCardRoot({\n  actionsRef,\n  children,\n  closeDelay = 0,\n  defaultOpen = false,\n  defaultTriggerId = null,\n  onOpenChange,\n  onOpenChangeComplete,\n  open: openProp,\n  openDelay = 300,\n  triggerId: triggerIdProp\n}: HoverCardRootProps) {\n  const parentContext = useContext(HoverCardContext);\n  const generatedId = safeId(useId());\n  const instanceId = `hui-hover-card-root-${generatedId}`;\n  const [descriptionIds, setDescriptionIds] = useState<string[]>([]);\n  const [internalOpen, setInternalOpen] = useState(defaultOpen);\n  const [internalTriggerId, setInternalTriggerId] = useState<string | null>(\n    defaultTriggerId\n  );\n  const [openReason, setOpenReasonState] =\n    useState<HoverCardOpenReason>(\"none\");\n  const [titleIds, setTitleIds] = useState<string[]>([]);\n  const controlled = openProp !== undefined;\n  const triggerControlled = triggerIdProp !== undefined;\n  const open = openProp ?? internalOpen;\n  const activeTriggerId = triggerIdProp ?? internalTriggerId;\n\n  const openRef = useRef(open);\n  openRef.current = open;\n  const activeTriggerIdRef = useRef(activeTriggerId);\n  activeTriggerIdRef.current = activeTriggerId;\n  const openReasonRef = useRef(openReason);\n  openReasonRef.current = openReason;\n  const contentRef = useRef<HTMLDivElement | null>(null);\n  const triggersRef = useRef(new Map<string, HTMLElement>());\n  const hoveredElementsRef = useRef(new Set<Element>());\n  const openTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const bridgeCleanupRef = useRef<(() => void) | null>(null);\n  const focusCloseTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  /**\n   * ponytail: WCAG 2.2 SC 1.4.13 Dismissible requires a mechanism that\n   * dismisses the card \"without moving pointer hover\" — so the dismissal has\n   * to SURVIVE the pointer staying put. Without this flag, Escape closed the\n   * card and the very next mousemove over the still-hovered trigger called\n   * scheduleHoverOpen(restart: true) and reopened it, which means no\n   * dismissal mechanism existed at all for a mouse user.\n   *\n   * Set on any explicit dismissal, cleared only when the pointer genuinely\n   * leaves the zone (or the card is deliberately reopened). The rejected\n   * alternative was suppressing on a timer: a timeout either reopens under a\n   * motionless pointer (still a 1.4.13 failure) or blocks a legitimate\n   * re-hover after the user has moved away — the pointer's own position is\n   * the correct signal, and it is what Floating UI's useHover/useDismiss\n   * pairing uses too.\n   */\n  const hoverSuppressedRef = useRef(false);\n  const pointerSelectingRef = useRef(false);\n  const nestedOpenCountRef = useRef(0);\n  const interactionTypeRef = useRef<HoverCardInteractionType>(\"\");\n  const returnFocusRef = useRef<HTMLElement | null>(null);\n  const restoringFocusRef = useRef(false);\n  const preventUnmountRef = useRef(false);\n  const completedStateRef = useRef(open);\n  const pendingRequestRef = useRef<boolean | null>(null);\n  const unmountCallbacksRef = useRef(new Set<() => void>());\n\n  const setOpenReason = useCallback((reason: HoverCardOpenReason) => {\n    openReasonRef.current = reason;\n    setOpenReasonState(reason);\n  }, []);\n\n  const clearOpenTimer = useCallback(() => {\n    if (openTimerRef.current !== null) {\n      clearTimeout(openTimerRef.current);\n      openTimerRef.current = null;\n    }\n  }, []);\n\n  const clearCloseTimer = useCallback(() => {\n    if (closeTimerRef.current !== null) {\n      clearTimeout(closeTimerRef.current);\n      closeTimerRef.current = null;\n    }\n  }, []);\n\n  const clearFocusCloseTimer = useCallback(() => {\n    if (focusCloseTimerRef.current !== null) {\n      clearTimeout(focusCloseTimerRef.current);\n      focusCloseTimerRef.current = null;\n    }\n  }, []);\n\n  const clearBridge = useCallback(() => {\n    bridgeCleanupRef.current?.();\n    bridgeCleanupRef.current = null;\n  }, []);\n\n  const cancelPendingClose = useCallback(() => {\n    clearCloseTimer();\n    clearFocusCloseTimer();\n    clearBridge();\n  }, [clearBridge, clearCloseTimer, clearFocusCloseTimer]);\n\n  const getOpen = useCallback(() => openRef.current, []);\n  const getOpenReason = useCallback(() => openReasonRef.current, []);\n  const getInteractionType = useCallback(\n    () => interactionTypeRef.current,\n    []\n  );\n  const getActiveTrigger = useCallback(() => {\n    const id = activeTriggerIdRef.current;\n    return id ? (triggersRef.current.get(id) ?? null) : null;\n  }, []);\n\n  const isWithinZone = useCallback((target: EventTarget | null): boolean => {\n    if (!(target instanceof Node)) {\n      return false;\n    }\n    const content = contentRef.current;\n    if (content && (target === content || content.contains(target))) {\n      return true;\n    }\n    for (const trigger of triggersRef.current.values()) {\n      if (target === trigger || trigger.contains(target)) {\n        return true;\n      }\n    }\n    return false;\n  }, []);\n\n  const hasFocusWithinZone = useCallback(\n    () => isWithinZone(document.activeElement),\n    [isWithinZone]\n  );\n\n  const activateTrigger = useCallback(\n    (\n      id: string,\n      element: HTMLElement,\n      event: Event,\n      reason: \"trigger-focus\" | \"trigger-hover\" | \"trigger-press\"\n    ): boolean => {\n      if (activeTriggerIdRef.current === id) {\n        return true;\n      }\n      if (triggerControlled) {\n        const details = createChangeEventDetails(reason, event, element);\n        onOpenChange?.(true, details);\n        if (details.isCanceled) {\n          return false;\n        }\n      } else {\n        activeTriggerIdRef.current = id;\n        setInternalTriggerId(id);\n      }\n      return true;\n    },\n    [onOpenChange, triggerControlled]\n  );\n\n  const requestOpen = useCallback(\n    (\n      next: boolean,\n      reason: HoverCardRootChangeEventReason,\n      event: Event,\n      trigger?: Element,\n      triggerId?: string,\n      interactionType = interactionTypeFromEvent(event)\n    ) => {\n      if (next === openRef.current || pendingRequestRef.current === next) {\n        return null;\n      }\n      const details = createChangeEventDetails(reason, event, trigger);\n      onOpenChange?.(next, details);\n      if (details.isCanceled) {\n        return details;\n      }\n      if (\n        next &&\n        triggerId &&\n        trigger instanceof HTMLElement &&\n        activeTriggerIdRef.current !== triggerId\n      ) {\n        if (!triggerControlled) {\n          activeTriggerIdRef.current = triggerId;\n          setInternalTriggerId(triggerId);\n        }\n      }\n      interactionTypeRef.current = interactionType;\n      if (next) {\n        hoverSuppressedRef.current = false;\n        preventUnmountRef.current = false;\n        returnFocusRef.current =\n          trigger instanceof HTMLElement\n            ? trigger\n            : getActiveTrigger() ??\n              (document.activeElement instanceof HTMLElement\n                ? document.activeElement\n                : null);\n        if (\n          reason === \"trigger-hover\" ||\n          reason === \"trigger-focus\" ||\n          reason === \"trigger-press\"\n        ) {\n          setOpenReason(reason);\n        } else {\n          setOpenReason(\"none\");\n        }\n      } else {\n        clearOpenTimer();\n        cancelPendingClose();\n        if (\n          reason === \"close-press\" ||\n          reason === \"escape-key\" ||\n          reason === \"outside-press\"\n        ) {\n          hoverSuppressedRef.current = true;\n        }\n        if (details.isUnmountPrevented) {\n          preventUnmountRef.current = true;\n        }\n      }\n      pendingRequestRef.current = next;\n      queueMicrotask(() => {\n        if (pendingRequestRef.current === next) {\n          pendingRequestRef.current = null;\n        }\n      });\n      if (!controlled) {\n        openRef.current = next;\n        setInternalOpen(next);\n      }\n      return details;\n    },\n    [\n      cancelPendingClose,\n      clearOpenTimer,\n      controlled,\n      getActiveTrigger,\n      onOpenChange,\n      setOpenReason,\n      triggerControlled\n    ]\n  );\n\n  const completeOpenChange = useCallback(\n    (next: boolean) => {\n      if (completedStateRef.current === next) {\n        return;\n      }\n      completedStateRef.current = next;\n      onOpenChangeComplete?.(next);\n    },\n    [onOpenChangeComplete]\n  );\n\n  const finishClose = useCallback(() => {\n    setOpenReason(\"none\");\n    interactionTypeRef.current = \"\";\n    pointerSelectingRef.current = false;\n  }, [setOpenReason]);\n\n  const shouldRetainClosedContent = useCallback(() => {\n    const retain = preventUnmountRef.current;\n    preventUnmountRef.current = false;\n    return retain;\n  }, []);\n\n  const forceUnmount = useCallback(() => {\n    for (const callback of unmountCallbacksRef.current) {\n      callback();\n    }\n    preventUnmountRef.current = false;\n  }, []);\n\n  useImperativeHandle(\n    actionsRef,\n    () => ({\n      close: () => {\n        requestOpen(\n          false,\n          \"imperative-action\",\n          new Event(\"heidi-hover-card-imperative-close\")\n        );\n      },\n      unmount: forceUnmount\n    }),\n    [forceUnmount, requestOpen]\n  );\n\n  const scheduleClose = useCallback(\n    (event: Event, reason: \"focus-out\" | \"trigger-hover\") => {\n      clearCloseTimer();\n      if (\n        !openRef.current ||\n        nestedOpenCountRef.current > 0 ||\n        pointerSelectingRef.current ||\n        (reason === \"trigger-hover\" && hasFocusWithinZone())\n      ) {\n        return;\n      }\n      if (\n        reason === \"trigger-hover\" &&\n        openReasonRef.current !== \"trigger-hover\"\n      ) {\n        return;\n      }\n      if (\n        reason === \"focus-out\" &&\n        openReasonRef.current !== \"trigger-focus\" &&\n        openReasonRef.current !== \"trigger-press\"\n      ) {\n        return;\n      }\n      const close = () => {\n        closeTimerRef.current = null;\n        if (\n          nestedOpenCountRef.current === 0 &&\n          !pointerSelectingRef.current &&\n          (reason !== \"focus-out\" || !hasFocusWithinZone())\n        ) {\n          requestOpen(false, reason, event);\n        }\n      };\n      if (!Number.isFinite(closeDelay) || closeDelay <= 0) {\n        close();\n      } else {\n        closeTimerRef.current = setTimeout(close, closeDelay);\n      }\n    },\n    [clearCloseTimer, closeDelay, hasFocusWithinZone, requestOpen]\n  );\n\n  const pointerEnterZone = useCallback(\n    (element: Element) => {\n      // Entering the zone is the pointer arriving from OUTSIDE it (movement\n      // within one element fires pointermove, not pointerenter), so this is\n      // fresh hover intent and ends any dismissal suppression. Without it an\n      // outside-press dismissal — which happens with the pointer already\n      // outside the zone — would deaden hover until an extra enter/leave lap.\n      hoverSuppressedRef.current = false;\n      hoveredElementsRef.current.add(element);\n      cancelPendingClose();\n    },\n    [cancelPendingClose]\n  );\n\n  const pointerLeaveZone = useCallback(\n    (element: Element, event: globalThis.PointerEvent) => {\n      hoveredElementsRef.current.delete(element);\n      clearOpenTimer();\n      // The pointer really left the trigger/content zone, so a later re-entry\n      // is a fresh hover intent and may reopen. Cleared before the early\n      // returns below on purpose: those guard the CLOSE scheduling, not the\n      // suppression lifetime.\n      if (\n        hoveredElementsRef.current.size === 0 &&\n        !isWithinZone(event.relatedTarget)\n      ) {\n        hoverSuppressedRef.current = false;\n      }\n      if (\n        event.pointerType !== \"mouse\" ||\n        hoveredElementsRef.current.size > 0 ||\n        isWithinZone(event.relatedTarget) ||\n        !openRef.current ||\n        openReasonRef.current !== \"trigger-hover\" ||\n        nestedOpenCountRef.current > 0 ||\n        pointerSelectingRef.current ||\n        hasFocusWithinZone()\n      ) {\n        return;\n      }\n\n      clearBridge();\n      const content = contentRef.current;\n      const activeTrigger = getActiveTrigger();\n      const destination = element === content ? activeTrigger : content;\n      if (!destination) {\n        scheduleClose(event, \"trigger-hover\");\n        return;\n      }\n      const origin = { x: event.clientX, y: event.clientY };\n      const fromRect = element.getBoundingClientRect();\n      const move = (moveEvent: globalThis.PointerEvent) => {\n        if (moveEvent.pointerType !== \"mouse\") {\n          return;\n        }\n        if (isWithinZone(moveEvent.target)) {\n          clearBridge();\n          return;\n        }\n        const toRect = destination.getBoundingClientRect();\n        if (\n          !pointInSafeBridge(\n            { x: moveEvent.clientX, y: moveEvent.clientY },\n            origin,\n            fromRect,\n            toRect\n          )\n        ) {\n          clearBridge();\n          scheduleClose(moveEvent, \"trigger-hover\");\n        }\n      };\n      document.addEventListener(\"pointermove\", move, { passive: true });\n      bridgeCleanupRef.current = () => {\n        document.removeEventListener(\"pointermove\", move);\n      };\n    },\n    [\n      clearBridge,\n      clearOpenTimer,\n      getActiveTrigger,\n      hasFocusWithinZone,\n      isWithinZone,\n      scheduleClose\n    ]\n  );\n\n  const scheduleHoverOpen = useCallback(\n    (\n      id: string,\n      element: HTMLElement,\n      event: globalThis.PointerEvent,\n      restart: boolean\n    ) => {\n      if (event.pointerType !== \"mouse\") {\n        return;\n      }\n      cancelPendingClose();\n      if (openRef.current) {\n        if (openReasonRef.current === \"trigger-press\") {\n          return;\n        }\n        if (openReasonRef.current === \"none\") {\n          if (activeTriggerIdRef.current === null && !triggerControlled) {\n            activeTriggerIdRef.current = id;\n            setInternalTriggerId(id);\n          }\n          return;\n        }\n        activateTrigger(id, element, event, \"trigger-hover\");\n        if (openReasonRef.current === \"trigger-focus\" && !hasFocusWithinZone()) {\n          setOpenReason(\"trigger-hover\");\n        }\n        return;\n      }\n      // Dismissed while the pointer never left: hover must not resurrect it.\n      if (hoverSuppressedRef.current) {\n        return;\n      }\n      if (restart || openTimerRef.current === null) {\n        clearOpenTimer();\n        const openCard = () => {\n          openTimerRef.current = null;\n          requestOpen(\n            true,\n            \"trigger-hover\",\n            event,\n            element,\n            id,\n            \"mouse\"\n          );\n        };\n        if (!Number.isFinite(openDelay) || openDelay <= 0) {\n          openCard();\n        } else {\n          openTimerRef.current = setTimeout(openCard, openDelay);\n        }\n      }\n    },\n    [\n      activateTrigger,\n      cancelPendingClose,\n      clearOpenTimer,\n      hasFocusWithinZone,\n      openDelay,\n      requestOpen,\n      setOpenReason,\n      triggerControlled\n    ]\n  );\n\n  const focusTrigger = useCallback(\n    (id: string, element: HTMLElement, event: Event) => {\n      if (restoringFocusRef.current) {\n        return;\n      }\n      clearOpenTimer();\n      cancelPendingClose();\n      if (openRef.current) {\n        if (\n          openReasonRef.current !== \"trigger-press\" &&\n          activateTrigger(id, element, event, \"trigger-focus\")\n        ) {\n          setOpenReason(\"trigger-focus\");\n          interactionTypeRef.current = \"keyboard\";\n          returnFocusRef.current = element;\n        }\n        return;\n      }\n      requestOpen(\n        true,\n        \"trigger-focus\",\n        event,\n        element,\n        id,\n        \"keyboard\"\n      );\n    },\n    [\n      activateTrigger,\n      cancelPendingClose,\n      clearOpenTimer,\n      requestOpen,\n      setOpenReason\n    ]\n  );\n\n  const scheduleFocusClose = useCallback(\n    (event: Event) => {\n      clearFocusCloseTimer();\n      focusCloseTimerRef.current = setTimeout(() => {\n        focusCloseTimerRef.current = null;\n        if (!hasFocusWithinZone()) {\n          scheduleClose(event, \"focus-out\");\n        }\n      }, 0);\n    }, [clearFocusCloseTimer, hasFocusWithinZone, scheduleClose]\n  );\n\n  const pressTrigger = useCallback(\n    (\n      id: string,\n      element: HTMLElement,\n      event: Event,\n      interactionType: HoverCardInteractionType\n    ) => {\n      clearOpenTimer();\n      cancelPendingClose();\n      interactionTypeRef.current = interactionType;\n      returnFocusRef.current = element;\n      const active = activeTriggerIdRef.current === id;\n      if (openRef.current) {\n        if (active && openReasonRef.current === \"trigger-press\") {\n          requestOpen(\n            false,\n            \"trigger-press\",\n            event,\n            element,\n            id,\n            interactionType\n          );\n          return;\n        }\n        if (\n          openReasonRef.current === \"none\" &&\n          (active || activeTriggerIdRef.current === null)\n        ) {\n          requestOpen(\n            false,\n            \"trigger-press\",\n            event,\n            element,\n            id,\n            interactionType\n          );\n          return;\n        }\n        if (activateTrigger(id, element, event, \"trigger-press\")) {\n          setOpenReason(\"trigger-press\");\n        }\n        return;\n      }\n      requestOpen(\n        true,\n        \"trigger-press\",\n        event,\n        element,\n        id,\n        interactionType\n      );\n    },\n    [\n      activateTrigger,\n      cancelPendingClose,\n      clearOpenTimer,\n      requestOpen,\n      setOpenReason\n    ]\n  );\n\n  const pointerDownInside = useCallback(\n    (event: Event) => {\n      pointerSelectingRef.current = true;\n      cancelPendingClose();\n      void event;\n    },\n    [cancelPendingClose]\n  );\n\n  const registerTrigger = useCallback(\n    (id: string, element: HTMLElement) => {\n      triggersRef.current.set(id, element);\n      if (activeTriggerIdRef.current === null && !triggerControlled) {\n        activeTriggerIdRef.current = id;\n        setInternalTriggerId(id);\n      }\n      return () => {\n        triggersRef.current.delete(id);\n        hoveredElementsRef.current.delete(element);\n        if (activeTriggerIdRef.current === id && !triggerControlled) {\n          const next = triggersRef.current.keys().next().value as\n            | string\n            | undefined;\n          activeTriggerIdRef.current = next ?? null;\n          setInternalTriggerId(next ?? null);\n        }\n      };\n    },\n    [triggerControlled]\n  );\n\n  const registerTitle = useCallback(\n    (id: string) => addRegisteredId(setTitleIds, id),\n    []\n  );\n  const registerDescription = useCallback(\n    (id: string) => addRegisteredId(setDescriptionIds, id),\n    []\n  );\n  const registerNestedOpen = useCallback(() => {\n    nestedOpenCountRef.current += 1;\n    cancelPendingClose();\n    return () => {\n      nestedOpenCountRef.current = Math.max(0, nestedOpenCountRef.current - 1);\n    };\n  }, [cancelPendingClose]);\n  const registerUnmount = useCallback((callback: () => void) => {\n    unmountCallbacksRef.current.add(callback);\n    return () => unmountCallbacksRef.current.delete(callback);\n  }, []);\n\n  const parentRegisterNestedOpen = parentContext?.registerNestedOpen;\n  useEffect(() => {\n    if (!open || !parentRegisterNestedOpen) {\n      return;\n    }\n    return parentRegisterNestedOpen();\n  }, [open, parentRegisterNestedOpen]);\n\n  useEffect(() => {\n    if (!open) {\n      return;\n    }\n    const removeFromStack = addToOpenStack(instanceId, () => contentRef.current);\n    const onKeyDown = (event: globalThis.KeyboardEvent) => {\n      if (\n        event.defaultPrevented ||\n        event.key !== \"Escape\" ||\n        !isTopmost(instanceId)\n      ) {\n        return;\n      }\n      const details = requestOpen(\n        false,\n        \"escape-key\",\n        event,\n        getActiveTrigger() ?? undefined,\n        activeTriggerIdRef.current ?? undefined,\n        \"keyboard\"\n      );\n      if (!details?.isPropagationAllowed) {\n        event.preventDefault();\n        event.stopPropagation();\n      } else if (parentContext?.getOpen()) {\n        parentContext.requestOpen(false, \"escape-key\", event);\n      }\n    };\n    const onPointerDown = (event: globalThis.PointerEvent) => {\n      if (\n        openReasonRef.current === \"trigger-press\" &&\n        !isWithinZone(event.target)\n      ) {\n        requestOpen(false, \"outside-press\", event);\n      }\n    };\n    const onPointerRelease = (event: globalThis.PointerEvent) => {\n      pointerSelectingRef.current = false;\n      if (openReasonRef.current !== \"trigger-hover\") {\n        return;\n      }\n      const hitTarget = document.elementFromPoint(event.clientX, event.clientY);\n      if (isWithinZone(hitTarget)) {\n        return;\n      }\n      pointerSelectingRef.current = false;\n      hoveredElementsRef.current.clear();\n      if (document.activeElement === contentRef.current) {\n        contentRef.current?.blur();\n      }\n      scheduleClose(event, \"trigger-hover\");\n    };\n    const onFocusIn = (event: globalThis.FocusEvent) => {\n      if (\n        (openReasonRef.current === \"trigger-focus\" ||\n          openReasonRef.current === \"trigger-press\") &&\n        !isWithinZone(event.target)\n      ) {\n        scheduleFocusClose(event);\n      } else if (isWithinZone(event.target)) {\n        cancelPendingClose();\n      }\n    };\n    const onWindowBlur = (event: Event) => {\n      if (openReasonRef.current === \"trigger-hover\") {\n        requestOpen(false, \"focus-out\", event);\n      }\n    };\n    document.addEventListener(\"keydown\", onKeyDown);\n    document.addEventListener(\"pointerdown\", onPointerDown, true);\n    document.addEventListener(\"pointerup\", onPointerRelease, true);\n    document.addEventListener(\"pointercancel\", onPointerRelease, true);\n    document.addEventListener(\"focusin\", onFocusIn);\n    window.addEventListener(\"blur\", onWindowBlur);\n    return () => {\n      removeFromStack();\n      document.removeEventListener(\"keydown\", onKeyDown);\n      document.removeEventListener(\"pointerdown\", onPointerDown, true);\n      document.removeEventListener(\"pointerup\", onPointerRelease, true);\n      document.removeEventListener(\"pointercancel\", onPointerRelease, true);\n      document.removeEventListener(\"focusin\", onFocusIn);\n      window.removeEventListener(\"blur\", onWindowBlur);\n    };\n  }, [\n    cancelPendingClose,\n    getActiveTrigger,\n    instanceId,\n    isWithinZone,\n    open,\n    parentContext,\n    requestOpen,\n    scheduleClose,\n    scheduleFocusClose\n  ]);\n\n  useEffect(\n    () => () => {\n      clearOpenTimer();\n      cancelPendingClose();\n    },\n    [cancelPendingClose, clearOpenTimer]\n  );\n\n  const restoreFocus = useCallback(\n    (popup: HTMLDivElement, finalFocus: HoverCardFocusTarget | undefined) => {\n      if (openReasonRef.current !== \"trigger-press\") {\n        returnFocusRef.current = null;\n        return;\n      }\n      const active = document.activeElement;\n      const remembered = returnFocusRef.current;\n      const shouldRestore =\n        active == null ||\n        active === document.body ||\n        active === popup ||\n        active === remembered ||\n        popup.contains(active);\n      if (shouldRestore) {\n        const directive = resolveFocusDirective(\n          finalFocus,\n          interactionTypeRef.current\n        );\n        if (directive.kind !== \"skip\") {\n          const target =\n            directive.kind === \"target\"\n              ? directive.target\n              : remembered?.isConnected\n                ? remembered\n                : getActiveTrigger();\n          if (target) {\n            restoringFocusRef.current = true;\n            target.focus({ preventScroll: true });\n            queueMicrotask(() => {\n              restoringFocusRef.current = false;\n            });\n          }\n        }\n      }\n      returnFocusRef.current = null;\n    },\n    [getActiveTrigger]\n  );\n\n  const value = useMemo<HoverCardContextValue>(\n    () => ({\n      activeTriggerId,\n      cancelPendingClose,\n      completeOpenChange,\n      contentId: `hui-hover-card-${generatedId}`,\n      contentRef,\n      descriptionIds,\n      finishClose,\n      focusTrigger,\n      getActiveTrigger,\n      getInteractionType,\n      getOpen,\n      getOpenReason,\n      open,\n      openReason,\n      pointerDownInside,\n      pointerEnterZone,\n      pointerLeaveZone,\n      pressTrigger,\n      registerDescription,\n      registerNestedOpen,\n      registerTitle,\n      registerTrigger,\n      registerUnmount,\n      requestOpen,\n      restoreFocus,\n      scheduleFocusClose,\n      scheduleHoverOpen,\n      shouldRetainClosedContent,\n      titleIds\n    }),\n    [\n      activeTriggerId,\n      cancelPendingClose,\n      completeOpenChange,\n      descriptionIds,\n      finishClose,\n      focusTrigger,\n      generatedId,\n      getActiveTrigger,\n      getInteractionType,\n      getOpen,\n      getOpenReason,\n      open,\n      openReason,\n      pointerDownInside,\n      pointerEnterZone,\n      pointerLeaveZone,\n      pressTrigger,\n      registerDescription,\n      registerNestedOpen,\n      registerTitle,\n      registerTrigger,\n      registerUnmount,\n      requestOpen,\n      restoreFocus,\n      scheduleFocusClose,\n      scheduleHoverOpen,\n      shouldRetainClosedContent,\n      titleIds\n    ]\n  );\n\n  return (\n    <HoverCardContext value={value}>\n      {children}\n    </HoverCardContext>\n  );\n}\n\nexport type HoverCardTriggerState = {\n  disabled: boolean;\n  open: boolean;\n};\n\ntype HoverCardTriggerNativeProps = Omit<\n  NativeHostProps<\"button\">,\n  | \"aria-controls\"\n  | \"aria-expanded\"\n  | \"aria-haspopup\"\n  | \"disabled\"\n  | \"type\"\n>;\n\nexport type HoverCardTriggerProps = HeidiIntrinsicHostProps<\n  HoverCardTriggerState,\n  \"button\"\n> &\n  HoverCardTriggerNativeProps & {\n    children?: ReactNode;\n    disabled?: boolean;\n    /** Set false when render returns a non-button host such as a link. */\n    nativeButton?: boolean;\n    /** Pins/toggles the card on activation. Defaults to nativeButton. */\n    openOnPress?: boolean;\n    ref?: Ref<HTMLButtonElement>;\n  };\n\nexport function HoverCardTrigger({\n  children,\n  className,\n  disabled = false,\n  id,\n  nativeButton = true,\n  onBlur,\n  onClick,\n  onFocus,\n  onKeyDown,\n  onKeyUp,\n  onPointerDown,\n  onPointerEnter,\n  onPointerLeave,\n  onPointerMove,\n  openOnPress = nativeButton,\n  ref,\n  render,\n  style,\n  tabIndex,\n  ...nativeProps\n}: HoverCardTriggerProps) {\n  const generatedId = safeId(useId());\n  const context = useHoverCardContext(\"Trigger\");\n  const triggerId = id ?? `hui-hover-card-trigger-${generatedId}`;\n  const localRef = useRef<HTMLButtonElement | null>(null);\n  const pointerTypeRef = useRef<HoverCardInteractionType>(\"\");\n  const triggerOpen =\n    context.open && context.activeTriggerId === triggerId;\n  const state = useMemo<HoverCardTriggerState>(\n    () => ({ disabled, open: triggerOpen }),\n    [disabled, triggerOpen]\n  );\n\n  const { registerTrigger } = context;\n  useHeidiLayoutEffect(() => {\n    const element = localRef.current;\n    if (!element) {\n      return;\n    }\n    return registerTrigger(triggerId, element);\n  }, [registerTrigger, triggerId]);\n\n  const handlePointerEnter = composeHeidiEventHandlers(\n    onPointerEnter,\n    (event: PointerEvent<HTMLButtonElement>) => {\n      if (disabled || event.pointerType !== \"mouse\") {\n        return;\n      }\n      context.pointerEnterZone(event.currentTarget);\n      context.scheduleHoverOpen(\n        triggerId,\n        event.currentTarget,\n        event.nativeEvent,\n        false\n      );\n    }\n  );\n  const handlePointerMove = composeHeidiEventHandlers(\n    onPointerMove,\n    (event: PointerEvent<HTMLButtonElement>) => {\n      if (disabled || event.pointerType !== \"mouse\" || context.open) {\n        return;\n      }\n      context.scheduleHoverOpen(\n        triggerId,\n        event.currentTarget,\n        event.nativeEvent,\n        true\n      );\n    }\n  );\n  const handlePointerLeave = composeHeidiEventHandlers(\n    onPointerLeave,\n    (event: PointerEvent<HTMLButtonElement>) => {\n      context.pointerLeaveZone(event.currentTarget, event.nativeEvent);\n    }\n  );\n  const handleFocus = composeHeidiEventHandlers(\n    onFocus,\n    (event: FocusEvent<HTMLButtonElement>) => {\n      if (!disabled) {\n        context.focusTrigger(\n          triggerId,\n          event.currentTarget,\n          event.nativeEvent\n        );\n      }\n    }\n  );\n  const handleBlur = composeHeidiEventHandlers(\n    onBlur,\n    (event: FocusEvent<HTMLButtonElement>) => {\n      context.scheduleFocusClose(event.nativeEvent);\n    }\n  );\n  const handlePointerDown = composeHeidiEventHandlers(\n    onPointerDown,\n    (event: PointerEvent<HTMLButtonElement>) => {\n      pointerTypeRef.current =\n        event.pointerType === \"touch\" || event.pointerType === \"pen\"\n          ? event.pointerType\n          : \"mouse\";\n    }\n  );\n  const handleClick = composeHeidiEventHandlers(\n    onClick,\n    (event: MouseEvent<HTMLButtonElement>) => {\n      if (disabled) {\n        event.preventDefault();\n        return;\n      }\n      if (!openOnPress) {\n        return;\n      }\n      const interactionType =\n        pointerTypeRef.current || interactionTypeFromEvent(event.nativeEvent);\n      pointerTypeRef.current = \"\";\n      context.pressTrigger(\n        triggerId,\n        event.currentTarget,\n        event.nativeEvent,\n        interactionType\n      );\n    }\n  );\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLButtonElement>) => {\n      if (\n        triggerOpen &&\n        !event.shiftKey &&\n        event.key === \"Tab\" &&\n        context.getOpenReason() !== \"trigger-press\"\n      ) {\n        const first = context.contentRef.current\n          ? getHeidiTabbableElements(context.contentRef.current)[0]\n          : undefined;\n        if (first) {\n          event.preventDefault();\n          first.focus({ preventScroll: true });\n          return;\n        }\n      }\n      if (\n        nativeButton ||\n        !openOnPress ||\n        event.target !== event.currentTarget\n      ) {\n        return;\n      }\n      if (event.key === \" \") {\n        event.preventDefault();\n        return;\n      }\n      if (event.key === \"Enter\" && !event.repeat) {\n        event.preventDefault();\n        event.currentTarget.click();\n      }\n    }\n  );\n  const handleKeyUp = composeHeidiEventHandlers(\n    onKeyUp,\n    (event: KeyboardEvent<HTMLButtonElement>) => {\n      if (\n        nativeButton ||\n        !openOnPress ||\n        event.target !== event.currentTarget ||\n        event.key !== \" \"\n      ) {\n        return;\n      }\n      event.preventDefault();\n      event.currentTarget.click();\n    }\n  );\n\n  return renderHeidiElement({\n    className: HOVER_CARD_CLASSES.trigger,\n    dataPart: \"hover-card-trigger\",\n    element: \"button\",\n    props: {\n      ...nativeProps,\n      ...(nativeButton\n        ? nativeDisabledAttrs(disabled)\n        : ariaDisabledAttrs(disabled)),\n      \"aria-controls\": context.contentId,\n      \"aria-expanded\": triggerOpen,\n      \"aria-haspopup\": \"dialog\",\n      children,\n      \"data-popup-open\": triggerOpen ? true : undefined,\n      id: triggerId,\n      onBlur: handleBlur,\n      onClick: handleClick,\n      onFocus: handleFocus,\n      onKeyDown: handleKeyDown,\n      onKeyUp: handleKeyUp,\n      onPointerDown: handlePointerDown,\n      onPointerEnter: handlePointerEnter,\n      onPointerLeave: handlePointerLeave,\n      onPointerMove: handlePointerMove,\n      ref: mergeHeidiRefs(ref, localRef),\n      role: !nativeButton && openOnPress ? \"button\" : undefined,\n      tabIndex: !nativeButton && openOnPress ? (tabIndex ?? 0) : tabIndex,\n      type: nativeButton ? \"button\" : undefined\n    },\n    renderProps: { className, render, style },\n    state,\n    structuralStyle: {\n      anchorName: anchorNameForTrigger(triggerId)\n    } as CSSProperties\n  });\n}\n\ntype HoverCardEmptyState = Record<string, never>;\n\nexport type HoverCardTitleProps = HeidiIntrinsicHostProps<\n  HoverCardEmptyState,\n  \"h2\"\n> &\n  Omit<NativeHostProps<\"h2\">, \"id\"> & {\n    children?: ReactNode;\n    id?: string;\n    ref?: Ref<HTMLHeadingElement>;\n  };\n\nexport function HoverCardTitle({\n  children,\n  className,\n  id,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: HoverCardTitleProps) {\n  const generatedId = safeId(useId());\n  const { registerTitle } = useHoverCardContext(\"Title\");\n  const titleId = id ?? `hui-hover-card-title-${generatedId}`;\n  useHeidiLayoutEffect(() => registerTitle(titleId), [registerTitle, titleId]);\n  return renderHeidiElement({\n    className: HOVER_CARD_CLASSES.title,\n    dataPart: \"hover-card-title\",\n    element: \"h2\",\n    props: { ...nativeProps, children, id: titleId, ref },\n    renderProps: { className, render, style },\n    state: {}\n  });\n}\n\nexport type HoverCardDescriptionProps = HeidiIntrinsicHostProps<\n  HoverCardEmptyState,\n  \"p\"\n> &\n  Omit<NativeHostProps<\"p\">, \"id\"> & {\n    children?: ReactNode;\n    id?: string;\n    ref?: Ref<HTMLParagraphElement>;\n  };\n\nexport function HoverCardDescription({\n  children,\n  className,\n  id,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: HoverCardDescriptionProps) {\n  const generatedId = safeId(useId());\n  const { registerDescription } = useHoverCardContext(\"Description\");\n  const descriptionId = id ?? `hui-hover-card-description-${generatedId}`;\n  useHeidiLayoutEffect(\n    () => registerDescription(descriptionId),\n    [descriptionId, registerDescription]\n  );\n  return renderHeidiElement({\n    className: HOVER_CARD_CLASSES.description,\n    dataPart: \"hover-card-description\",\n    element: \"p\",\n    props: { ...nativeProps, children, id: descriptionId, ref },\n    renderProps: { className, render, style },\n    state: {}\n  });\n}\n\nexport type HoverCardContentState = {\n  align: HoverCardAlign;\n  open: boolean;\n  side: HoverCardSide;\n  transitionStatus: HoverCardTransitionStatus;\n};\n\ntype HoverCardPhysicalSide = Exclude<\n  HoverCardSide,\n  \"inline-end\" | \"inline-start\"\n>;\n\nfunction resolveHoverCardPhysicalSide(\n  side: HoverCardSide,\n  direction: \"ltr\" | \"rtl\"\n): HoverCardPhysicalSide {\n  if (side === \"inline-start\") {\n    return direction === \"rtl\" ? \"right\" : \"left\";\n  }\n  if (side === \"inline-end\") {\n    return direction === \"rtl\" ? \"left\" : \"right\";\n  }\n  return side;\n}\n\ntype HoverCardContentNativeProps = Omit<\n  NativeHostProps<\"div\">,\n  | \"children\"\n  | \"id\"\n  | \"onBlur\"\n  | \"onFocus\"\n  | \"onKeyDown\"\n  | \"onPointerDown\"\n  | \"onPointerEnter\"\n  | \"onPointerLeave\"\n  | \"onToggle\"\n  | \"popover\"\n  | \"role\"\n>;\n\nexport type HoverCardContentProps = HeidiIntrinsicHostProps<\n  HoverCardContentState,\n  \"div\"\n> &\n  HoverCardContentNativeProps & {\n    align?: HoverCardAlign;\n    /** Cross-axis offset in CSS pixels. */\n    alignOffset?: number;\n    children?: ReactNode;\n    finalFocus?: HoverCardFocusTarget;\n    initialFocus?: HoverCardFocusTarget;\n    /** Keep the closed native popover in the DOM after exit motion. */\n    keepMounted?: boolean;\n    /** Accessible-name convenience when no Title is rendered. */\n    label?: string;\n    onBlur?: ComponentPropsWithoutRef<\"div\">[\"onBlur\"];\n    onFocus?: ComponentPropsWithoutRef<\"div\">[\"onFocus\"];\n    onKeyDown?: ComponentPropsWithoutRef<\"div\">[\"onKeyDown\"];\n    onPointerDown?: ComponentPropsWithoutRef<\"div\">[\"onPointerDown\"];\n    onPointerEnter?: ComponentPropsWithoutRef<\"div\">[\"onPointerEnter\"];\n    onPointerLeave?: ComponentPropsWithoutRef<\"div\">[\"onPointerLeave\"];\n    onToggle?: ComponentPropsWithoutRef<\"div\">[\"onToggle\"];\n    ref?: Ref<HTMLDivElement>;\n    side?: HoverCardSide;\n    /** Main-axis gap in CSS pixels. */\n    sideOffset?: number;\n  };\n\nfunction hoverCardHasAccessibleName(popup: HTMLDivElement): boolean {\n  if (popup.getAttribute(\"aria-label\")?.trim()) {\n    return true;\n  }\n  const labelledBy = popup.getAttribute(\"aria-labelledby\")?.trim();\n  if (!labelledBy) {\n    return false;\n  }\n  const labelledElements = new Map(\n    Array.from(popup.querySelectorAll<HTMLElement>(\"[id]\"), (element) => [\n      element.id,\n      element\n    ])\n  );\n  return labelledBy.split(/\\s+/).some((id) => {\n    const element = labelledElements.get(id);\n    return element != null && !!element.textContent?.trim();\n  });\n}\n\nfunction focusInitialElement(\n  popup: HTMLDivElement,\n  initialFocus: HoverCardFocusTarget | undefined,\n  interactionType: HoverCardInteractionType\n) {\n  const directive = resolveFocusDirective(initialFocus, interactionType);\n  if (directive.kind === \"skip\") {\n    return;\n  }\n  if (\n    directive.kind === \"target\" &&\n    directive.target.isConnected &&\n    (directive.target === popup || popup.contains(directive.target))\n  ) {\n    directive.target.focus({ preventScroll: true });\n    return;\n  }\n  if (interactionType === \"touch\") {\n    popup.focus({ preventScroll: true });\n    return;\n  }\n  const target = getHeidiTabbableElements(popup)[0] ?? popup;\n  target.focus({ preventScroll: true });\n}\n\nexport function HoverCardContent({\n  \"aria-describedby\": ariaDescribedBy,\n  \"aria-label\": ariaLabel,\n  \"aria-labelledby\": ariaLabelledBy,\n  align = \"center\",\n  alignOffset = 0,\n  children,\n  className,\n  finalFocus,\n  initialFocus,\n  keepMounted = false,\n  label,\n  onBlur,\n  onFocus,\n  onKeyDown,\n  onPointerDown,\n  onPointerEnter,\n  onPointerLeave,\n  onToggle,\n  ref,\n  render,\n  side = \"bottom\",\n  sideOffset = 4,\n  style,\n  tabIndex = -1,\n  ...nativeProps\n}: HoverCardContentProps) {\n  const context = useHoverCardContext(\"Content\");\n  const {\n    activeTriggerId,\n    completeOpenChange,\n    finishClose,\n    getActiveTrigger,\n    getInteractionType,\n    getOpenReason,\n    open,\n    registerUnmount,\n    restoreFocus,\n    shouldRetainClosedContent\n  } = context;\n  const popupRef = useRef<HTMLDivElement | null>(null);\n  const [direction, setDirection] = useState<\"ltr\" | \"rtl\">(\"ltr\");\n  const [forceUnmounted, setForceUnmounted] = useState(false);\n  const [retained, setRetained] = useState(open || keepMounted);\n  const [transitionStatus, setTransitionStatus] =\n    useState<HoverCardTransitionStatus>(\n      open ? \"starting\" : undefined\n    );\n  const cycleRef = useRef(0);\n  const internallyTogglingRef = useRef(false);\n  const openRef = useRef(open);\n  openRef.current = open;\n  const keepMountedRef = useRef(keepMounted);\n  keepMountedRef.current = keepMounted;\n  const initialFocusRef = useRef(initialFocus);\n  initialFocusRef.current = initialFocus;\n  const finalFocusRef = useRef(finalFocus);\n  finalFocusRef.current = finalFocus;\n  const shouldRender =\n    !forceUnmounted && (keepMounted || open || retained);\n\n  useHeidiLayoutEffect(() => {\n    if (!shouldRender) {\n      return;\n    }\n    const directionSource = getActiveTrigger() ?? popupRef.current;\n    const nextDirection =\n      directionSource && getComputedStyle(directionSource).direction === \"rtl\"\n        ? \"rtl\"\n        : \"ltr\";\n    setDirection((current) =>\n      current === nextDirection ? current : nextDirection\n    );\n  }, [activeTriggerId, getActiveTrigger, shouldRender]);\n\n  useHeidiLayoutEffect(\n    () =>\n      registerUnmount(() => {\n        if (!openRef.current) {\n          setRetained(false);\n          setForceUnmounted(true);\n        }\n      }),\n    [registerUnmount]\n  );\n\n  useHeidiLayoutEffect(() => {\n    if (open) {\n      setForceUnmounted(false);\n      setRetained(true);\n    }\n  }, [open]);\n\n  useEffect(() => {\n    if (process.env.NODE_ENV === \"production\" || !shouldRender) {\n      return;\n    }\n    const timeout = window.setTimeout(() => {\n      const popup = popupRef.current;\n      if (popup && !hoverCardHasAccessibleName(popup)) {\n        // oxlint-disable-next-line no-console -- an unnamed dialog is a WCAG failure.\n        console.warn(\n          \"heidi-ui HoverCard: missing accessible name — provide <HoverCard.Title>, label, aria-label, or aria-labelledby.\"\n        );\n      }\n    }, 0);\n    return () => window.clearTimeout(timeout);\n  }, [shouldRender]);\n\n  useEffect(() => {\n    const popup = popupRef.current;\n    if (!popup) {\n      return;\n    }\n    const cycle = ++cycleRef.current;\n    if (open) {\n      setRetained(true);\n      setTransitionStatus(\"starting\");\n      if (!popup.matches(\":popover-open\")) {\n        internallyTogglingRef.current = true;\n        try {\n          popup.showPopover();\n        } catch {\n          internallyTogglingRef.current = false;\n          return;\n        }\n      }\n      if (getOpenReason() === \"trigger-press\") {\n        focusInitialElement(\n          popup,\n          initialFocusRef.current,\n          getInteractionType()\n        );\n      }\n      void waitForElementAnimations(popup, { awaitPaint: true, subtree: true }).then(() => {\n        if (cycle !== cycleRef.current || !openRef.current) {\n          return;\n        }\n        setTransitionStatus(\"idle\");\n        completeOpenChange(true);\n      });\n      return;\n    }\n\n    const nativeOpen = popup.matches(\":popover-open\");\n    setTransitionStatus(nativeOpen ? \"ending\" : undefined);\n    void waitForElementAnimations(popup, { awaitPaint: true, subtree: true }).then(() => {\n      if (cycle !== cycleRef.current || openRef.current) {\n        return;\n      }\n      if (popup.matches(\":popover-open\")) {\n        internallyTogglingRef.current = true;\n        try {\n          popup.hidePopover();\n        } catch {\n          internallyTogglingRef.current = false;\n        }\n      }\n      setTransitionStatus(undefined);\n      completeOpenChange(false);\n      requestAnimationFrame(() => {\n        if (!openRef.current) {\n          restoreFocus(popup, finalFocusRef.current);\n          finishClose();\n        }\n      });\n      if (\n        !keepMountedRef.current &&\n        !shouldRetainClosedContent()\n      ) {\n        setRetained(false);\n      }\n    });\n  }, [\n    completeOpenChange,\n    finishClose,\n    getInteractionType,\n    getOpenReason,\n    open,\n    restoreFocus,\n    shouldRetainClosedContent\n  ]);\n\n  if (!shouldRender) {\n    return null;\n  }\n\n  const computedLabel = ariaLabel ?? label;\n  const computedLabelledBy =\n    ariaLabelledBy ??\n    (computedLabel ? undefined : context.titleIds.join(\" \") || undefined);\n  const computedDescribedBy =\n    ariaDescribedBy ?? (context.descriptionIds.join(\" \") || undefined);\n  const state: HoverCardContentState = {\n    align,\n    open,\n    side,\n    transitionStatus\n  };\n  const physicalSide = resolveHoverCardPhysicalSide(side, direction);\n\n  const handleFocus = composeHeidiEventHandlers(\n    onFocus,\n    () => {\n      context.cancelPendingClose();\n    }\n  );\n  const handleBlur = composeHeidiEventHandlers(\n    onBlur,\n    (event: FocusEvent<HTMLDivElement>) => {\n      context.scheduleFocusClose(event.nativeEvent);\n    }\n  );\n  const handlePointerEnter = composeHeidiEventHandlers(\n    onPointerEnter,\n    (event: PointerEvent<HTMLDivElement>) => {\n      if (event.pointerType === \"mouse\") {\n        context.pointerEnterZone(event.currentTarget);\n      }\n    }\n  );\n  const handlePointerLeave = composeHeidiEventHandlers(\n    onPointerLeave,\n    (event: PointerEvent<HTMLDivElement>) => {\n      context.pointerLeaveZone(event.currentTarget, event.nativeEvent);\n    }\n  );\n  const handlePointerDown = composeHeidiEventHandlers(\n    onPointerDown,\n    (event: PointerEvent<HTMLDivElement>) => {\n      context.pointerDownInside(event.nativeEvent);\n    }\n  );\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLDivElement>) => {\n      if (event.key !== \"Tab\" || !event.shiftKey) {\n        return;\n      }\n      const tabbables = getHeidiTabbableElements(event.currentTarget);\n      const active = document.activeElement;\n      if (\n        active === event.currentTarget ||\n        active === tabbables[0]\n      ) {\n        const trigger = context.getActiveTrigger();\n        if (trigger) {\n          event.preventDefault();\n          trigger.focus({ preventScroll: true });\n        }\n      }\n    }\n  );\n  const handleToggle = composeHeidiEventHandlers(\n    onToggle,\n    (event: SyntheticEvent<HTMLDivElement>) => {\n      if (internallyTogglingRef.current) {\n        internallyTogglingRef.current = false;\n        return;\n      }\n      const toggle = event.nativeEvent as ToggleEvent;\n      if (toggle.newState === \"closed\" && context.getOpen()) {\n        const details = context.requestOpen(false, \"none\", event.nativeEvent);\n        if (details?.isCanceled || context.getOpen()) {\n          requestAnimationFrame(() => {\n            const popup = popupRef.current;\n            if (\n              popup?.isConnected &&\n              context.getOpen() &&\n              !popup.matches(\":popover-open\")\n            ) {\n              popup.showPopover();\n            }\n          });\n        }\n      }\n    }\n  );\n\n  return renderHeidiElement({\n    className: HOVER_CARD_CLASSES.content,\n    dataPart: \"hover-card-content\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      \"aria-describedby\": computedDescribedBy,\n      \"aria-label\": computedLabel,\n      \"aria-labelledby\": computedLabelledBy,\n      children,\n      \"data-align\": align,\n      \"data-open-reason\": context.openReason,\n      \"data-position-side\": physicalSide,\n      \"data-side\": side,\n      \"data-state\": open ? \"open\" : \"closed\",\n      ...openStateAttributes(open),\n      ...transitionAttributes(transitionStatus),\n      id: context.contentId,\n      onBlur: handleBlur,\n      onFocus: handleFocus,\n      onKeyDown: handleKeyDown,\n      onPointerDown: handlePointerDown,\n      onPointerEnter: handlePointerEnter,\n      onPointerLeave: handlePointerLeave,\n      onToggle: handleToggle,\n      popover: \"manual\",\n      ref: mergeHeidiRefs(ref, popupRef, context.contentRef),\n      role: \"dialog\",\n      tabIndex\n    },\n    renderProps: { className, render, style },\n    state,\n    structuralStyle: {\n      \"--_hui-hover-card-align-offset\": `${alignOffset}px`,\n      \"--_hui-hover-card-side-offset\": `${Math.max(0, sideOffset)}px`,\n      positionAnchor: activeTriggerId\n        ? anchorNameForTrigger(activeTriggerId)\n        : undefined\n    } as CSSProperties\n  });\n}\n\nexport type HoverCardCloseState = {\n  disabled: boolean;\n};\n\ntype HoverCardCloseNativeProps = Omit<\n  NativeHostProps<\"button\">,\n  \"aria-label\" | \"disabled\" | \"type\"\n>;\n\nexport type HoverCardCloseProps = HeidiIntrinsicHostProps<\n  HoverCardCloseState,\n  \"button\"\n> &\n  HoverCardCloseNativeProps & {\n    \"aria-label\"?: string;\n    children?: ReactNode;\n    disabled?: boolean;\n    nativeButton?: boolean;\n    ref?: Ref<HTMLButtonElement>;\n  };\n\nexport function HoverCardClose({\n  \"aria-label\": ariaLabel,\n  children,\n  className,\n  disabled = false,\n  nativeButton = true,\n  onClick,\n  onKeyDown,\n  onKeyUp,\n  ref,\n  render,\n  style,\n  tabIndex,\n  ...nativeProps\n}: HoverCardCloseProps) {\n  const context = useHoverCardContext(\"Close\");\n  const state = useMemo<HoverCardCloseState>(\n    () => ({ disabled }),\n    [disabled]\n  );\n  const handleClick = composeHeidiEventHandlers(\n    onClick,\n    (event: MouseEvent<HTMLButtonElement>) => {\n      if (disabled) {\n        event.preventDefault();\n        return;\n      }\n      context.requestOpen(\n        false,\n        \"close-press\",\n        event.nativeEvent,\n        event.currentTarget\n      );\n    }\n  );\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLButtonElement>) => {\n      if (nativeButton || event.target !== event.currentTarget) {\n        return;\n      }\n      if (event.key === \" \") {\n        event.preventDefault();\n      } else if (event.key === \"Enter\" && !event.repeat) {\n        event.preventDefault();\n        event.currentTarget.click();\n      }\n    }\n  );\n  const handleKeyUp = composeHeidiEventHandlers(\n    onKeyUp,\n    (event: KeyboardEvent<HTMLButtonElement>) => {\n      if (\n        nativeButton ||\n        event.target !== event.currentTarget ||\n        event.key !== \" \"\n      ) {\n        return;\n      }\n      event.preventDefault();\n      event.currentTarget.click();\n    }\n  );\n  return renderHeidiElement({\n    className: HOVER_CARD_CLASSES.close,\n    dataPart: \"hover-card-close\",\n    element: \"button\",\n    props: {\n      ...nativeProps,\n      ...(nativeButton\n        ? nativeDisabledAttrs(disabled)\n        : ariaDisabledAttrs(disabled)),\n      \"aria-label\": ariaLabel ?? (children ? undefined : \"Close preview\"),\n      children,\n      onClick: handleClick,\n      onKeyDown: handleKeyDown,\n      onKeyUp: handleKeyUp,\n      ref,\n      role: nativeButton ? undefined : \"button\",\n      tabIndex: nativeButton ? tabIndex : (tabIndex ?? 0),\n      type: nativeButton ? \"button\" : undefined\n    },\n    renderProps: { className, render, style },\n    state\n  });\n}\n\n/** Base-shaped alias while preserving the original Content export. */\nexport const HoverCardPopup = HoverCardContent;\n\nexport const HoverCard = {\n  Close: HoverCardClose,\n  Content: HoverCardContent,\n  Description: HoverCardDescription,\n  Popup: HoverCardPopup,\n  Root: HoverCardRoot,\n  Title: HoverCardTitle,\n  Trigger: HoverCardTrigger\n} as const;\n",
      "path": "packages/heidi-ui/src/hover-card/hover-card.tsx",
      "target": "components/ui/heidi/hover-card/hover-card.tsx",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui hover-card — STRUCTURAL CSS only (platform behavior).\n * Native top layer + CSS anchors; no --hui-* / --heidi-* theme tokens.\n */\n\n@layer heidi-ui-base {\n  .hui-hover-card-content:is(\n    [data-side=\"bottom\"],\n    [data-side=\"inline-end\"],\n    [data-side=\"inline-start\"],\n    [data-side=\"left\"],\n    [data-side=\"right\"],\n    [data-side=\"top\"]\n  ) {\n    --_hui-hover-card-align-offset: 0px;\n    --_hui-hover-card-side-offset: 4px;\n\n    box-sizing: border-box;\n    inset: auto;\n    margin: var(--_hui-hover-card-side-offset);\n    max-block-size: calc(100dvb - 2 * var(--_hui-hover-card-side-offset));\n    max-inline-size: calc(100dvi - 2 * var(--_hui-hover-card-side-offset));\n    overflow: auto;\n    overscroll-behavior: contain;\n    position: fixed;\n    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;\n    scrollbar-gutter: stable;\n    transition-behavior: allow-discrete;\n    transition-property: display, overlay;\n  }\n\n  .hui-hover-card-content:not(:popover-open) {\n    display: none !important;\n  }\n\n  .hui-hover-card-content[data-position-side=\"top\"],\n  .hui-hover-card-content[data-position-side=\"bottom\"] {\n    translate: var(--_hui-hover-card-align-offset) 0;\n  }\n\n  .hui-hover-card-content[data-position-side=\"left\"],\n  .hui-hover-card-content[data-position-side=\"right\"] {\n    translate: 0 var(--_hui-hover-card-align-offset);\n  }\n\n  .hui-hover-card-content[data-position-side=\"top\"][data-align=\"start\"] {\n    position-area: block-start span-inline-end;\n  }\n\n  .hui-hover-card-content[data-position-side=\"top\"][data-align=\"center\"] {\n    position-area: block-start;\n  }\n\n  .hui-hover-card-content[data-position-side=\"top\"][data-align=\"end\"] {\n    position-area: block-start span-inline-start;\n  }\n\n  .hui-hover-card-content[data-position-side=\"bottom\"][data-align=\"start\"] {\n    position-area: block-end span-inline-end;\n  }\n\n  .hui-hover-card-content[data-position-side=\"bottom\"][data-align=\"center\"] {\n    position-area: block-end;\n  }\n\n  .hui-hover-card-content[data-position-side=\"bottom\"][data-align=\"end\"] {\n    position-area: block-end span-inline-start;\n  }\n\n  .hui-hover-card-content[data-position-side=\"left\"][data-align=\"start\"] {\n    position-area: left span-block-end;\n  }\n\n  .hui-hover-card-content[data-position-side=\"left\"][data-align=\"center\"] {\n    position-area: left;\n  }\n\n  .hui-hover-card-content[data-position-side=\"left\"][data-align=\"end\"] {\n    position-area: left span-block-start;\n  }\n\n  .hui-hover-card-content[data-position-side=\"right\"][data-align=\"start\"] {\n    position-area: right span-block-end;\n  }\n\n  .hui-hover-card-content[data-position-side=\"right\"][data-align=\"center\"] {\n    position-area: right;\n  }\n\n  .hui-hover-card-content[data-position-side=\"right\"][data-align=\"end\"] {\n    position-area: right span-block-start;\n  }\n}\n",
      "path": "packages/heidi-ui/src/hover-card/hover-card.base.css",
      "target": "components/ui/heidi/hover-card/hover-card.base.css",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui hover-card — VISUAL theme (opt-in). Consumes established --hui-*.\n */\n\n@layer heidi-ui {\n  .hui-hover-card-trigger {\n    background: var(--hui-color-bg-elevated);\n    border: var(--hui-border-width) var(--hui-border-style) var(--hui-color-border-default);\n    border-radius: var(--hui-radius-md);\n    color: var(--hui-color-fg-default);\n    cursor: pointer;\n    font: inherit;\n    padding: var(--hui-space-1-5) var(--hui-space-3);\n  }\n\n  .hui-hover-card-trigger:disabled,\n  .hui-hover-card-trigger[data-disabled=\"true\"] {\n    cursor: not-allowed;\n    opacity: 0.5;\n  }\n\n  .hui-hover-card-trigger:focus-visible,\n  .hui-hover-card-close:focus-visible,\n  .hui-hover-card-content:focus-visible {\n    outline: var(--hui-focus-ring-width) solid var(--hui-color-focus-ring);\n    outline-offset: var(--hui-focus-ring-offset);\n  }\n\n  .hui-hover-card-trigger[data-popup-open=\"true\"] {\n    background: var(--hui-color-interactive-ghost-bg-hover);\n    color: var(--hui-color-fg-strong);\n  }\n\n  .hui-hover-card-content {\n    background: var(--hui-color-bg-raised);\n    border: 0;\n    border-radius: var(--hui-radius-2xl);\n    box-shadow: var(--hui-shadow-surface-lg);\n    color: var(--hui-color-fg-default);\n    font-size: var(--hui-text-body-size);\n    inline-size: max-content;\n    max-inline-size: min(20rem, calc(100dvi - 2rem));\n    opacity: 0;\n    padding: var(--hui-space-3);\n    pointer-events: auto;\n    transform: scale(0.97);\n    transition-duration: var(--hui-duration-fast);\n    transition-property: display, opacity, overlay, transform;\n    transition-timing-function: var(--hui-ease-out);\n  }\n\n  .hui-hover-card-content[data-state=\"open\"] {\n    opacity: 1;\n    transform: none;\n  }\n\n  .hui-hover-card-content[data-state=\"closed\"] {\n    opacity: 0;\n    transform: scale(0.97);\n  }\n\n  .hui-hover-card-content[data-starting-style=\"true\"],\n  .hui-hover-card-content[data-ending-style=\"true\"] {\n    will-change: opacity, transform;\n  }\n\n  @starting-style {\n    .hui-hover-card-content:popover-open[data-state=\"open\"] {\n      opacity: 0;\n      transform: scale(0.97);\n    }\n  }\n\n  .hui-hover-card-title {\n    font-size: var(--hui-text-heading-3-size);\n    font-weight: var(--hui-font-weight-medium);\n    margin: 0 0 var(--hui-space-1);\n  }\n\n  .hui-hover-card-description {\n    color: var(--hui-color-fg-muted);\n    font-size: var(--hui-text-body-size);\n    margin: 0 0 var(--hui-space-2);\n  }\n\n  .hui-hover-card-close {\n    background: transparent;\n    border: var(--hui-border-width) var(--hui-border-style) var(--hui-color-border-default);\n    border-radius: var(--hui-radius-sm);\n    color: var(--hui-color-fg-muted);\n    cursor: pointer;\n    font: inherit;\n    padding: var(--hui-space-0-5) var(--hui-space-2);\n  }\n\n  .hui-hover-card-close:disabled,\n  .hui-hover-card-close[data-disabled=\"true\"] {\n    cursor: not-allowed;\n    opacity: 0.5;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .hui-hover-card-content {\n      transition-duration: 0s;\n    }\n  }\n\n  @media (forced-colors: active) {\n    .hui-hover-card-content {\n      border: var(--hui-border-width) solid CanvasText;\n    }\n  }\n}\n",
      "path": "packages/heidi-ui/src/hover-card/hover-card.theme.css",
      "target": "components/ui/heidi/hover-card/hover-card.theme.css",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui hover-card — aggregator.\n */\n\n@import \"./hover-card.base.css\";\n@import \"../../../../styles/heidi-ui-adapter.css\";\n@import \"./hover-card.theme.css\";\n",
      "path": "packages/heidi-ui/src/hover-card/hover-card.css",
      "target": "components/ui/heidi/hover-card/hover-card.css",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from packages/heidi-ui/src/hover-card/hover-card.base.css + packages/heidi-ui/src/hover-card/hover-card.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\nexport const HOVER_CARD_CLASSES = {\n  close: \"hui-hover-card-close\",\n  content: \"hui-hover-card-content\",\n  description: \"hui-hover-card-description\",\n  title: \"hui-hover-card-title\",\n  trigger: \"hui-hover-card-trigger\",\n} as const;\n",
      "path": "packages/heidi-ui/src/hover-card/hover-card.classes.generated.ts",
      "target": "components/ui/heidi/hover-card/hover-card.classes.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from hover-card.anatomy.json + hover-card.base.css + hover-card.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\n\nexport type HoverCardAlign = \"center\" | \"end\" | \"start\";\nexport type HoverCardDisabled = \"true\";\nexport type HoverCardEndingStyle = \"true\";\nexport type HoverCardPopupOpen = \"true\";\nexport type HoverCardPositionSide = \"bottom\" | \"left\" | \"right\" | \"top\";\nexport type HoverCardSide = \"bottom\" | \"inline-end\" | \"inline-start\" | \"left\" | \"right\" | \"top\";\nexport type HoverCardStartingStyle = \"true\";\nexport type HoverCardState = \"closed\" | \"open\";\n\nexport const HOVER_CARD_ANATOMY = {\n  \"component\": \"hover-card\",\n  \"description\": \"Base-UI-shaped interactive preview on native popover + CSS anchors. Mouse-rest hover intent uses a safe geometric bridge, focus opens without delay, and press pins the non-modal dialog for keyboard/touch access. Controlled changes are cancellable; multiple triggers, nested Escape isolation, focus return, lazy presence, collision fallback, component-local RTL logical sides, and exit completion are first-class.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"close\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-disabled\",\n          \"aria-label\"\n        ],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-close\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-close\",\n      \"description\": \"Optional native close button for press-pinned/touch cards; requests a cancellable close-press change.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"disabled\",\n        \"nativeButton\",\n        \"onClick\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":disabled\",\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ]\n      }\n    },\n    \"content\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-describedby\",\n          \"aria-label\",\n          \"aria-labelledby\"\n        ],\n        \"role\": \"dialog\"\n      },\n      \"class\": \"hui-hover-card-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"hover-card-content\",\n      \"description\": \"Also exported as HoverCard.Popup. Lazily mounted non-modal dialog in the top layer; remains interactive across the safe pointer corridor and stays viewport-contained after CSS-anchor collision flips.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"align\",\n        \"alignOffset\",\n        \"className\",\n        \"finalFocus\",\n        \"initialFocus\",\n        \"keepMounted\",\n        \"label\",\n        \"onBlur\",\n        \"onFocus\",\n        \"onKeyDown\",\n        \"onPointerDown\",\n        \"onPointerEnter\",\n        \"onPointerLeave\",\n        \"onToggle\",\n        \"ref\",\n        \"render\",\n        \"side\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\",\n        \":popover-open\"\n      ],\n      \"states\": {\n        \"data-align\": [\n          \"center\",\n          \"end\",\n          \"start\"\n        ],\n        \"data-ending-style\": [\n          \"true\"\n        ],\n        \"data-position-side\": [\n          \"bottom\",\n          \"left\",\n          \"right\",\n          \"top\"\n        ],\n        \"data-side\": [\n          \"bottom\",\n          \"inline-end\",\n          \"inline-start\",\n          \"left\",\n          \"right\",\n          \"top\"\n        ],\n        \"data-starting-style\": [\n          \"true\"\n        ],\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"description\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-description\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-description\",\n      \"description\": \"Optional supporting text registered into aria-describedby without dangling references.\",\n      \"element\": \"p\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"id\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"title\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-title\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-title\",\n      \"description\": \"Heading registered as the interactive preview dialog's accessible name.\",\n      \"element\": \"h2\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"id\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-controls\",\n          \"aria-disabled\",\n          \"aria-expanded\",\n          \"aria-haspopup\"\n        ],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-trigger\",\n      \"description\": \"Native button by default; supports multiple invokers, mouse-only rest intent, immediate focus access, optional press pinning, and link-safe composition.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"disabled\",\n        \"id\",\n        \"nativeButton\",\n        \"onBlur\",\n        \"onClick\",\n        \"onFocus\",\n        \"onPointerEnter\",\n        \"onPointerLeave\",\n        \"onPointerMove\",\n        \"openOnPress\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":disabled\",\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ],\n        \"data-popup-open\": [\n          \"true\"\n        ]\n      }\n    }\n  },\n  \"rootProps\": [\n    \"actionsRef\",\n    \"closeDelay\",\n    \"defaultOpen\",\n    \"defaultTriggerId\",\n    \"onOpenChange\",\n    \"onOpenChangeComplete\",\n    \"open\",\n    \"openDelay\",\n    \"triggerId\"\n  ],\n  \"tokens\": [\n    \"--hui-border-style\",\n    \"--hui-border-width\",\n    \"--hui-color-bg-elevated\",\n    \"--hui-color-bg-raised\",\n    \"--hui-color-border-default\",\n    \"--hui-color-fg-default\",\n    \"--hui-color-fg-muted\",\n    \"--hui-color-fg-strong\",\n    \"--hui-color-focus-ring\",\n    \"--hui-color-interactive-ghost-bg-hover\",\n    \"--hui-duration-fast\",\n    \"--hui-ease-out\",\n    \"--hui-focus-ring-offset\",\n    \"--hui-focus-ring-width\",\n    \"--hui-font-weight-medium\",\n    \"--hui-radius-2xl\",\n    \"--hui-radius-md\",\n    \"--hui-radius-sm\",\n    \"--hui-shadow-surface-lg\",\n    \"--hui-space-0-5\",\n    \"--hui-space-1\",\n    \"--hui-space-1-5\",\n    \"--hui-space-2\",\n    \"--hui-space-3\",\n    \"--hui-text-body-size\",\n    \"--hui-text-heading-3-size\"\n  ]\n} as const;\n",
      "path": "packages/heidi-ui/src/hover-card/hover-card.anatomy.generated.ts",
      "target": "components/ui/heidi/hover-card/hover-card.anatomy.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "{\n  \"component\": \"hover-card\",\n  \"description\": \"Base-UI-shaped interactive preview on native popover + CSS anchors. Mouse-rest hover intent uses a safe geometric bridge, focus opens without delay, and press pins the non-modal dialog for keyboard/touch access. Controlled changes are cancellable; multiple triggers, nested Escape isolation, focus return, lazy presence, collision fallback, component-local RTL logical sides, and exit completion are first-class.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"close\": {\n      \"aria\": {\n        \"owns\": [\"aria-disabled\", \"aria-label\"],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-close\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-close\",\n      \"description\": \"Optional native close button for press-pinned/touch cards; requests a cancellable close-press change.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"disabled\",\n        \"nativeButton\",\n        \"onClick\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\":disabled\", \":focus-visible\"],\n      \"states\": {\n        \"data-disabled\": [\"true\"]\n      }\n    },\n    \"content\": {\n      \"aria\": {\n        \"owns\": [\"aria-describedby\", \"aria-label\", \"aria-labelledby\"],\n        \"role\": \"dialog\"\n      },\n      \"class\": \"hui-hover-card-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"hover-card-content\",\n      \"description\": \"Also exported as HoverCard.Popup. Lazily mounted non-modal dialog in the top layer; remains interactive across the safe pointer corridor and stays viewport-contained after CSS-anchor collision flips.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"align\",\n        \"alignOffset\",\n        \"className\",\n        \"finalFocus\",\n        \"initialFocus\",\n        \"keepMounted\",\n        \"label\",\n        \"onBlur\",\n        \"onFocus\",\n        \"onKeyDown\",\n        \"onPointerDown\",\n        \"onPointerEnter\",\n        \"onPointerLeave\",\n        \"onToggle\",\n        \"ref\",\n        \"render\",\n        \"side\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\":focus-visible\", \":popover-open\"],\n      \"states\": {\n        \"data-align\": [\"center\", \"end\", \"start\"],\n        \"data-ending-style\": [\"true\"],\n        \"data-position-side\": [\"bottom\", \"left\", \"right\", \"top\"],\n        \"data-side\": [\n          \"bottom\",\n          \"inline-end\",\n          \"inline-start\",\n          \"left\",\n          \"right\",\n          \"top\"\n        ],\n        \"data-starting-style\": [\"true\"],\n        \"data-state\": [\"closed\", \"open\"]\n      }\n    },\n    \"description\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-description\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-description\",\n      \"description\": \"Optional supporting text registered into aria-describedby without dangling references.\",\n      \"element\": \"p\",\n      \"nativeProps\": true,\n      \"props\": [\"className\", \"id\", \"ref\", \"render\", \"style\"],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"title\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-title\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-title\",\n      \"description\": \"Heading registered as the interactive preview dialog's accessible name.\",\n      \"element\": \"h2\",\n      \"nativeProps\": true,\n      \"props\": [\"className\", \"id\", \"ref\", \"render\", \"style\"],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-controls\",\n          \"aria-disabled\",\n          \"aria-expanded\",\n          \"aria-haspopup\"\n        ],\n        \"role\": null\n      },\n      \"class\": \"hui-hover-card-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"hover-card-trigger\",\n      \"description\": \"Native button by default; supports multiple invokers, mouse-only rest intent, immediate focus access, optional press pinning, and link-safe composition.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"disabled\",\n        \"id\",\n        \"nativeButton\",\n        \"onBlur\",\n        \"onClick\",\n        \"onFocus\",\n        \"onPointerEnter\",\n        \"onPointerLeave\",\n        \"onPointerMove\",\n        \"openOnPress\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\":disabled\", \":focus-visible\"],\n      \"states\": {\n        \"data-disabled\": [\"true\"],\n        \"data-popup-open\": [\"true\"]\n      }\n    }\n  },\n  \"rootProps\": [\n    \"actionsRef\",\n    \"closeDelay\",\n    \"defaultOpen\",\n    \"defaultTriggerId\",\n    \"onOpenChange\",\n    \"onOpenChangeComplete\",\n    \"open\",\n    \"openDelay\",\n    \"triggerId\"\n  ]\n}\n",
      "path": "packages/heidi-ui/src/hover-card/hover-card.anatomy.json",
      "target": "components/ui/heidi/hover-card/hover-card.anatomy.json",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared Web-Animations settling helpers (P6).\n *\n * Dialog, HoverCard and Collapsible each carried a byte-identical async\n * \"wait for this element's animations, but never forever\" routine, and all\n * four popover-family components carried their own `finiteAnimationDuration`.\n *\n * ponytail: the audit read this as one helper duplicated four times and\n * prescribed a single `waitForElementAnimations` covering \"the four call-site\n * variants\". Diffing them first — which that same entry insisted on — shows\n * that is not the shape. THREE are the same async routine differing only in\n * two flags. Menu's `afterPopoverAnimations` is a different algorithm: it is\n * callback-shaped rather than awaited, returns a canceller, subscribes to\n * `finish`/`cancel` events instead of racing `animation.finished`, and falls\n * back to computed CSS on engines without `getAnimations`. Forcing it into the\n * awaited shape would have rewritten working popover-dismissal logic to make a\n * count go from four to one. So Menu keeps its own routine and shares only the\n * duration helper it genuinely had in common.\n *\n * The duration helper resolves to MENU's copy, which was the strict one: the\n * other three returned `timing.endTime` raw, so a non-finite or negative\n * `endTime` (reachable via a negative `endDelay`) propagated into\n * `setTimeout` — and `setTimeout(fn, NaN)` fires at 0ms, collapsing the wait\n * it was supposed to bound. Same class as the non-finite bounds that reached\n * Slider's and Progress's ARIA.\n */\n\n/** Ceiling on any settle wait. A stuck animation must not strand a component. */\nexport const MAX_ANIMATION_WAIT_MS = 30_000;\n\n/** Grace added to the longest observed animation before the wait gives up. */\nexport const ANIMATION_WAIT_GRACE_MS = 250;\n\nexport function finiteAnimationDuration(\n  animation: Animation\n): number | undefined {\n  const timing = animation.effect?.getComputedTiming();\n  if (\n    !timing ||\n    timing.iterations === Infinity ||\n    typeof timing.endTime !== \"number\" ||\n    !Number.isFinite(timing.endTime)\n  ) {\n    return undefined;\n  }\n  return Math.max(0, timing.endTime);\n}\n\nexport async function afterNextPaint(): Promise<void> {\n  await new Promise<void>((resolve) => {\n    requestAnimationFrame(() => requestAnimationFrame(() => resolve()));\n  });\n}\n\n/** Animations worth waiting on: real, still running, and finitely long. */\nexport function pendingAnimations(\n  element: Element,\n  subtree: boolean\n): Array<{ animation: Animation; duration: number }> {\n  // Embedded and older engines can omit the Web Animations inspection API.\n  // In that no-observer path the state must still settle instead of throwing.\n  if (typeof element.getAnimations !== \"function\") {\n    return [];\n  }\n  return element\n    .getAnimations(subtree ? { subtree: true } : undefined)\n    .map((animation) => ({\n      animation,\n      duration: finiteAnimationDuration(animation)\n    }))\n    .filter(\n      (entry): entry is { animation: Animation; duration: number } =>\n        entry.duration !== undefined &&\n        entry.duration > 0 &&\n        entry.animation.playState !== \"finished\" &&\n        entry.animation.playState !== \"idle\"\n    );\n}\n\nexport type WaitForElementAnimationsOptions = {\n  /**\n   * Wait two frames before inspecting. Dialog and HoverCard need this: they\n   * ask immediately after a state flip, before the engine has started the\n   * animations they mean to wait for. Collapsible asks after the fact and\n   * must NOT gain the extra frames — that would be an observable timing\n   * change, and this slice does not make those.\n   */\n  awaitPaint?: boolean;\n  /** Inspect descendants too — for panels whose motion lives on children. */\n  subtree?: boolean;\n};\n\nexport async function waitForElementAnimations(\n  element: Element,\n  { awaitPaint = false, subtree = false }: WaitForElementAnimationsOptions = {}\n): Promise<void> {\n  if (awaitPaint) {\n    await afterNextPaint();\n    // ponytail: the connectedness check is deliberately INSIDE the awaitPaint\n    // branch rather than unconditional. It is not a general safety net — it\n    // exists because those two frames are a window in which the element can be\n    // torn down, and it was present in exactly the two copies that wait. The\n    // non-waiting caller has no such window: nothing can run between its own\n    // check and this call, so hoisting the guard would be adding behaviour to\n    // Collapsible in a slice whose whole premise is changing none.\n    if (!element.isConnected) {\n      return;\n    }\n  }\n  const animations = pendingAnimations(element, subtree);\n  if (animations.length === 0) {\n    return;\n  }\n  const maximum = Math.min(\n    MAX_ANIMATION_WAIT_MS,\n    Math.max(...animations.map(({ duration }) => duration)) +\n      ANIMATION_WAIT_GRACE_MS\n  );\n  let timeout = 0;\n  await Promise.race([\n    Promise.allSettled(animations.map(({ animation }) => animation.finished)),\n    new Promise<void>((resolve) => {\n      timeout = window.setTimeout(resolve, maximum);\n    })\n  ]);\n  window.clearTimeout(timeout);\n}\n",
      "path": "packages/heidi-ui/src/_internal/animation-wait.ts",
      "target": "components/ui/heidi/_internal/animation-wait.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Cross-cutting disabled attrs for heidi-ui interactive hosts.\n *\n * House convention (Phase 23):\n * - Button widgets → native `disabled` + `data-disabled=\"true\"` (CSS + a11y).\n * - Non-button options (Select.Item) → `aria-disabled` + `data-disabled=\"true\"`\n *   (native disabled is invalid on role=option divs).\n * - Group roots may cascade `disabled` to children and mirror `data-disabled`.\n * - Keyboard nav / activation always skip disabled hosts.\n */\n\nexport type NativeDisabledAttrs = {\n  \"data-disabled\"?: true;\n  disabled?: true;\n};\n\nexport type AriaDisabledAttrs = {\n  \"aria-disabled\"?: true;\n  \"data-disabled\"?: true;\n};\n\nexport type DataDisabledAttrs = {\n  \"data-disabled\"?: true;\n};\n\n/** Native button/input disabled + styling hook. */\nexport function nativeDisabledAttrs(disabled: boolean | undefined): NativeDisabledAttrs {\n  if (!disabled) {\n    return {};\n  }\n  return {\n    \"data-disabled\": true,\n    disabled: true\n  };\n}\n\n/** ARIA-disabled for non-native hosts (e.g. role=option). */\nexport function ariaDisabledAttrs(disabled: boolean | undefined): AriaDisabledAttrs {\n  if (!disabled) {\n    return {};\n  }\n  return {\n    \"aria-disabled\": true,\n    \"data-disabled\": true\n  };\n}\n\n/** Styling hook only (group roots that cascade disabled). */\nexport function dataDisabledAttrs(disabled: boolean | undefined): DataDisabledAttrs {\n  if (!disabled) {\n    return {};\n  }\n  return {\n    \"data-disabled\": true\n  };\n}\n\nexport function isDisabledElement(el: Element | null | undefined): boolean {\n  if (!el || !(el instanceof HTMLElement)) {\n    return false;\n  }\n  if (el.matches(\":disabled\")) {\n    return true;\n  }\n  return el.getAttribute(\"aria-disabled\") === \"true\" || el.getAttribute(\"data-disabled\") === \"true\";\n}\n",
      "path": "packages/heidi-ui/src/_internal/disabled.ts",
      "target": "components/ui/heidi/_internal/disabled.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared Escape layering for transient top-layer surfaces (tooltip, toast,\n * hover-card). Each surface registers while open; only the topmost entry may\n * consume an Escape keydown, and consuming cancels the event\n * (preventDefault + stopPropagation) so one keystroke never also dismisses\n * the layer underneath — native <dialog closedby=\"closerequest\"> hosts only\n * close on an uncancelled Escape keydown.\n */\n\ntype EscapeLayer = {\n  getElement: () => Element | null;\n  id: string;\n};\n\nconst openEscapeLayers: EscapeLayer[] = [];\n\nfunction removeFromStack(id: string): void {\n  const index = openEscapeLayers.findIndex((layer) => layer.id === id);\n  if (index >= 0) {\n    openEscapeLayers.splice(index, 1);\n  }\n}\n\n/**\n * Register an open surface (re-registering moves it to the top). Returns the\n * stack cleanup. `getElement` identifies the surface's top-layer element so\n * `hasEscapeConsumerOutsideStack` can tell stack popovers from foreign ones.\n */\nexport function addToOpenStack(\n  id: string,\n  getElement: () => Element | null = () => null\n): () => void {\n  removeFromStack(id);\n  openEscapeLayers.push({ getElement, id });\n  return () => removeFromStack(id);\n}\n\nexport function isTopmost(id: string): boolean {\n  return openEscapeLayers.at(-1)?.id === id;\n}\n\n/**\n * True when an open popover that does not participate in this stack (menu,\n * select, context-menu, navigation-menu, popover) could consume the same\n * Escape keystroke — an unfocused toast must defer to it.\n * ponytail: open `<dialog>` hosts are deliberately NOT counted. Dialogs close\n * via a native close request only when the Escape keydown goes uncancelled,\n * and a transient layer consuming (cancelling) the keydown is exactly what\n * shields them; counting them would leave an unfocused toast unclosable\n * whenever any modal is open.\n */\nexport function hasEscapeConsumerOutsideStack(): boolean {\n  const stackElements = new Set<Element>();\n  for (const layer of openEscapeLayers) {\n    const element = layer.getElement();\n    if (element !== null) {\n      stackElements.add(element);\n    }\n  }\n  for (const element of document.querySelectorAll(\":popover-open\")) {\n    if (!stackElements.has(element)) {\n      return true;\n    }\n  }\n  return false;\n}\n",
      "path": "packages/heidi-ui/src/_internal/escape-layer.ts",
      "target": "components/ui/heidi/_internal/escape-layer.ts",
      "type": "registry:ui"
    },
    {
      "content": "import type { KeyboardEvent as ReactKeyboardEvent } from \"react\";\n\nconst TABBABLE_SELECTOR = [\n  \"a[href]\",\n  \"area[href]\",\n  \"audio[controls]\",\n  \"button\",\n  \"details > summary:first-of-type\",\n  \"embed\",\n  \"iframe\",\n  \"input\",\n  \"object\",\n  \"select\",\n  \"textarea\",\n  \"video[controls]\",\n  \"[contenteditable]:not([contenteditable='false'])\",\n  \"[tabindex]\"\n].join(\",\");\n\nfunction isActuallyTabbable(element: HTMLElement): boolean {\n  if (\n    element.tabIndex < 0 ||\n    element.closest(\"[hidden], [inert], [aria-hidden='true']\") !== null\n  ) {\n    return false;\n  }\n  if (\n    (element instanceof HTMLButtonElement ||\n      element instanceof HTMLInputElement ||\n      element instanceof HTMLSelectElement ||\n      element instanceof HTMLTextAreaElement) &&\n    (element.disabled || element.matches(\":disabled\"))\n  ) {\n    return false;\n  }\n  if (element instanceof HTMLInputElement && element.type === \"hidden\") {\n    return false;\n  }\n  const style = getComputedStyle(element);\n  return style.display !== \"none\" && style.visibility !== \"hidden\" && element.getClientRects().length > 0;\n}\n\nfunction isActiveRadio(element: HTMLElement, candidates: HTMLElement[]): boolean {\n  if (!(element instanceof HTMLInputElement) || element.type !== \"radio\" || !element.name) {\n    return true;\n  }\n  const group = candidates.filter(\n    (candidate): candidate is HTMLInputElement =>\n      candidate instanceof HTMLInputElement &&\n      candidate.type === \"radio\" &&\n      candidate.name === element.name &&\n      candidate.form === element.form\n  );\n  const checked = group.find((radio) => radio.checked);\n  return checked ? checked === element : group[0] === element;\n}\n\n/** Return the elements reached by sequential keyboard focus in browser order. */\nexport function getHeidiTabbableElements(container: HTMLElement): HTMLElement[] {\n  const candidates = Array.from(\n    container.querySelectorAll<HTMLElement>(TABBABLE_SELECTOR)\n  ).filter(isActuallyTabbable);\n\n  return candidates\n    .filter((element) => isActiveRadio(element, candidates))\n    .map((element, index) => ({ element, index }))\n    .sort((left, right) => {\n      const leftOrder = left.element.tabIndex > 0 ? left.element.tabIndex : Number.MAX_SAFE_INTEGER;\n      const rightOrder = right.element.tabIndex > 0 ? right.element.tabIndex : Number.MAX_SAFE_INTEGER;\n      return leftOrder - rightOrder || left.index - right.index;\n    })\n    .map(({ element }) => element);\n}\n\n/**\n * Restore focus after the browser has settled a top-layer transition.\n *\n * Callers must pass the host captured synchronously from a React event:\n * React clears `SyntheticEvent.currentTarget` after listener dispatch.\n */\nexport function focusHeidiElementNextFrame(element: HTMLElement): void {\n  element.ownerDocument.defaultView?.requestAnimationFrame(() => {\n    if (element.isConnected) {\n      element.focus({ preventScroll: true });\n    }\n  });\n}\n\n/**\n * Keep sequential Tab navigation inside a modal. The native dialog top layer\n * makes the rest of the document inert, but Chromium can still place focus on\n * `body` for one keystroke at either edge; APG requires an immediate wrap.\n */\nexport function containHeidiModalTabFocus(\n  event: ReactKeyboardEvent<HTMLElement>\n): void {\n  if (\n    event.key !== \"Tab\" ||\n    event.defaultPrevented ||\n    event.altKey ||\n    event.ctrlKey ||\n    event.metaKey\n  ) {\n    return;\n  }\n\n  const container = event.currentTarget;\n  const tabbables = getHeidiTabbableElements(container);\n  const active = document.activeElement;\n  const first = tabbables[0];\n  const last = tabbables.at(-1);\n\n  if (!first || !last) {\n    event.preventDefault();\n    container\n      .querySelector<HTMLElement>(\"[data-hui-focus-fallback]\")\n      ?.focus({ preventScroll: true });\n    return;\n  }\n\n  if (event.shiftKey) {\n    if (active === first || !(active instanceof Node) || !container.contains(active)) {\n      event.preventDefault();\n      last.focus();\n    }\n    return;\n  }\n\n  if (active === last || active === container || !(active instanceof Node) || !container.contains(active)) {\n    event.preventDefault();\n    first.focus();\n  }\n}\n",
      "path": "packages/heidi-ui/src/_internal/focus-scope.ts",
      "target": "components/ui/heidi/_internal/focus-scope.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared host rendering for heidi-ui parts — Base-UI-shaped composition\n * (docs/HEIDI-UI-HEADLESS.md § 4 / HEIDI-UI.md § 4.5):\n *   - className / style as value or (state) => value\n *   - render as ReactElement or (props, state) => ReactElement\n *   - ref forwarding\n *   - data-hui-part always set (stable unstyled hook)\n *\n * Structural inline styles (e.g. anchorName) are merged last so a consumer\n * style override cannot drop platform wiring.\n */\n\nimport {\n  cloneElement,\n  createElement,\n  isValidElement,\n  type CSSProperties,\n  type ComponentPropsWithRef,\n  type HTMLAttributes,\n  type ReactElement,\n  type ReactNode,\n  type Ref,\n  type RefCallback\n} from \"react\";\n\nexport type HeidiClassName<State> = string | ((state: State) => string | undefined) | undefined;\n\nexport type HeidiStyle<State> =\n  | CSSProperties\n  | ((state: State) => CSSProperties | undefined)\n  | undefined;\n\nexport type HeidiRenderFn<State, Props> = (\n  props: Props,\n  state: State\n) => ReactElement;\n\nexport type HeidiRender<State, Props> =\n  | ReactElement\n  | HeidiRenderFn<State, Props>\n  | undefined;\n\ntype DefaultRenderProps = HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> };\n\nexport type HeidiHostProps<\n  State,\n  RenderProps extends object = DefaultRenderProps\n> = {\n  className?: HeidiClassName<State>;\n  render?: HeidiRender<State, RenderProps>;\n  style?: HeidiStyle<State>;\n};\n\nexport type HeidiIntrinsicHostProps<\n  State,\n  Tag extends keyof HTMLElementTagNameMap\n> = HeidiHostProps<State, ComponentPropsWithRef<Tag>>;\n\nfunction resolveClassName<State>(\n  className: HeidiClassName<State>,\n  state: State\n): string | undefined {\n  return typeof className === \"function\" ? className(state) : className;\n}\n\nfunction resolveStyle<State>(style: HeidiStyle<State>, state: State): CSSProperties | undefined {\n  return typeof style === \"function\" ? style(state) : style;\n}\n\nfunction mergeClassNames(...parts: Array<string | undefined>): string | undefined {\n  const merged = parts.filter(Boolean).join(\" \");\n  return merged.length > 0 ? merged : undefined;\n}\n\n/**\n * Compose a consumer event with library behavior. Consumers run first and may\n * cancel the component behavior with `event.preventDefault()`. Heidi keeps\n * this native-event convention intentionally: it avoids a second branded event\n * API while still making async/controlled actions vetoable.\n */\nexport function composeHeidiEventHandlers<Event extends { defaultPrevented: boolean }>(\n  consumer: ((event: Event) => void) | undefined,\n  library: (event: Event) => void\n): (event: Event) => void {\n  return (event) => {\n    consumer?.(event);\n    if (!event.defaultPrevented) {\n      library(event);\n    }\n  };\n}\n\n/** React 19-safe ref fan-out, including callback-ref cleanup functions. */\nexport function mergeHeidiRefs<Element>(\n  ...inputRefs: Array<Ref<Element> | undefined>\n): Ref<Element> | undefined {\n  const refs = Array.from(\n    new Set(\n      inputRefs.filter(\n        (ref): ref is Exclude<Ref<Element>, null> => ref != null\n      )\n    )\n  );\n  if (refs.length === 0) {\n    return undefined;\n  }\n  if (refs.length === 1) {\n    return refs[0];\n  }\n\n  let cache = mergedRefCache;\n  for (const ref of refs) {\n    const key = ref as object;\n    let child = cache.children.get(key);\n    if (!child) {\n      child = { children: new WeakMap() };\n      cache.children.set(key, child);\n    }\n    cache = child;\n  }\n  if (cache.callback) {\n    return cache.callback as RefCallback<Element>;\n  }\n\n  const callback: RefCallback<Element> = (node) => {\n    const cleanups: Array<() => void> = [];\n    for (const ref of refs) {\n      if (typeof ref === \"function\") {\n        const cleanup = ref(node);\n        if (node !== null) {\n          cleanups.push(typeof cleanup === \"function\" ? cleanup : () => ref(null));\n        }\n      } else if (ref) {\n        ref.current = node;\n        if (node !== null) {\n          cleanups.push(() => {\n            ref.current = null;\n          });\n        }\n      }\n    }\n    return cleanups.length > 0\n      ? () => {\n          for (const cleanup of cleanups) {\n            cleanup();\n          }\n        }\n      : undefined;\n  };\n  cache.callback = callback as RefCallback<unknown>;\n  return callback;\n}\n\ntype MergedRefCache = {\n  callback?: RefCallback<unknown>;\n  children: WeakMap<object, MergedRefCache>;\n};\n\nconst mergedRefCache: MergedRefCache = { children: new WeakMap() };\n\ntype UnknownHandler = (...args: never[]) => unknown;\n\nfunction isEventHandler(key: string, value: unknown): value is UnknownHandler {\n  return /^on[A-Z]/.test(key) && typeof value === \"function\";\n}\n\nfunction defaultPrevented(args: unknown[]): boolean {\n  const event = args[0];\n  return (\n    typeof event === \"object\" &&\n    event !== null &&\n    \"defaultPrevented\" in event &&\n    event.defaultPrevented === true\n  );\n}\n\nfunction composeUnknownHandlers(\n  consumer: UnknownHandler,\n  library: UnknownHandler\n): UnknownHandler {\n  if (consumer === library) {\n    return library;\n  }\n  return ((...args: unknown[]) => {\n    (consumer as (...handlerArgs: unknown[]) => unknown)(...args);\n    if (!defaultPrevented(args)) {\n      (library as (...handlerArgs: unknown[]) => unknown)(...args);\n    }\n  }) as UnknownHandler;\n}\n\n/** Library-owned host props — permissive so button `type`, `data-*`, `popover`, etc. type-check. */\nexport type HeidiElementProps<Tag extends keyof HTMLElementTagNameMap> = Record<\n  string,\n  unknown\n> & {\n  children?: ReactNode;\n  className?: string;\n  ref?: Ref<HTMLElementTagNameMap[Tag]>;\n  style?: CSSProperties;\n};\n\ntype RenderElementParams<\n  State,\n  Tag extends keyof HTMLElementTagNameMap,\n  RenderProps extends object = DefaultRenderProps\n> = {\n  /** Default class from the generated hui-* map (theme/base target). */\n  className?: string;\n  /** Stable machine hook — always emitted. */\n  dataPart: string;\n  /** Intrinsic tag when `render` is omitted. */\n  element: Tag;\n  /** Props the library owns (aria, ids, handlers, popover, …). */\n  props: HeidiElementProps<Tag>;\n  /** Optional consumer composition props. */\n  renderProps?: HeidiHostProps<State, RenderProps>;\n  /** Typed state passed to functional className/style/render. */\n  state: State;\n  /** Structural inline styles that must survive consumer style merges. */\n  structuralStyle?: CSSProperties;\n  /**\n   * Render-element handlers to remove instead of composing. This is reserved\n   * for states such as a focusable disabled composite item where the public\n   * contract requires press handlers to be completely inert.\n   */\n  suppressRenderedHandlers?: readonly string[];\n};\n\n/**\n * Render a heidi-ui host element with optional Base-shaped composition.\n */\nexport function renderHeidiElement<\n  State,\n  Tag extends keyof HTMLElementTagNameMap,\n  RenderProps extends object = DefaultRenderProps\n>(\n  params: RenderElementParams<State, Tag, RenderProps>\n): ReactElement {\n  const {\n    className,\n    dataPart,\n    element,\n    props,\n    renderProps,\n    state,\n    structuralStyle,\n    suppressRenderedHandlers\n  } = params;\n  const consumerClass = resolveClassName(renderProps?.className, state);\n  const consumerStyle = resolveStyle(renderProps?.style, state);\n  const mergedStyle: CSSProperties | undefined =\n    props.style || consumerStyle || structuralStyle\n      ? { ...props.style, ...consumerStyle, ...structuralStyle }\n      : undefined;\n\n  const outProps: HTMLAttributes<HTMLElement> & {\n    \"data-hui-part\": string;\n    ref?: Ref<HTMLElement>;\n  } = {\n    ...(props as HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> }),\n    className: mergeClassNames(consumerClass, className),\n    \"data-hui-part\": dataPart,\n    style: mergedStyle\n  };\n\n  const render = renderProps?.render;\n  if (typeof render === \"function\") {\n    // Post-merge the returned element as well. This deliberately makes the\n    // stable data hook, owned semantics, refs, and internal handlers survive\n    // even when a render function forgets to spread one of the supplied props.\n    return mergeRenderedElement(\n      render(outProps as unknown as RenderProps, state),\n      outProps,\n      dataPart,\n      structuralStyle,\n      suppressRenderedHandlers\n    );\n  }\n  if (isValidElement(render)) {\n    return mergeRenderedElement(\n      render,\n      outProps,\n      dataPart,\n      structuralStyle,\n      suppressRenderedHandlers\n    );\n  }\n\n  return createElement(element, outProps as never, props.children);\n}\n\ntype RenderedProps = Record<string, unknown> & {\n  children?: ReactNode;\n  className?: string;\n  ref?: Ref<HTMLElement>;\n  style?: CSSProperties;\n};\n\nfunction mergeRenderedElement(\n  element: ReactElement,\n  libraryProps: HTMLAttributes<HTMLElement> & {\n    \"data-hui-part\": string;\n    ref?: Ref<HTMLElement>;\n  },\n  dataPart: string,\n  structuralStyle: CSSProperties | undefined,\n  suppressRenderedHandlers: readonly string[] | undefined\n): ReactElement {\n  const rendered = element as ReactElement<RenderedProps>;\n  const renderedProps = rendered.props;\n  const renderedClass =\n    renderedProps.className === libraryProps.className\n      ? undefined\n      : renderedProps.className;\n  const merged: RenderedProps = {\n    ...renderedProps,\n    ...libraryProps,\n    className: mergeClassNames(renderedClass, libraryProps.className),\n    \"data-hui-part\": dataPart,\n    ref: mergeHeidiRefs(libraryProps.ref, renderedProps.ref),\n    style: {\n      ...libraryProps.style,\n      ...renderedProps.style,\n      ...structuralStyle\n    }\n  };\n\n  if (Object.prototype.hasOwnProperty.call(renderedProps, \"children\")) {\n    merged.children = renderedProps.children;\n  }\n\n  const suppressedHandlers = suppressRenderedHandlers\n    ? new Set(suppressRenderedHandlers)\n    : null;\n  for (const key of new Set([...Object.keys(renderedProps), ...Object.keys(libraryProps)])) {\n    const consumer = renderedProps[key];\n    const library = (libraryProps as unknown as Record<string, unknown>)[key];\n    if (suppressedHandlers?.has(key)) {\n      merged[key] = library;\n      continue;\n    }\n    if (isEventHandler(key, consumer) && isEventHandler(key, library)) {\n      merged[key] = composeUnknownHandlers(consumer, library);\n    }\n  }\n\n  return cloneElement(rendered, merged as never);\n}\n",
      "path": "packages/heidi-ui/src/_internal/render-element.ts",
      "target": "components/ui/heidi/_internal/render-element.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared id sanitizer for heidi-ui hosts.\n *\n * React's useId() emits characters that are invalid in HTML id / attribute\n * selectors and in CSS identifiers (`«r0»` in React 19, `:r0:` earlier).\n * heidi-ui interpolates these ids into element ids, aria relationships, and\n * dashed-ident anchor names (`--hui-*-anchor-…`), so everything outside\n * [a-zA-Z0-9_-] is stripped.\n */\n\nexport function safeId(id: string): string {\n  return id.replace(/[^a-zA-Z0-9_-]/g, \"\");\n}\n",
      "path": "packages/heidi-ui/src/_internal/safe-id.ts",
      "target": "components/ui/heidi/_internal/safe-id.ts",
      "type": "registry:ui"
    },
    {
      "content": "export type SafeTrianglePoint = {\n  x: number;\n  y: number;\n};\n\ntype SafeTriangleRect = Pick<DOMRect, \"bottom\" | \"left\" | \"right\" | \"top\">;\n\nexport function pointInTriangle(\n  point: SafeTrianglePoint,\n  first: SafeTrianglePoint,\n  second: SafeTrianglePoint,\n  third: SafeTrianglePoint\n): boolean {\n  const sign = (\n    value: SafeTrianglePoint,\n    edgeStart: SafeTrianglePoint,\n    edgeEnd: SafeTrianglePoint\n  ) =>\n    (value.x - edgeEnd.x) * (edgeStart.y - edgeEnd.y) -\n    (edgeStart.x - edgeEnd.x) * (value.y - edgeEnd.y);\n  const firstSign = sign(point, first, second);\n  const secondSign = sign(point, second, third);\n  const thirdSign = sign(point, third, first);\n  const hasNegative =\n    firstSign < 0 || secondSign < 0 || thirdSign < 0;\n  const hasPositive =\n    firstSign > 0 || secondSign > 0 || thirdSign > 0;\n  return !(hasNegative && hasPositive);\n}\n\n/**\n * Return the padded destination edge facing the pointer's departure point.\n *\n * ponytail: choosing the edge at the smallest scalar distance is a different\n * question and fails for an ordinary top-aligned submenu. A pointer leaving\n * below its trigger can be numerically closer to the submenu's top than its\n * left, even though it approaches from the left. That puts the whole bridge\n * above the diagonal and drops pointer grace on the first move. Containment\n * chooses the approach axis first; horizontal wins because submenus open\n * beside their triggers, with vertical as the collision-flip fallback.\n */\nexport function pointerBridgeEdge(\n  rect: SafeTriangleRect,\n  start: SafeTrianglePoint\n): [SafeTrianglePoint, SafeTrianglePoint] {\n  const padding = 8;\n  if (start.x <= rect.left) {\n    return [\n      { x: rect.left, y: rect.top - padding },\n      { x: rect.left, y: rect.bottom + padding }\n    ];\n  }\n  if (start.x >= rect.right) {\n    return [\n      { x: rect.right, y: rect.top - padding },\n      { x: rect.right, y: rect.bottom + padding }\n    ];\n  }\n  if (start.y <= rect.top) {\n    return [\n      { x: rect.left - padding, y: rect.top },\n      { x: rect.right + padding, y: rect.top }\n    ];\n  }\n  return [\n    { x: rect.left - padding, y: rect.bottom },\n    { x: rect.right + padding, y: rect.bottom }\n  ];\n}\n",
      "path": "packages/heidi-ui/src/_internal/safe-triangle.ts",
      "target": "components/ui/heidi/_internal/safe-triangle.ts",
      "type": "registry:ui"
    },
    {
      "content": "import { useEffect, useLayoutEffect } from \"react\";\n\n/**\n * `useLayoutEffect` that does not warn on the server.\n *\n * ponytail: fifteen primitives imported `useLayoutEffect` straight from React.\n * Every one of them carries `\"use client\"`, which is easy to misread as\n * \"client-only\" — in the App Router it means \"hydrated on the client\", and the\n * component is still rendered to HTML on the server first. React logs\n * \"useLayoutEffect does nothing on the server\" for each one, so a consumer\n * doing SSR saw a wall of warnings the app that ships this library never saw,\n * because it renders these routes on the client path.\n *\n * The branch is evaluated ONCE at module scope, not per render, and it is\n * therefore stable across a component's lifetime — swapping which hook is\n * called between renders would violate the rules of hooks. `typeof document`\n * rather than `typeof window`: both work, but `document` is the thing the\n * effect actually needs, and it keeps the check honest in exotic runtimes that\n * define a partial `window`.\n *\n * Rejected: `useInsertionEffect`, which runs earlier but is specified for\n * style injection and is not a general layout hook; and per-file guards, which\n * is what the 15 copies would have become.\n */\nexport const useHeidiLayoutEffect =\n  typeof document === \"undefined\" ? useEffect : useLayoutEffect;\n",
      "path": "packages/heidi-ui/src/_internal/use-layout-effect.ts",
      "target": "components/ui/heidi/_internal/use-layout-effect.ts",
      "type": "registry:ui"
    }
  ],
  "name": "hover-card",
  "registryDependencies": [
    "@heidi-ui/heidi-tokens"
  ],
  "title": "Hover-card",
  "type": "registry:ui"
}
