{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "nav-menu",
  "title": "Nav Menu",
  "description": "A vertical navigation list with two-layer proximity highlight (active route + hover), weight-shift labels, and status dots for new/updated items.",
  "dependencies": [
    "motion",
    "@radix-ui/react-slot"
  ],
  "files": [
    {
      "path": "components/ui/nav-menu.tsx",
      "type": "registry:ui",
      "content": "'use client'\n\nimport {\n  cloneElement,\n  createContext,\n  forwardRef,\n  isValidElement,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  type HTMLAttributes,\n  type ReactNode,\n} from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { motion, useMotionValue, useReducedMotion, useSpring, type MotionValue } from 'motion/react'\nimport { cn } from '@/lib/utils'\nimport { springs } from '@/lib/motion-tokens'\nimport {\n  useProximityHighlight,\n  ProximityHighlight,\n  type ItemRect,\n} from '@/lib/hooks/use-proximity-highlight'\nimport { WeightShiftText } from '@/components/ui/weight-shift-text'\n\ninterface NavMenuContextValue {\n  activeSlug: string | null\n  nextIndex: () => number\n  registerItem: (index: number, el: HTMLElement | null) => void\n}\n\nconst NavMenuContext = createContext<NavMenuContextValue | null>(null)\n\nfunction useNavMenuCtx(componentName: string) {\n  const ctx = useContext(NavMenuContext)\n  if (!ctx) throw new Error(`${componentName} must be used within NavMenu`)\n  return ctx\n}\n\ninterface NavMenuContentContextValue {\n  activeIndex: number | null\n  setActiveIndex: (i: number | null) => void\n  highlightX: ReturnType<typeof import('motion/react').useSpring>\n  highlightSize: MotionValue<number>\n  highlightOpacity: ReturnType<typeof import('motion/react').useSpring>\n  axis: 'x' | 'y'\n  registerItem: (index: number, el: HTMLElement | null) => void\n}\n\nconst NavMenuContentContext = createContext<NavMenuContentContextValue | null>(null)\n\nfunction useNavMenuContentCtx(componentName: string) {\n  const ctx = useContext(NavMenuContentContext)\n  if (!ctx) throw new Error(`${componentName} must be used within NavMenuContent`)\n  return ctx\n}\n\nexport interface NavMenuProps extends HTMLAttributes<HTMLElement> {\n  children: ReactNode\n  activeSlug: string | null\n  'aria-label'?: string\n}\n\nexport const NavMenu = forwardRef<HTMLElement, NavMenuProps>(\n  ({ children, activeSlug, 'aria-label': ariaLabel = 'Main', className, ...props }, ref) => {\n    const navRef = useRef<HTMLElement>(null)\n    const activeItemRectRef = useRef<ItemRect | null>(null)\n\n    const {\n      activeIndex,\n      setActiveIndex,\n      registerItem,\n      handlers,\n      highlightX,\n      highlightSize,\n      highlightOpacity,\n      axis,\n    } = useProximityHighlight(navRef, { axis: 'y' })\n\n    const indexRef = useRef(0)\n    const nextIndex = useCallback(() => {\n      const idx = indexRef.current\n      indexRef.current += 1\n      return idx\n    }, [])\n\n    const rawActiveX = useMotionValue(0)\n    const rawActiveSize = useMotionValue(0)\n    const rawActiveOpacity = useMotionValue(0)\n    const reduceMotion = useReducedMotion()\n    const activeSpringOpts = reduceMotion ? { duration: 0 } : springs.moderate\n    const activeX = useSpring(rawActiveX, activeSpringOpts)\n    const activeSize = useSpring(rawActiveSize, activeSpringOpts)\n    const activeOpacity = useSpring(rawActiveOpacity, activeSpringOpts)\n\n    const isHoveringOther = activeIndex !== null\n\n    useEffect(() => {\n      if (!activeItemRectRef.current) return\n      if (reduceMotion) {\n        rawActiveOpacity.jump(isHoveringOther ? 0.8 : 1)\n      } else {\n        rawActiveOpacity.set(isHoveringOther ? 0.8 : 1)\n      }\n    }, [isHoveringOther, reduceMotion, rawActiveOpacity])\n\n    useEffect(() => {\n      const nav = navRef.current\n      if (!nav) return\n      const items = Array.from(nav.querySelectorAll<HTMLElement>('[data-nav-item]'))\n      const found = items.find((el) => el.getAttribute('data-slug') === activeSlug)\n      if (found) {\n        const rect: ItemRect = {\n          top: found.offsetTop,\n          height: found.offsetHeight,\n          left: found.offsetLeft,\n          width: found.offsetWidth,\n        }\n        activeItemRectRef.current = rect\n        if (reduceMotion) {\n          rawActiveX.jump(rect.top)\n          rawActiveSize.jump(rect.height)\n          rawActiveOpacity.jump(1)\n        } else {\n          rawActiveX.set(rect.top)\n          rawActiveSize.jump(rect.height)\n          rawActiveOpacity.set(1)\n        }\n      } else {\n        activeItemRectRef.current = null\n        if (reduceMotion) rawActiveOpacity.jump(0)\n        else rawActiveOpacity.set(0)\n      }\n    }, [activeSlug, reduceMotion, rawActiveX, rawActiveSize, rawActiveOpacity])\n\n    useEffect(() => {\n      const nav = navRef.current\n      if (!nav) return\n      const handleKeyDown = (e: KeyboardEvent) => {\n        if (!nav.contains(document.activeElement)) return\n        const items = nav.querySelectorAll<HTMLElement>('[data-nav-item]:not([data-disabled])')\n        if (!items.length) return\n        const count = items.length\n        const currentIdx = Array.from(items).indexOf(document.activeElement as HTMLElement)\n\n        if (e.key === 'ArrowDown') {\n          e.preventDefault()\n          const next = currentIdx < 0 ? 0 : (currentIdx + 1) % count\n          items[next]?.focus()\n        } else if (e.key === 'ArrowUp') {\n          e.preventDefault()\n          const next = currentIdx < 0 ? count - 1 : (currentIdx - 1 + count) % count\n          items[next]?.focus()\n        } else if (e.key === 'Home') {\n          e.preventDefault()\n          items[0]?.focus()\n        } else if (e.key === 'End') {\n          e.preventDefault()\n          items[count - 1]?.focus()\n        }\n      }\n      document.addEventListener('keydown', handleKeyDown)\n      return () => document.removeEventListener('keydown', handleKeyDown)\n    }, [])\n\n    const setRefs = useCallback(\n      (node: HTMLElement | null) => {\n        ;(navRef as React.MutableRefObject<HTMLElement | null>).current = node\n        if (typeof ref === 'function') ref(node)\n        else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n      },\n      [ref],\n    )\n\n    const contentCtx = useMemo<NavMenuContentContextValue>(\n      () => ({\n        activeIndex,\n        setActiveIndex,\n        highlightX,\n        highlightSize,\n        highlightOpacity,\n        axis,\n        registerItem,\n      }),\n      [\n        activeIndex,\n        setActiveIndex,\n        highlightX,\n        highlightSize,\n        highlightOpacity,\n        axis,\n        registerItem,\n      ],\n    )\n\n    return (\n      <NavMenuContext.Provider value={{ activeSlug, nextIndex, registerItem }}>\n        <NavMenuContentContext.Provider value={contentCtx}>\n          <nav\n            ref={setRefs}\n            aria-label={ariaLabel}\n            className={cn('relative flex flex-col gap-0.5', className)}\n            {...handlers}\n            {...props}\n          >\n            <motion.div\n              aria-hidden=\"true\"\n              className=\"pointer-events-none absolute inset-x-1 rounded-md squircle-corners bg-(--color-accent)/12\"\n              style={{ y: activeX, height: activeSize, opacity: activeOpacity }}\n            />\n            <ProximityHighlight\n              highlightX={highlightX}\n              highlightSize={highlightSize}\n              highlightOpacity={highlightOpacity}\n              axis={axis}\n              className=\"mx-1 rounded-md squircle-corners bg-(--color-fg)/8\"\n            />\n            {children}\n          </nav>\n        </NavMenuContentContext.Provider>\n      </NavMenuContext.Provider>\n    )\n  },\n)\nNavMenu.displayName = 'NavMenu'\n\nexport interface NavMenuItemProps extends HTMLAttributes<HTMLAnchorElement> {\n  href: string\n  label: string\n  icon?: ReactNode\n  isNew?: boolean\n  isUpdated?: boolean\n  asChild?: boolean\n}\n\nexport const NavMenuItem = forwardRef<HTMLAnchorElement, NavMenuItemProps>(\n  (\n    { href, label, icon, isNew, isUpdated, asChild = false, className, children, ...props },\n    ref,\n  ) => {\n    const { activeSlug, nextIndex } = useNavMenuCtx('NavMenuItem')\n    const { activeIndex, setActiveIndex, registerItem } = useNavMenuContentCtx('NavMenuItem')\n\n    const itemRef = useRef<HTMLAnchorElement | null>(null)\n    const indexRef = useRef<number | null>(null)\n    if (indexRef.current === null) indexRef.current = nextIndex()\n    // eslint-disable-next-line react-hooks/refs\n    const index = indexRef.current\n    const isActive = activeSlug === href\n    const isHovered = activeIndex === index\n\n    useEffect(() => {\n      const el = itemRef.current\n      if (index < 0 || !el) return\n      registerItem(index, el)\n      return () => registerItem(index, null)\n    }, [index, registerItem])\n\n    const setItemRef = useCallback(\n      (node: HTMLAnchorElement | null) => {\n        itemRef.current = node\n        if (typeof ref === 'function') ref(node)\n        else if (ref) (ref as React.MutableRefObject<HTMLAnchorElement | null>).current = node\n      },\n      [ref],\n    )\n\n    const weight = isActive ? 550 : isHovered ? 500 : 400\n\n    const itemClassName = cn(\n      'group relative flex min-h-11 items-center gap-2.5 rounded-md squircle-corners px-3 py-2 text-sm outline-none transition-colors duration-(--motion-dur-fast) motion-reduce:transition-none',\n      'text-(--color-muted) hover:text-(--color-fg)',\n      'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-2 focus-visible:ring-offset-(--color-bg)',\n      isActive && 'text-(--color-fg)',\n      className,\n    )\n\n    const sharedProps = {\n      'data-nav-item': true,\n      'data-slug': href,\n      'aria-current': isActive ? ('page' as const) : undefined,\n      tabIndex: isActive ? 0 : -1,\n      onFocus: () => setActiveIndex(index),\n    }\n\n    const content = (\n      <>\n        {icon && (\n          <span className=\"shrink-0 [&_svg]:size-4\" aria-hidden=\"true\">\n            {icon}\n          </span>\n        )}\n        <WeightShiftText\n          baseWeight={400}\n          activeWeight={weight}\n          active={isActive || isHovered}\n          duration=\"150ms\"\n          className=\"min-w-0\"\n        >\n          {label}\n        </WeightShiftText>\n        {isNew && (\n          <>\n            <span\n              className=\"size-1.5 shrink-0 rounded-full bg-(--color-accent)\"\n              aria-hidden=\"true\"\n            />\n            <span className=\"sr-only\">New</span>\n          </>\n        )}\n        {isUpdated && !isNew && (\n          <>\n            <span\n              className=\"size-1.5 shrink-0 rounded-full bg-(--color-subtle)\"\n              aria-hidden=\"true\"\n            />\n            <span className=\"sr-only\">Updated</span>\n          </>\n        )}\n      </>\n    )\n\n    if (asChild && isValidElement(children)) {\n      return (\n        <Slot ref={setItemRef} className={itemClassName} {...sharedProps} {...props}>\n          {cloneElement(children as React.ReactElement, undefined, content)}\n        </Slot>\n      )\n    }\n\n    return (\n      <a ref={setItemRef} href={href} className={itemClassName} {...sharedProps} {...props}>\n        {content}\n      </a>\n    )\n  },\n)\nNavMenuItem.displayName = 'NavMenuItem'\n\nexport function NavMenuPreview() {\n  return (\n    <div\n      className=\"flex h-full w-full items-center justify-center p-6\"\n      style={{ backgroundColor: 'var(--color-surface)' }}\n    >\n      <div className=\"w-48\">\n        <NavMenu activeSlug=\"/docs/install\" aria-label=\"Docs\">\n          <NavMenuItem href=\"/docs\" label=\"Overview\" />\n          <NavMenuItem href=\"/docs/install\" label=\"Install\" isNew />\n          <NavMenuItem href=\"/docs/theming\" label=\"Theming\" />\n          <NavMenuItem href=\"/docs/components\" label=\"Components\" isUpdated />\n        </NavMenu>\n      </div>\n    </div>\n  )\n}\n"
    },
    {
      "path": "components/ui/weight-shift-text.tsx",
      "type": "registry:ui",
      "content": "'use client'\n\nimport { forwardRef, useState, type HTMLAttributes, type ReactNode } from 'react'\nimport { useReducedMotion } from 'motion/react'\nimport { cn } from '@/lib/utils'\nimport { durations } from '@/lib/motion-tokens'\n\nexport interface WeightShiftTextProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {\n  children: ReactNode\n  baseWeight?: number\n  activeWeight?: number\n  active?: boolean\n  duration?: string\n}\n\nexport const WeightShiftText = forwardRef<HTMLSpanElement, WeightShiftTextProps>(\n  (\n    {\n      children,\n      baseWeight = 400,\n      activeWeight = 600,\n      active,\n      duration = durations.fast,\n      className,\n      style,\n      onMouseEnter: onMouseEnterProp,\n      onMouseLeave: onMouseLeaveProp,\n      ...props\n    },\n    ref,\n  ) => {\n    const reduceMotion = useReducedMotion()\n    const [isHovered, setIsHovered] = useState(false)\n\n    const isControlledActive = active !== undefined\n    const currentWeight = isControlledActive\n      ? active\n        ? activeWeight\n        : baseWeight\n      : isHovered\n        ? activeWeight\n        : baseWeight\n\n    return (\n      <span\n        ref={ref}\n        className={cn('relative inline-block', className)}\n        onMouseEnter={(e) => {\n          setIsHovered(true)\n          onMouseEnterProp?.(e)\n        }}\n        onMouseLeave={(e) => {\n          setIsHovered(false)\n          onMouseLeaveProp?.(e)\n        }}\n        style={style}\n        {...props}\n      >\n        <span\n          aria-hidden=\"true\"\n          className=\"pointer-events-none invisible absolute inset-0\"\n          style={{\n            fontVariationSettings: `\"wght\" ${activeWeight}`,\n          }}\n        >\n          {children}\n        </span>\n        <span\n          className=\"relative\"\n          style={{\n            fontVariationSettings: `\"wght\" ${currentWeight}`,\n            transition: reduceMotion\n              ? 'none'\n              : `font-variation-settings ${duration} var(--motion-ease-out)`,\n          }}\n        >\n          {children}\n        </span>\n      </span>\n    )\n  },\n)\nWeightShiftText.displayName = 'WeightShiftText'\n\nexport function WeightShiftTextPreview() {\n  return (\n    <div\n      className=\"w-full h-full min-h-50 rounded-lg overflow-hidden flex flex-col items-center justify-center gap-6 p-4\"\n      style={{ backgroundColor: 'var(--color-surface)' }}\n    >\n      <WeightShiftText\n        baseWeight={300}\n        activeWeight={700}\n        className=\"text-lg cursor-pointer select-none\"\n        style={{ color: 'var(--color-fg)' }}\n      >\n        Hover to shift weight\n      </WeightShiftText>\n      <WeightShiftText\n        baseWeight={400}\n        activeWeight={600}\n        active={true}\n        className=\"text-sm\"\n        style={{ color: 'var(--color-muted)' }}\n      >\n        Always active\n      </WeightShiftText>\n    </div>\n  )\n}\n"
    },
    {
      "path": "lib/hooks/use-proximity-highlight.tsx",
      "type": "registry:lib",
      "content": "'use client'\n\nimport { useRef, useState, useCallback, useEffect, type RefObject } from 'react'\nimport {\n  motion,\n  useMotionValue,\n  useReducedMotion,\n  useSpring,\n  type MotionValue,\n  type MotionStyle,\n} from 'motion/react'\nimport { springs } from '@/lib/motion-tokens'\n\nexport interface ItemRect {\n  top: number\n  height: number\n  left: number\n  width: number\n}\n\ninterface UseProximityHighlightOptions {\n  axis?: 'x' | 'y'\n  spring?: { stiffness: number; damping: number; mass: number }\n}\n\ninterface UseProximityHighlightReturn {\n  activeIndex: number | null\n  setActiveIndex: (index: number | null) => void\n  itemRects: ItemRect[]\n  sessionRef: RefObject<number>\n  handlers: {\n    onMouseMove: (e: React.MouseEvent) => void\n    onMouseEnter: () => void\n    onMouseLeave: () => void\n  }\n  registerItem: (index: number, element: HTMLElement | null) => void\n  measureItems: () => void\n  highlightX: ReturnType<typeof useSpring>\n  highlightSize: MotionValue<number>\n  highlightOpacity: ReturnType<typeof useSpring>\n  axis: 'x' | 'y'\n}\n\nexport function useProximityHighlight<T extends HTMLElement>(\n  containerRef: RefObject<T | null>,\n  options: UseProximityHighlightOptions = {},\n): UseProximityHighlightReturn {\n  const { axis = 'y', spring: springConfig } = options\n  const reduceMotion = useReducedMotion()\n\n  const itemsRef = useRef(new Map<number, HTMLElement>())\n  const [activeIndex, setActiveIndexRaw] = useState<number | null>(null)\n  const activeIndexRef = useRef<number | null>(null)\n  const [itemRects, setItemRects] = useState<ItemRect[]>([])\n  const itemRectsRef = useRef<ItemRect[]>([])\n  const sessionRef = useRef(0)\n  const rafIdRef = useRef<number | null>(null)\n  const remeasureRafIdRef = useRef<number | null>(null)\n  const updateHighlightRef = useRef<(index: number | null) => void>(() => {})\n\n  const rawX = useMotionValue(0)\n  const highlightSize = useMotionValue(0)\n  const rawOpacity = useMotionValue(0)\n\n  const springOpts = reduceMotion ? { duration: 0 } : (springConfig ?? springs.settle)\n\n  const highlightX = useSpring(rawX, springOpts)\n  const highlightOpacity = useSpring(rawOpacity, springOpts)\n\n  const measureItems = useCallback(() => {\n    const container = containerRef.current\n    if (!container) return\n    const rects: ItemRect[] = []\n    itemsRef.current.forEach((element, index) => {\n      rects[index] = {\n        top: element.offsetTop,\n        height: element.offsetHeight,\n        left: element.offsetLeft,\n        width: element.offsetWidth,\n      }\n    })\n    itemRectsRef.current = rects\n    setItemRects(rects)\n  }, [containerRef])\n\n  const registerItem = useCallback(\n    (index: number, element: HTMLElement | null) => {\n      if (element) {\n        itemsRef.current.set(index, element)\n      } else {\n        itemsRef.current.delete(index)\n      }\n      if (remeasureRafIdRef.current !== null) {\n        cancelAnimationFrame(remeasureRafIdRef.current)\n      }\n      remeasureRafIdRef.current = requestAnimationFrame(() => {\n        remeasureRafIdRef.current = null\n        measureItems()\n      })\n    },\n    [measureItems],\n  )\n\n  const updateHighlight = useCallback(\n    (index: number | null) => {\n      if (index === null || !itemRectsRef.current[index]) {\n        if (reduceMotion) {\n          rawOpacity.jump(0)\n        } else {\n          rawOpacity.set(0)\n        }\n        return\n      }\n      const r = itemRectsRef.current[index]\n      if (axis === 'y') {\n        if (reduceMotion) {\n          rawX.jump(r.top)\n          highlightSize.jump(r.height)\n          rawOpacity.jump(1)\n        } else {\n          rawX.set(r.top)\n          highlightSize.jump(r.height)\n          rawOpacity.set(1)\n        }\n      } else {\n        if (reduceMotion) {\n          rawX.jump(r.left)\n          highlightSize.jump(r.width)\n          rawOpacity.jump(1)\n        } else {\n          rawX.set(r.left)\n          highlightSize.jump(r.width)\n          rawOpacity.set(1)\n        }\n      }\n    },\n    [axis, reduceMotion, rawX, highlightSize, rawOpacity],\n  )\n\n  useEffect(() => {\n    updateHighlightRef.current = updateHighlight\n  })\n\n  const setActiveIndex = useCallback((index: number | null) => {\n    setActiveIndexRaw(index)\n    updateHighlightRef.current(index)\n  }, [])\n\n  const handleMouseMove = useCallback(\n    (e: React.MouseEvent) => {\n      const mouseX = e.clientX\n      const mouseY = e.clientY\n\n      if (rafIdRef.current !== null) {\n        cancelAnimationFrame(rafIdRef.current)\n      }\n\n      rafIdRef.current = requestAnimationFrame(() => {\n        rafIdRef.current = null\n        const container = containerRef.current\n        if (!container) return\n\n        const containerRect = container.getBoundingClientRect()\n        const mousePos = axis === 'x' ? mouseX : mouseY\n\n        let closestIndex: number | null = null\n        let closestDistance = Infinity\n        let containingIndex: number | null = null\n\n        const rects = itemRectsRef.current\n        const scrollOffset = axis === 'x' ? container.scrollLeft : container.scrollTop\n        const borderOffset = axis === 'x' ? container.clientLeft : container.clientTop\n        const containerEdge = axis === 'x' ? containerRect.left : containerRect.top\n\n        const layoutSize = axis === 'x' ? container.offsetWidth : container.offsetHeight\n        const visualSize = axis === 'x' ? containerRect.width : containerRect.height\n        const scale = layoutSize > 0 ? visualSize / layoutSize : 1\n\n        for (let index = 0; index < rects.length; index++) {\n          const r = rects[index]\n          if (!r) continue\n\n          const contentPos = axis === 'x' ? r.left : r.top\n          const itemStart = containerEdge + (borderOffset + contentPos - scrollOffset) * scale\n          const itemSize = (axis === 'x' ? r.width : r.height) * scale\n          const itemEnd = itemStart + itemSize\n\n          if (mousePos >= itemStart && mousePos <= itemEnd) {\n            containingIndex = index\n          }\n\n          const itemCenter = itemStart + itemSize / 2\n          const distance = Math.abs(mousePos - itemCenter)\n\n          if (distance < closestDistance) {\n            closestDistance = distance\n            closestIndex = index\n          }\n        }\n\n        const next = containingIndex ?? closestIndex\n        if (next !== activeIndexRef.current) {\n          activeIndexRef.current = next\n          setActiveIndex(next)\n        }\n      })\n    },\n    [axis, containerRef, setActiveIndex],\n  )\n\n  const handleMouseEnter = useCallback(() => {\n    sessionRef.current += 1\n  }, [])\n\n  const handleMouseLeave = useCallback(() => {\n    if (rafIdRef.current !== null) {\n      cancelAnimationFrame(rafIdRef.current)\n      rafIdRef.current = null\n    }\n    activeIndexRef.current = null\n    setActiveIndex(null)\n  }, [setActiveIndex])\n\n  useEffect(() => {\n    return () => {\n      if (rafIdRef.current !== null) {\n        cancelAnimationFrame(rafIdRef.current)\n      }\n      if (remeasureRafIdRef.current !== null) {\n        cancelAnimationFrame(remeasureRafIdRef.current)\n      }\n    }\n  }, [])\n\n  return {\n    activeIndex,\n    setActiveIndex,\n    itemRects,\n    sessionRef,\n    handlers: {\n      onMouseMove: handleMouseMove,\n      onMouseEnter: handleMouseEnter,\n      onMouseLeave: handleMouseLeave,\n    },\n    registerItem,\n    measureItems,\n    highlightX,\n    highlightSize,\n    highlightOpacity,\n    axis,\n  }\n}\n\nexport interface ProximityHighlightProps {\n  highlightX: ReturnType<typeof useSpring>\n  highlightSize: MotionValue<number>\n  highlightOpacity: ReturnType<typeof useSpring>\n  axis: 'x' | 'y'\n  className?: string\n  style?: MotionStyle\n}\n\nexport function ProximityHighlight({\n  highlightX,\n  highlightSize,\n  highlightOpacity,\n  axis,\n  className,\n  style,\n}: ProximityHighlightProps) {\n  return (\n    <motion.div\n      aria-hidden=\"true\"\n      className={className}\n      style={{\n        ...style,\n        position: 'absolute',\n        inset: 0,\n        pointerEvents: 'none',\n        ...(axis === 'y'\n          ? { y: highlightX, height: highlightSize }\n          : { x: highlightX, width: highlightSize }),\n        opacity: highlightOpacity,\n      }}\n    />\n  )\n}\n\nexport function useRegisterProximityItem(\n  registerItem: (index: number, element: HTMLElement | null) => void,\n  index: number,\n  ref: RefObject<HTMLElement | null>,\n) {\n  useEffect(() => {\n    registerItem(index, ref.current)\n    return () => registerItem(index, null)\n  }, [index, registerItem, ref])\n}\n"
    },
    {
      "path": "lib/motion-tokens.ts",
      "type": "registry:lib",
      "content": "export const durations = {\n  instant: '50ms',\n  fast: '120ms',\n  base: '200ms',\n  slow: '320ms',\n} as const\n\nexport const easings = {\n  out: 'cubic-bezier(0.22, 1, 0.36, 1)',\n  inOut: 'cubic-bezier(0.65, 0, 0.35, 1)',\n} as const\n\nexport const springs = {\n  fast: { type: 'spring', stiffness: 400, damping: 30, mass: 0.8 },\n  press: { type: 'spring', stiffness: 700, damping: 32, mass: 1 },\n  moderate: { type: 'spring', stiffness: 300, damping: 24, mass: 1 },\n  settle: { type: 'spring', stiffness: 260, damping: 26, mass: 1 },\n} as const\n"
    }
  ],
  "type": "registry:ui"
}
