{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "dependencies": [],
  "description": "APG Disclosure Navigation Menu (NOT menubar/menu roles — site nav does not need menu widget semantics). Landmark <nav> with a horizontal list of disclosure triggers and/or links. Submenus use [popover=auto] + CSS anchors + light dismiss (exclusive open is free). Optional Arrow/Home/End among top-level controls; Escape closes the open panel and returns focus to its trigger. Controlled value = which item's panel is open (null when closed). Deferred: hover-open, Viewport/Indicator, nested submenus, vertical orientation.",
  "files": [
    {
      "content": "\"use client\";\n\n/**\n * heidi-ui NavigationMenu — APG Disclosure Navigation Menu\n * (docs/HEIDI-UI-HEADLESS.md § P4 / Slice G). Site navigation landmark with\n * disclosure buttons and/or links. Submenus use the popover shell\n * ([popover=auto] + CSS anchors + light dismiss). Deliberately NOT\n * role=menubar/menu — APG reserves those for application menus; site nav\n * uses disclosures + links so screen readers stay in browse mode.\n *\n * Keyboard (APG + optional arrows): Tab through controls; Enter/Space toggles\n * a disclosure; Escape closes and returns focus to the trigger; ArrowLeft/\n * ArrowRight/Home/End move among top-level triggers/links; ArrowDown on an\n * open trigger focuses the first link in its panel.\n *\n * Controlled: `value` / `defaultValue` / `onValueChange` — which item's panel\n * is open (`null` when closed). Composition via renderHeidiElement.\n *\n * Deferred: hover-open, Viewport/Indicator, nested submenus, vertical.\n *\n * RSC rule: named exports from Server Components; NavigationMenu.X is client sugar.\n */\n\nimport {\n  type ComponentPropsWithoutRef,\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n  type CSSProperties,\n  type KeyboardEvent,\n  type ReactNode,\n  type Ref,\n  type ToggleEvent\n} from \"react\";\nimport {\n  composeHeidiEventHandlers,\n  mergeHeidiRefs,\n  renderHeidiElement,\n  type HeidiIntrinsicHostProps\n} from \"../_internal/render-element\";\nimport { synchronizeNativePopoverOpen } from \"../_internal/native-popover-sync\";\nimport { safeId } from \"../_internal/safe-id\";\nimport {\n  type NavigationMenuAlign,\n  type NavigationMenuSide\n} from \"./navigation-menu.anatomy.generated\";\nimport { NAVIGATION_MENU_CLASSES } from \"./navigation-menu.classes.generated\";\n\nexport type { NavigationMenuAlign, NavigationMenuSide };\n\ntype NavigationMenuRootContextValue = {\n  label: string;\n  openValue: string | null;\n  openValueRef: { current: string | null };\n  rootId: string;\n  setOpenValue: (value: string | null) => void;\n};\n\ntype NavigationMenuItemContextValue = {\n  anchorName: string;\n  contentId: string;\n  open: boolean;\n  triggerId: string;\n  value: string;\n};\n\nconst NavigationMenuRootContext = createContext<NavigationMenuRootContextValue | null>(\n  null\n);\nconst NavigationMenuItemContext = createContext<NavigationMenuItemContextValue | null>(\n  null\n);\n\nfunction useNavigationMenuRoot(part: string): NavigationMenuRootContextValue {\n  const context = useContext(NavigationMenuRootContext);\n  if (!context) {\n    throw new Error(`NavigationMenu.${part} must be rendered inside NavigationMenu.Root.`);\n  }\n  return context;\n}\n\nfunction useNavigationMenuItem(part: string): NavigationMenuItemContextValue {\n  const context = useContext(NavigationMenuItemContext);\n  if (!context) {\n    throw new Error(`NavigationMenu.${part} must be rendered inside NavigationMenu.Item.`);\n  }\n  return context;\n}\n\nfunction dataState(open: boolean): \"open\" | \"closed\" {\n  return open ? \"open\" : \"closed\";\n}\n\nfunction topLevelControls(list: HTMLElement): HTMLElement[] {\n  return [\n    ...list.querySelectorAll<HTMLElement>(\n      ':scope > [data-hui-part=\"navigation-menu-item\"] > [data-hui-part=\"navigation-menu-trigger\"], :scope > [data-hui-part=\"navigation-menu-item\"] > [data-hui-part=\"navigation-menu-link\"]'\n    )\n  ];\n}\n\nfunction contentLinks(content: HTMLElement): HTMLAnchorElement[] {\n  return [...content.querySelectorAll<HTMLAnchorElement>('a[data-hui-part=\"navigation-menu-link\"]')];\n}\n\nexport type NavigationMenuRootState = Record<string, never>;\n\ntype NavigationMenuRootNativeProps = Omit<\n  ComponentPropsWithoutRef<\"nav\">,\n  | \"aria-label\"\n  | \"children\"\n  | \"className\"\n  | \"defaultValue\"\n  | \"ref\"\n  | \"style\"\n  | \"value\"\n>;\n\nexport type NavigationMenuRootProps = HeidiIntrinsicHostProps<\n  NavigationMenuRootState,\n  \"nav\"\n> &\n  NavigationMenuRootNativeProps & {\n    children?: ReactNode;\n    /** Uncontrolled initially-open item value (`null` = all closed). */\n    defaultValue?: string | null;\n    /** Accessible name for the nav landmark (required). */\n    label: string;\n    onValueChange?: (value: string | null) => void;\n    ref?: Ref<HTMLElement>;\n    /** Controlled open item value (`null` = all closed). */\n    value?: string | null;\n  };\n\nexport function NavigationMenuRoot({\n  children,\n  className,\n  defaultValue = null,\n  label,\n  onValueChange,\n  ref,\n  render,\n  style,\n  value,\n  ...nativeProps\n}: NavigationMenuRootProps) {\n  const id = useId();\n  const rootId = `hui-navigation-menu-${safeId(id)}`;\n  const [internal, setInternal] = useState<string | null>(defaultValue);\n  const controlled = value !== undefined;\n  const openValue = controlled ? value : internal;\n  const openValueRef = useRef<string | null>(openValue);\n  openValueRef.current = openValue;\n\n  const setOpenValue = useCallback(\n    (next: string | null) => {\n      if (!controlled) {\n        openValueRef.current = next;\n        setInternal(next);\n      }\n      onValueChange?.(next);\n    },\n    [controlled, onValueChange]\n  );\n\n  // Escape closes the open panel when focus is inside the nav (APG / WCAG 1.4.13).\n  useEffect(() => {\n    if (!openValue) {\n      return;\n    }\n    const onKeyDown = (event: globalThis.KeyboardEvent) => {\n      if (event.key !== \"Escape\") {\n        return;\n      }\n      const root = document.getElementById(rootId);\n      if (!root || !root.contains(document.activeElement)) {\n        return;\n      }\n      const openContent = root.querySelector<HTMLElement>(\n        '[data-hui-part=\"navigation-menu-content\"]:popover-open'\n      );\n      if (!openContent) {\n        setOpenValue(null);\n        return;\n      }\n      event.preventDefault();\n      const triggerId = openContent.getAttribute(\"aria-labelledby\");\n      setOpenValue(null);\n      if (triggerId) {\n        requestAnimationFrame(() => {\n          requestAnimationFrame(() => {\n            if (!openContent.matches(\":popover-open\")) {\n              document.getElementById(triggerId)?.focus();\n            }\n          });\n        });\n      }\n    };\n    window.addEventListener(\"keydown\", onKeyDown);\n    return () => window.removeEventListener(\"keydown\", onKeyDown);\n  }, [openValue, rootId, setOpenValue]);\n\n  const context = useMemo(\n    () => ({ label, openValue, openValueRef, rootId, setOpenValue }),\n    [label, openValue, rootId, setOpenValue]\n  );\n\n  return (\n    <NavigationMenuRootContext value={context}>\n      {renderHeidiElement({\n        className: NAVIGATION_MENU_CLASSES.root,\n        dataPart: \"navigation-menu-root\",\n        element: \"nav\",\n        props: {\n          ...nativeProps,\n          \"aria-label\": label,\n          children,\n          id: rootId,\n          ref\n        },\n        renderProps: { className, render, style },\n        state: {}\n      })}\n    </NavigationMenuRootContext>\n  );\n}\n\nexport type NavigationMenuListState = Record<string, never>;\n\ntype NavigationMenuListNativeProps = Omit<\n  ComponentPropsWithoutRef<\"ul\">,\n  \"children\" | \"className\" | \"onKeyDown\" | \"ref\" | \"style\"\n>;\n\nexport type NavigationMenuListProps = HeidiIntrinsicHostProps<\n  NavigationMenuListState,\n  \"ul\"\n> &\n  NavigationMenuListNativeProps & {\n    children?: ReactNode;\n    onKeyDown?: ComponentPropsWithoutRef<\"ul\">[\"onKeyDown\"];\n    ref?: Ref<HTMLUListElement>;\n  };\n\nexport function NavigationMenuList({\n  children,\n  className,\n  onKeyDown: onKeyDownProp,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: NavigationMenuListProps) {\n  useNavigationMenuRoot(\"List\");\n\n  const onKeyDown = composeHeidiEventHandlers(\n    onKeyDownProp,\n    (event: KeyboardEvent<HTMLUListElement>) => {\n    const keys = [\"ArrowDown\", \"ArrowLeft\", \"ArrowRight\", \"End\", \"Home\"];\n    if (!keys.includes(event.key)) {\n      return;\n    }\n    const controls = topLevelControls(event.currentTarget);\n    if (controls.length === 0) {\n      return;\n    }\n    const active = document.activeElement as HTMLElement | null;\n    const current = active ? controls.indexOf(active) : -1;\n\n    if (event.key === \"ArrowDown\" && active?.getAttribute(\"data-hui-part\") === \"navigation-menu-trigger\") {\n      const controlsId = active.getAttribute(\"aria-controls\");\n      const content = controlsId ? document.getElementById(controlsId) : null;\n      if (content?.matches(\":popover-open\")) {\n        event.preventDefault();\n        contentLinks(content)[0]?.focus();\n      }\n      return;\n    }\n\n    if (event.key === \"ArrowDown\") {\n      return;\n    }\n\n    // Top-layer panel content remains a descendant of the authored list.\n    // Ignore bubbled navigation keys unless focus is on a top-level control.\n    if (current === -1) {\n      return;\n    }\n\n    event.preventDefault();\n    const rtl = getComputedStyle(event.currentTarget).direction === \"rtl\";\n    const backwardKey = rtl ? \"ArrowRight\" : \"ArrowLeft\";\n    const next =\n      event.key === \"Home\"\n        ? 0\n        : event.key === \"End\"\n          ? controls.length - 1\n          : event.key === backwardKey\n            ? (current <= 0 ? controls.length : current) - 1\n            : (current + 1) % controls.length;\n    controls[next]?.focus();\n    }\n  );\n\n  return renderHeidiElement({\n    className: NAVIGATION_MENU_CLASSES.list,\n    dataPart: \"navigation-menu-list\",\n    element: \"ul\",\n    props: { ...nativeProps, children, onKeyDown, ref },\n    renderProps: { className, render, style },\n    state: {}\n  });\n}\n\nexport type NavigationMenuItemState = { open: boolean };\n\ntype NavigationMenuItemNativeProps = Omit<\n  ComponentPropsWithoutRef<\"li\">,\n  \"children\" | \"className\" | \"ref\" | \"style\" | \"value\"\n>;\n\nexport type NavigationMenuItemProps = HeidiIntrinsicHostProps<\n  NavigationMenuItemState,\n  \"li\"\n> &\n  NavigationMenuItemNativeProps & {\n    children?: ReactNode;\n    ref?: Ref<HTMLLIElement>;\n    /**\n     * Stable id for this item's disclosure panel. Required when the item has a\n     * Trigger/Content pair; optional for link-only items.\n     */\n    value?: string;\n  };\n\nexport function NavigationMenuItem({\n  children,\n  className,\n  ref,\n  render,\n  style,\n  value = \"\",\n  ...nativeProps\n}: NavigationMenuItemProps) {\n  const { openValue, rootId } = useNavigationMenuRoot(\"Item\");\n  const open = value !== \"\" && openValue === value;\n  const safe = safeId(`${rootId}-${value || \"link\"}`);\n\n  const itemContext = useMemo(\n    () => ({\n      anchorName: `--hui-navigation-menu-anchor-${safe}`,\n      contentId: `hui-navigation-menu-content-${safe}`,\n      open,\n      triggerId: `hui-navigation-menu-trigger-${safe}`,\n      value\n    }),\n    [open, safe, value]\n  );\n\n  return (\n    <NavigationMenuItemContext value={itemContext}>\n      {renderHeidiElement({\n        className: NAVIGATION_MENU_CLASSES.item,\n        dataPart: \"navigation-menu-item\",\n        element: \"li\",\n        props: {\n          ...nativeProps,\n          children,\n          \"data-state\": value ? dataState(open) : undefined,\n          ref\n        },\n        renderProps: { className, render, style },\n        state: { open }\n      })}\n    </NavigationMenuItemContext>\n  );\n}\n\nexport type NavigationMenuTriggerState = { open: boolean };\n\ntype NavigationMenuTriggerNativeProps = Omit<\n  ComponentPropsWithoutRef<\"button\">,\n  | \"aria-controls\"\n  | \"aria-expanded\"\n  | \"children\"\n  | \"className\"\n  | \"id\"\n  | \"onKeyDown\"\n  | \"popoverTarget\"\n  | \"ref\"\n  | \"style\"\n  | \"type\"\n>;\n\nexport type NavigationMenuTriggerProps = HeidiIntrinsicHostProps<\n  NavigationMenuTriggerState,\n  \"button\"\n> &\n  NavigationMenuTriggerNativeProps & {\n    children?: ReactNode;\n    onKeyDown?: ComponentPropsWithoutRef<\"button\">[\"onKeyDown\"];\n    ref?: Ref<HTMLButtonElement>;\n  };\n\nexport function NavigationMenuTrigger({\n  children,\n  className,\n  onKeyDown,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: NavigationMenuTriggerProps) {\n  const { anchorName, contentId, open, triggerId, value } = useNavigationMenuItem(\"Trigger\");\n  const { setOpenValue } = useNavigationMenuRoot(\"Trigger\");\n\n  if (!value) {\n    throw new Error(\"NavigationMenu.Trigger requires NavigationMenu.Item to have a `value`.\");\n  }\n\n  return renderHeidiElement({\n    className: NAVIGATION_MENU_CLASSES.trigger,\n    dataPart: \"navigation-menu-trigger\",\n    element: \"button\",\n    props: {\n      ...nativeProps,\n      \"aria-controls\": contentId,\n      \"aria-expanded\": open,\n      children,\n      \"data-state\": dataState(open),\n      id: triggerId,\n      onKeyDown: composeHeidiEventHandlers(\n        onKeyDown,\n        (event: KeyboardEvent<HTMLButtonElement>) => {\n          if (event.key === \"ArrowDown\") {\n            event.preventDefault();\n            const content = document.getElementById(contentId);\n            if (!content?.matches(\":popover-open\")) {\n              setOpenValue(value);\n            }\n            requestAnimationFrame(() => {\n              requestAnimationFrame(() => {\n                const panel = document.getElementById(contentId);\n                if (panel?.matches(\":popover-open\")) {\n                  contentLinks(panel)[0]?.focus();\n                }\n              });\n            });\n          }\n        }\n      ),\n      popoverTarget: contentId,\n      ref,\n      type: \"button\"\n    },\n    renderProps: { className, render, style },\n    state: { open },\n    structuralStyle: { anchorName } as CSSProperties\n  });\n}\n\nexport type NavigationMenuContentState = {\n  align: NavigationMenuAlign;\n  open: boolean;\n  side: NavigationMenuSide;\n};\n\ntype NavigationMenuContentNativeProps = Omit<\n  ComponentPropsWithoutRef<\"div\">,\n  | \"aria-labelledby\"\n  | \"children\"\n  | \"className\"\n  | \"id\"\n  | \"onBeforeToggle\"\n  | \"onToggle\"\n  | \"popover\"\n  | \"ref\"\n  | \"role\"\n  | \"style\"\n>;\n\nexport type NavigationMenuContentProps = HeidiIntrinsicHostProps<\n  NavigationMenuContentState,\n  \"div\"\n> &\n  NavigationMenuContentNativeProps & {\n    align?: NavigationMenuAlign;\n    /** Cross-axis offset in CSS pixels. */\n    alignOffset?: number;\n    children?: ReactNode;\n    onBeforeToggle?: ComponentPropsWithoutRef<\"div\">[\"onBeforeToggle\"];\n    onToggle?: ComponentPropsWithoutRef<\"div\">[\"onToggle\"];\n    ref?: Ref<HTMLDivElement>;\n    side?: NavigationMenuSide;\n    /** Main-axis gap in CSS pixels. */\n    sideOffset?: number;\n  };\n\nexport function NavigationMenuContent({\n  align = \"start\",\n  alignOffset = 0,\n  children,\n  className,\n  onBeforeToggle,\n  onToggle,\n  ref,\n  render,\n  side = \"bottom\",\n  sideOffset = 4,\n  style,\n  ...nativeProps\n}: NavigationMenuContentProps) {\n  const { anchorName, contentId, open, triggerId, value } = useNavigationMenuItem(\"Content\");\n  const { openValueRef, setOpenValue } = useNavigationMenuRoot(\"Content\");\n  const contentRef = useRef<HTMLDivElement | null>(null);\n  const internalNativeTransitionRef = useRef(false);\n  const lastBeforeToggleRef = useRef<{\n    internal: boolean;\n    open: boolean;\n  } | null>(null);\n  const [nativeTransitionVersion, setNativeTransitionVersion] = useState(0);\n\n  if (!value) {\n    throw new Error(\"NavigationMenu.Content requires NavigationMenu.Item to have a `value`.\");\n  }\n\n  const synchronizeNativeOpen = useCallback(\n    (element: HTMLDivElement, next: boolean) => {\n      synchronizeNativePopoverOpen(element, next, {\n        transitionRef: internalNativeTransitionRef\n      });\n    },\n    []\n  );\n\n  const handleBeforeToggle = (event: ToggleEvent<HTMLDivElement>) => {\n    onBeforeToggle?.(event);\n    if (event.defaultPrevented) {\n      return;\n    }\n    if (event.target !== event.currentTarget) {\n      return;\n    }\n    const nativeToggle = event.nativeEvent as Event & {\n      newState?: string;\n    };\n    lastBeforeToggleRef.current = {\n      internal: internalNativeTransitionRef.current,\n      open: nativeToggle.newState === \"open\"\n    };\n  };\n\n  const handleToggle = (event: ToggleEvent<HTMLDivElement>) => {\n    if (event.target !== event.currentTarget) {\n      return;\n    }\n    onToggle?.(event);\n    const nativeToggle = event.nativeEvent as unknown as { newState?: string };\n    const isOpen = nativeToggle.newState === \"open\";\n    const transition = lastBeforeToggleRef.current;\n    lastBeforeToggleRef.current = null;\n    const internallySynchronized =\n      transition?.internal === true && transition.open === isOpen;\n    if (internallySynchronized) {\n      return;\n    }\n    if (isOpen) {\n      setOpenValue(value);\n    } else if (openValueRef.current === value) {\n      // Only clear if this panel was the open one (another may have taken over).\n      setOpenValue(null);\n    }\n    setNativeTransitionVersion((version) => version + 1);\n  };\n\n  // Sync controlled/default value → native popover.\n  // ponytail: defer showPopover one frame so the popover attribute is\n  // connected after hydration (defaultValue / controlled external open).\n  useEffect(() => {\n    const el = contentRef.current;\n    if (!el || typeof el.showPopover !== \"function\") {\n      return;\n    }\n    const isOpen = el.matches(\":popover-open\");\n    if (open && !isOpen) {\n      const frame = window.requestAnimationFrame(() => {\n        synchronizeNativeOpen(el, true);\n      });\n      return () => window.cancelAnimationFrame(frame);\n    }\n    if (!open && isOpen) {\n      synchronizeNativeOpen(el, false);\n    }\n    return undefined;\n  }, [nativeTransitionVersion, open, synchronizeNativeOpen]);\n\n  return renderHeidiElement({\n    className: NAVIGATION_MENU_CLASSES.content,\n    dataPart: \"navigation-menu-content\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      \"aria-labelledby\": triggerId,\n      children,\n      \"data-align\": align,\n      \"data-side\": side,\n      \"data-state\": dataState(open),\n      id: contentId,\n      onBeforeToggle: handleBeforeToggle,\n      onToggle: handleToggle,\n      popover: \"auto\",\n      ref: mergeHeidiRefs(contentRef, ref),\n      role: \"region\"\n    },\n    renderProps: { className, render, style },\n    state: { align, open, side },\n    structuralStyle: {\n      \"--_hui-navigation-menu-align-offset\": `${alignOffset}px`,\n      \"--_hui-navigation-menu-side-offset\": `${Math.max(0, sideOffset)}px`,\n      positionAnchor: anchorName\n    } as CSSProperties\n  });\n}\n\nexport type NavigationMenuLinkState = { active: boolean };\n\ntype NavigationMenuLinkNativeProps = Omit<\n  ComponentPropsWithoutRef<\"a\">,\n  | \"aria-current\"\n  | \"children\"\n  | \"className\"\n  | \"href\"\n  | \"ref\"\n  | \"style\"\n>;\n\nexport type NavigationMenuLinkProps = HeidiIntrinsicHostProps<\n  NavigationMenuLinkState,\n  \"a\"\n> &\n  NavigationMenuLinkNativeProps & {\n    /** When true, sets aria-current=\"page\". */\n    active?: boolean;\n    children?: ReactNode;\n    href?: string;\n    ref?: Ref<HTMLAnchorElement>;\n  };\n\nexport function NavigationMenuLink({\n  active = false,\n  children,\n  className,\n  href = \"#\",\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: NavigationMenuLinkProps) {\n  return renderHeidiElement({\n    className: NAVIGATION_MENU_CLASSES.link,\n    dataPart: \"navigation-menu-link\",\n    element: \"a\",\n    props: {\n      ...nativeProps,\n      \"aria-current\": active ? \"page\" : undefined,\n      children,\n      \"data-active\": active ? \"true\" : \"false\",\n      href,\n      ref\n    },\n    renderProps: { className, render, style },\n    state: { active }\n  });\n}\n\nexport const NavigationMenu = {\n  Content: NavigationMenuContent,\n  Item: NavigationMenuItem,\n  Link: NavigationMenuLink,\n  List: NavigationMenuList,\n  Root: NavigationMenuRoot,\n  Trigger: NavigationMenuTrigger\n} as const;\n",
      "path": "packages/heidi-ui/src/navigation-menu/navigation-menu.tsx",
      "target": "components/ui/heidi/navigation-menu/navigation-menu.tsx",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui navigation-menu — STRUCTURAL CSS only. No --hui-*.\n * Horizontal disclosure nav + popover panels (APG Disclosure Navigation).\n */\n\n@layer heidi-ui-base {\n  .hui-navigation-menu-root {\n    box-sizing: border-box;\n    display: block;\n    max-inline-size: 100%;\n    min-inline-size: 0;\n  }\n\n  .hui-navigation-menu-list {\n    display: flex;\n    gap: 0;\n    list-style: none;\n    margin: 0;\n    max-inline-size: 100%;\n    min-inline-size: 0;\n    overflow-x: auto;\n    overscroll-behavior-x: contain;\n    padding: 0;\n  }\n\n  .hui-navigation-menu-item {\n    display: list-item;\n    flex: 0 0 auto;\n    list-style: none;\n    min-inline-size: 0;\n  }\n\n  .hui-navigation-menu-item[data-state=\"closed\"],\n  .hui-navigation-menu-item[data-state=\"open\"] {\n    display: list-item;\n  }\n\n  .hui-navigation-menu-trigger[data-state=\"closed\"],\n  .hui-navigation-menu-trigger[data-state=\"open\"] {\n    cursor: pointer;\n  }\n\n  .hui-navigation-menu-link[data-active=\"false\"],\n  .hui-navigation-menu-link[data-active=\"true\"] {\n    text-decoration: none;\n  }\n\n  .hui-navigation-menu-content {\n    --_hui-navigation-menu-align-offset: 0px;\n    --_hui-navigation-menu-side-offset: 4px;\n\n    box-sizing: border-box;\n    inset: auto;\n    /* main-axis gap between trigger and panel; sideOffset writes this var */\n    margin: var(--_hui-navigation-menu-side-offset);\n    max-block-size: calc(100dvb - 1rem);\n    max-inline-size: calc(100dvi - 1rem);\n    min-inline-size: 0;\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-navigation-menu-content[data-state=\"closed\"] {\n    display: none !important;\n  }\n\n  .hui-navigation-menu-content[data-state=\"open\"] {\n    display: block;\n  }\n\n  .hui-navigation-menu-trigger,\n  .hui-navigation-menu-link {\n    box-sizing: border-box;\n    max-inline-size: 100%;\n    min-inline-size: 0;\n    overflow-wrap: anywhere;\n  }\n\n  /* Cross-axis nudge (alignOffset) runs along the side's perpendicular axis.\n     Keyed on data-side because navigation-menu places left/right through the\n     LOGICAL inline-start/inline-end position-areas below — the cross axis is\n     the same under either direction. `translate` rather than `transform` so\n     the theme's scale animation stays independent of the nudge. */\n  .hui-navigation-menu-content[data-side=\"bottom\"],\n  .hui-navigation-menu-content[data-side=\"top\"] {\n    translate: var(--_hui-navigation-menu-align-offset) 0;\n  }\n\n  .hui-navigation-menu-content[data-side=\"left\"],\n  .hui-navigation-menu-content[data-side=\"right\"] {\n    translate: 0 var(--_hui-navigation-menu-align-offset);\n  }\n\n  .hui-navigation-menu-content[data-side=\"top\"][data-align=\"start\"] {\n    position-area: block-start span-inline-end;\n  }\n  .hui-navigation-menu-content[data-side=\"top\"][data-align=\"center\"] {\n    position-area: block-start;\n  }\n  .hui-navigation-menu-content[data-side=\"top\"][data-align=\"end\"] {\n    position-area: block-start span-inline-start;\n  }\n  .hui-navigation-menu-content[data-side=\"bottom\"][data-align=\"start\"] {\n    position-area: block-end span-inline-end;\n  }\n  .hui-navigation-menu-content[data-side=\"bottom\"][data-align=\"center\"] {\n    position-area: block-end;\n  }\n  .hui-navigation-menu-content[data-side=\"bottom\"][data-align=\"end\"] {\n    position-area: block-end span-inline-start;\n  }\n  .hui-navigation-menu-content[data-side=\"left\"][data-align=\"start\"] {\n    position-area: inline-start span-block-end;\n  }\n  .hui-navigation-menu-content[data-side=\"left\"][data-align=\"center\"] {\n    position-area: inline-start;\n  }\n  .hui-navigation-menu-content[data-side=\"left\"][data-align=\"end\"] {\n    position-area: inline-start span-block-start;\n  }\n  .hui-navigation-menu-content[data-side=\"right\"][data-align=\"start\"] {\n    position-area: inline-end span-block-end;\n  }\n  .hui-navigation-menu-content[data-side=\"right\"][data-align=\"center\"] {\n    position-area: inline-end;\n  }\n  .hui-navigation-menu-content[data-side=\"right\"][data-align=\"end\"] {\n    position-area: inline-end span-block-start;\n  }\n}\n",
      "path": "packages/heidi-ui/src/navigation-menu/navigation-menu.base.css",
      "target": "components/ui/heidi/navigation-menu/navigation-menu.base.css",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui navigation-menu — VISUAL theme (opt-in). Consumes --hui-*.\n */\n\n@layer heidi-ui {\n  .hui-navigation-menu-list {\n    gap: var(--hui-space-1);\n  }\n\n  .hui-navigation-menu-trigger,\n  .hui-navigation-menu-link {\n    align-items: center;\n    background: transparent;\n    border: none;\n    border-radius: var(--hui-radius-md);\n    color: var(--hui-color-fg-default);\n    cursor: pointer;\n    display: inline-flex;\n    font: inherit;\n    padding: var(--hui-space-1-5) var(--hui-space-3);\n    text-decoration: none;\n  }\n\n  .hui-navigation-menu-trigger:hover,\n  .hui-navigation-menu-link:hover {\n    background: var(--hui-color-interactive-ghost-bg-hover);\n  }\n\n  .hui-navigation-menu-trigger:focus-visible,\n  .hui-navigation-menu-link: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-navigation-menu-trigger[data-state=\"open\"],\n  .hui-navigation-menu-link[data-active=\"true\"] {\n    background: var(--hui-color-interactive-ghost-bg-hover);\n    color: var(--hui-color-fg-strong);\n  }\n\n  .hui-navigation-menu-trigger::after {\n    content: \"\";\n    display: inline-block;\n    margin-inline-start: var(--hui-space-1-5);\n    /* High-contrast caret via borders (APG disclosure pattern). */\n    border-block-start: 0.35em solid currentColor;\n    border-inline: 0.3em solid transparent;\n    transition: transform var(--hui-duration-fast) var(--hui-ease-out);\n    vertical-align: 0.15em;\n  }\n\n  .hui-navigation-menu-trigger[data-state=\"open\"]::after {\n    transform: rotate(180deg);\n  }\n\n  /* ponytail: no `margin-block-start` here. The trigger↔panel gap is the\n     structural sideOffset (--_hui-navigation-menu-side-offset, default 4px =\n     space-1) in navigation-menu.base.css. Rejected: keeping the one-sided\n     theme margin — it sat in a later layer, so it both pinned the block-start\n     gap against the sideOffset prop AND only produced a gap for the default\n     side=\"bottom\"; a side=\"top\" or side=\"left\" panel got no gap at all.\n     The replacement is a margin on ALL four sides, matching popover/tooltip/\n     hover-card: `position-try-fallbacks` can flip the used side and CSS has no\n     way to select the fallback that won, so the only gap that survives a flip\n     is one that exists on every side. The visible cost is 4px of inline inset\n     at align=\"start\", which the whole anchored family already pays. */\n  .hui-navigation-menu-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    display: flex;\n    flex-direction: column;\n    gap: var(--hui-space-0-5);\n    min-width: min(12rem, calc(100dvi - 1rem));\n    opacity: 0;\n    padding: var(--hui-space-1);\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-navigation-menu-content:popover-open,\n  .hui-navigation-menu-content[data-state=\"open\"] {\n    opacity: 1;\n    transform: none;\n  }\n\n  @starting-style {\n    .hui-navigation-menu-content:popover-open {\n      opacity: 0;\n      transform: scale(0.97);\n    }\n  }\n\n  .hui-navigation-menu-content .hui-navigation-menu-link {\n    display: block;\n    padding: var(--hui-space-1-5) var(--hui-space-2);\n    text-align: start;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .hui-navigation-menu-content,\n    .hui-navigation-menu-trigger::after {\n      transition-duration: 0s;\n    }\n  }\n\n  @media (forced-colors: active) {\n    .hui-navigation-menu-content {\n      border: var(--hui-border-width) solid CanvasText;\n    }\n\n    .hui-navigation-menu-trigger:focus-visible,\n    .hui-navigation-menu-link:focus-visible {\n      outline-color: Highlight;\n    }\n\n    .hui-navigation-menu-trigger[data-state=\"open\"],\n    .hui-navigation-menu-link[data-active=\"true\"] {\n      background: Highlight;\n      color: HighlightText;\n      forced-color-adjust: none;\n    }\n  }\n}\n",
      "path": "packages/heidi-ui/src/navigation-menu/navigation-menu.theme.css",
      "target": "components/ui/heidi/navigation-menu/navigation-menu.theme.css",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui navigation-menu — aggregator (base + Heidi adapter + theme).\n */\n\n@import \"./navigation-menu.base.css\";\n@import \"../../../../styles/heidi-ui-adapter.css\";\n@import \"./navigation-menu.theme.css\";\n",
      "path": "packages/heidi-ui/src/navigation-menu/navigation-menu.css",
      "target": "components/ui/heidi/navigation-menu/navigation-menu.css",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from packages/heidi-ui/src/navigation-menu/navigation-menu.base.css + packages/heidi-ui/src/navigation-menu/navigation-menu.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\nexport const NAVIGATION_MENU_CLASSES = {\n  content: \"hui-navigation-menu-content\",\n  item: \"hui-navigation-menu-item\",\n  link: \"hui-navigation-menu-link\",\n  list: \"hui-navigation-menu-list\",\n  root: \"hui-navigation-menu-root\",\n  trigger: \"hui-navigation-menu-trigger\",\n} as const;\n",
      "path": "packages/heidi-ui/src/navigation-menu/navigation-menu.classes.generated.ts",
      "target": "components/ui/heidi/navigation-menu/navigation-menu.classes.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from navigation-menu.anatomy.json + navigation-menu.base.css + navigation-menu.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\n\nexport type NavigationMenuActive = \"false\" | \"true\";\nexport type NavigationMenuAlign = \"center\" | \"end\" | \"start\";\nexport type NavigationMenuSide = \"bottom\" | \"left\" | \"right\" | \"top\";\nexport type NavigationMenuState = \"closed\" | \"open\";\n\nexport const NAVIGATION_MENU_ANATOMY = {\n  \"component\": \"navigation-menu\",\n  \"description\": \"APG Disclosure Navigation Menu (NOT menubar/menu roles — site nav does not need menu widget semantics). Landmark <nav> with a horizontal list of disclosure triggers and/or links. Submenus use [popover=auto] + CSS anchors + light dismiss (exclusive open is free). Optional Arrow/Home/End among top-level controls; Escape closes the open panel and returns focus to its trigger. Controlled value = which item's panel is open (null when closed). Deferred: hover-open, Viewport/Indicator, nested submenus, vertical orientation.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"content\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-labelledby\"\n        ],\n        \"role\": \"region\"\n      },\n      \"class\": \"hui-navigation-menu-content\",\n      \"css\": {\n        \"structural\": [\n          \"inset\",\n          \"margin\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"navigation-menu-content\",\n      \"description\": \"The [popover=auto] disclosure panel; role=region named by its trigger; anchored below the trigger by default.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"align\",\n        \"alignOffset\",\n        \"className\",\n        \"onBeforeToggle\",\n        \"onToggle\",\n        \"ref\",\n        \"render\",\n        \"side\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":popover-open\"\n      ],\n      \"states\": {\n        \"data-align\": [\n          \"center\",\n          \"end\",\n          \"start\"\n        ],\n        \"data-side\": [\n          \"bottom\",\n          \"left\",\n          \"right\",\n          \"top\"\n        ],\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"item\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-item\",\n      \"css\": {\n        \"structural\": [\n          \"display\",\n          \"list-style\"\n        ]\n      },\n      \"dataPart\": \"navigation-menu-item\",\n      \"description\": \"List item wrapping a Trigger+Content pair or a top-level Link. value identifies the open panel for controlled mode.\",\n      \"element\": \"li\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"ref\",\n        \"render\",\n        \"style\",\n        \"value\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"link\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-current\"\n        ],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-link\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"navigation-menu-link\",\n      \"description\": \"Navigation anchor; optional active sets aria-current=page. Usable as a top-level Item child or inside Content.\",\n      \"element\": \"a\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"active\",\n        \"className\",\n        \"href\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\",\n        \":hover\"\n      ],\n      \"states\": {\n        \"data-active\": [\n          \"false\",\n          \"true\"\n        ]\n      }\n    },\n    \"list\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-list\",\n      \"css\": {\n        \"structural\": [\n          \"display\",\n          \"gap\",\n          \"list-style\",\n          \"margin\",\n          \"padding\"\n        ]\n      },\n      \"dataPart\": \"navigation-menu-list\",\n      \"description\": \"Top-level <ul>; owns optional Arrow/Home/End focus movement among triggers and top-level links.\",\n      \"element\": \"ul\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"onKeyDown\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"root\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-label\"\n        ],\n        \"role\": \"navigation\"\n      },\n      \"class\": \"hui-navigation-menu-root\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"navigation-menu-root\",\n      \"description\": \"The <nav> landmark; requires an accessible name via label.\",\n      \"element\": \"nav\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"label\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-controls\",\n          \"aria-expanded\"\n        ],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"navigation-menu-trigger\",\n      \"description\": \"Disclosure button: popoverTarget + aria-expanded/controls. Enter/Space toggles; ArrowDown opens and focuses the first link in Content.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"onKeyDown\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    }\n  },\n  \"rootProps\": [\n    \"defaultValue\",\n    \"label\",\n    \"onValueChange\",\n    \"value\"\n  ],\n  \"tokens\": [\n    \"--hui-border-width\",\n    \"--hui-color-bg-raised\",\n    \"--hui-color-fg-default\",\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-radius-2xl\",\n    \"--hui-radius-md\",\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  ]\n} as const;\n",
      "path": "packages/heidi-ui/src/navigation-menu/navigation-menu.anatomy.generated.ts",
      "target": "components/ui/heidi/navigation-menu/navigation-menu.anatomy.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "{\n  \"component\": \"navigation-menu\",\n  \"description\": \"APG Disclosure Navigation Menu (NOT menubar/menu roles — site nav does not need menu widget semantics). Landmark <nav> with a horizontal list of disclosure triggers and/or links. Submenus use [popover=auto] + CSS anchors + light dismiss (exclusive open is free). Optional Arrow/Home/End among top-level controls; Escape closes the open panel and returns focus to its trigger. Controlled value = which item's panel is open (null when closed). Deferred: hover-open, Viewport/Indicator, nested submenus, vertical orientation.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"content\": {\n      \"aria\": {\n        \"owns\": [\"aria-labelledby\"],\n        \"role\": \"region\"\n      },\n      \"class\": \"hui-navigation-menu-content\",\n      \"css\": {\n        \"structural\": [\n          \"inset\",\n          \"margin\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"navigation-menu-content\",\n      \"description\": \"The [popover=auto] disclosure panel; role=region named by its trigger; anchored below the trigger by default.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"align\",\n        \"alignOffset\",\n        \"className\",\n        \"onBeforeToggle\",\n        \"onToggle\",\n        \"ref\",\n        \"render\",\n        \"side\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\":popover-open\"],\n      \"states\": {\n        \"data-align\": [\"center\", \"end\", \"start\"],\n        \"data-side\": [\"bottom\", \"left\", \"right\", \"top\"],\n        \"data-state\": [\"closed\", \"open\"]\n      }\n    },\n    \"item\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-item\",\n      \"css\": {\n        \"structural\": [\"display\", \"list-style\"]\n      },\n      \"dataPart\": \"navigation-menu-item\",\n      \"description\": \"List item wrapping a Trigger+Content pair or a top-level Link. value identifies the open panel for controlled mode.\",\n      \"element\": \"li\",\n      \"nativeProps\": true,\n      \"props\": [\"className\", \"ref\", \"render\", \"style\", \"value\"],\n      \"pseudoStates\": [],\n      \"states\": {\n        \"data-state\": [\"closed\", \"open\"]\n      }\n    },\n    \"link\": {\n      \"aria\": {\n        \"owns\": [\"aria-current\"],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-link\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"navigation-menu-link\",\n      \"description\": \"Navigation anchor; optional active sets aria-current=page. Usable as a top-level Item child or inside Content.\",\n      \"element\": \"a\",\n      \"nativeProps\": true,\n      \"props\": [\"active\", \"className\", \"href\", \"ref\", \"render\", \"style\"],\n      \"pseudoStates\": [\":focus-visible\", \":hover\"],\n      \"states\": {\n        \"data-active\": [\"false\", \"true\"]\n      }\n    },\n    \"list\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-list\",\n      \"css\": {\n        \"structural\": [\"display\", \"gap\", \"list-style\", \"margin\", \"padding\"]\n      },\n      \"dataPart\": \"navigation-menu-list\",\n      \"description\": \"Top-level <ul>; owns optional Arrow/Home/End focus movement among triggers and top-level links.\",\n      \"element\": \"ul\",\n      \"nativeProps\": true,\n      \"props\": [\"className\", \"onKeyDown\", \"ref\", \"render\", \"style\"],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"root\": {\n      \"aria\": {\n        \"owns\": [\"aria-label\"],\n        \"role\": \"navigation\"\n      },\n      \"class\": \"hui-navigation-menu-root\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"navigation-menu-root\",\n      \"description\": \"The <nav> landmark; requires an accessible name via label.\",\n      \"element\": \"nav\",\n      \"nativeProps\": true,\n      \"props\": [\"className\", \"label\", \"ref\", \"render\", \"style\"],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [\"aria-controls\", \"aria-expanded\"],\n        \"role\": null\n      },\n      \"class\": \"hui-navigation-menu-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"navigation-menu-trigger\",\n      \"description\": \"Disclosure button: popoverTarget + aria-expanded/controls. Enter/Space toggles; ArrowDown opens and focuses the first link in Content.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\"className\", \"onKeyDown\", \"ref\", \"render\", \"style\"],\n      \"pseudoStates\": [\":focus-visible\"],\n      \"states\": {\n        \"data-state\": [\"closed\", \"open\"]\n      }\n    }\n  },\n  \"rootProps\": [\"defaultValue\", \"label\", \"onValueChange\", \"value\"]\n}\n",
      "path": "packages/heidi-ui/src/navigation-menu/navigation-menu.anatomy.json",
      "target": "components/ui/heidi/navigation-menu/navigation-menu.anatomy.json",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Pushing React's open state onto the native popover, once (P5).\n *\n * Popover, Toast, Select, NavigationMenu and Menu (root + submenu) each carried\n * this block. It exists because `showPopover()`/`hidePopover()` fire `toggle`\n * synchronously, and the `toggle` listener is also what reconciles NATIVE\n * dismissal (Esc, light dismiss) back into React. Without a marker, every\n * React-driven open would look like a user-driven one and echo a redundant\n * open-change request back to the owner.\n *\n * ponytail: six of the seven copies the audit counted share this boolean-flag\n * model and differ only in how they SHOW — four call `element.showPopover()`,\n * Menu's two call `showPopoverFrom(element, trigger)` so the popover anchors to\n * its trigger. That is the `show` option and nothing else.\n *\n * ContextMenu is the seventh and does NOT adopt this. It marks transitions by\n * pushing onto a queue that a later `toggle` drains, rather than by raising a\n * flag it lowers in `finally`. Those answer \"is this transition ours?\" at\n * different times — synchronously inside the call, versus whenever the event\n * arrives — and a nested context menu can have more than one transition in\n * flight, which is what the queue is for. Forcing it into the flag model would\n * be a rewrite of its reentrancy handling to make a count read 1 instead of 7,\n * which is the same trade P6 and P9 declined.\n */\n\nexport type NativePopoverSyncOptions = {\n  /**\n   * Anchored show, for popovers that position against a trigger. Defaults to\n   * `element.showPopover()`.\n   */\n  show?: (element: HTMLElement) => void;\n  /**\n   * Raised for the duration of the native call so the component's own `toggle`\n   * listener can tell its own transition from a user's.\n   */\n  transitionRef: { current: boolean };\n};\n\nexport function synchronizeNativePopoverOpen(\n  element: HTMLElement,\n  next: boolean,\n  { show, transitionRef }: NativePopoverSyncOptions\n): void {\n  if (element.matches(\":popover-open\") === next) {\n    return;\n  }\n  transitionRef.current = true;\n  try {\n    if (next) {\n      if (show) {\n        show(element);\n      } else {\n        element.showPopover();\n      }\n    } else {\n      element.hidePopover();\n    }\n  } catch {\n    // A detached or already-transitioning popover can reject the request. The\n    // next state/effect pass retries against React's authority, so swallowing\n    // it here loses nothing — and throwing would take down a render.\n  } finally {\n    // `finally`, not the end of `try`: a rejected request must still lower the\n    // flag, or the component would treat every later native transition as its\n    // own and stop reconciling user dismissal for the rest of its life.\n    transitionRef.current = false;\n  }\n}\n",
      "path": "packages/heidi-ui/src/_internal/native-popover-sync.ts",
      "target": "components/ui/heidi/_internal/native-popover-sync.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"
    }
  ],
  "name": "navigation-menu",
  "registryDependencies": [
    "@heidi-ui/heidi-tokens"
  ],
  "title": "Navigation-menu",
  "type": "registry:ui"
}
