{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "PillNav-JS-TW",
	"title": "PillNav",
	"description": "Minimal pill nav with sliding active highlight + smooth easing.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "PillNav/PillNav.jsx",
			"content": "import { useEffect, useRef, useState } from 'react';\nimport { Link } from 'react-router-dom';\nimport { gsap } from 'gsap';\n\nconst PillNav = ({\n  logo,\n  logoAlt = 'Logo',\n  items,\n  activeHref,\n  className = '',\n  ease = 'power3.easeOut',\n  baseColor = '#fff',\n  pillColor = '#120F17',\n  hoveredPillTextColor = '#120F17',\n  pillTextColor,\n  onMobileMenuClick,\n  initialLoadAnimation = true\n}) => {\n  const resolvedPillTextColor = pillTextColor ?? baseColor;\n  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);\n  const circleRefs = useRef([]);\n  const tlRefs = useRef([]);\n  const activeTweenRefs = useRef([]);\n  const logoImgRef = useRef(null);\n  const logoTweenRef = useRef(null);\n  const hamburgerRef = useRef(null);\n  const mobileMenuRef = useRef(null);\n  const navItemsRef = useRef(null);\n  const logoRef = useRef(null);\n\n  useEffect(() => {\n    const layout = () => {\n      circleRefs.current.forEach(circle => {\n        if (!circle?.parentElement) return;\n\n        const pill = circle.parentElement;\n        const rect = pill.getBoundingClientRect();\n        const { width: w, height: h } = rect;\n        const R = ((w * w) / 4 + h * h) / (2 * h);\n        const D = Math.ceil(2 * R) + 2;\n        const delta = Math.ceil(R - Math.sqrt(Math.max(0, R * R - (w * w) / 4))) + 1;\n        const originY = D - delta;\n\n        circle.style.width = `${D}px`;\n        circle.style.height = `${D}px`;\n        circle.style.bottom = `-${delta}px`;\n\n        gsap.set(circle, {\n          xPercent: -50,\n          scale: 0,\n          transformOrigin: `50% ${originY}px`\n        });\n\n        const label = pill.querySelector('.pill-label');\n        const white = pill.querySelector('.pill-label-hover');\n\n        if (label) gsap.set(label, { y: 0 });\n        if (white) gsap.set(white, { y: h + 12, opacity: 0 });\n\n        const index = circleRefs.current.indexOf(circle);\n        if (index === -1) return;\n\n        tlRefs.current[index]?.kill();\n        const tl = gsap.timeline({ paused: true });\n\n        tl.to(circle, { scale: 1.2, xPercent: -50, duration: 2, ease, overwrite: 'auto' }, 0);\n\n        if (label) {\n          tl.to(label, { y: -(h + 8), duration: 2, ease, overwrite: 'auto' }, 0);\n        }\n\n        if (white) {\n          gsap.set(white, { y: Math.ceil(h + 100), opacity: 0 });\n          tl.to(white, { y: 0, opacity: 1, duration: 2, ease, overwrite: 'auto' }, 0);\n        }\n\n        tlRefs.current[index] = tl;\n      });\n    };\n\n    layout();\n\n    const onResize = () => layout();\n    window.addEventListener('resize', onResize);\n\n    if (document.fonts?.ready) {\n      document.fonts.ready.then(layout).catch(() => {});\n    }\n\n    const menu = mobileMenuRef.current;\n    if (menu) {\n      gsap.set(menu, { visibility: 'hidden', opacity: 0, scaleY: 1, y: 0 });\n    }\n\n    if (initialLoadAnimation) {\n      const logo = logoRef.current;\n      const navItems = navItemsRef.current;\n\n      if (logo) {\n        gsap.set(logo, { scale: 0 });\n        gsap.to(logo, {\n          scale: 1,\n          duration: 0.6,\n          ease\n        });\n      }\n\n      if (navItems) {\n        gsap.set(navItems, { width: 0, overflow: 'hidden' });\n        gsap.to(navItems, {\n          width: 'auto',\n          duration: 0.6,\n          ease\n        });\n      }\n    }\n\n    return () => window.removeEventListener('resize', onResize);\n  }, [items, ease, initialLoadAnimation]);\n\n  const handleEnter = i => {\n    const tl = tlRefs.current[i];\n    if (!tl) return;\n    activeTweenRefs.current[i]?.kill();\n    activeTweenRefs.current[i] = tl.tweenTo(tl.duration(), {\n      duration: 0.3,\n      ease,\n      overwrite: 'auto'\n    });\n  };\n\n  const handleLeave = i => {\n    const tl = tlRefs.current[i];\n    if (!tl) return;\n    activeTweenRefs.current[i]?.kill();\n    activeTweenRefs.current[i] = tl.tweenTo(0, {\n      duration: 0.2,\n      ease,\n      overwrite: 'auto'\n    });\n  };\n\n  const handleLogoEnter = () => {\n    const img = logoImgRef.current;\n    if (!img) return;\n    logoTweenRef.current?.kill();\n    gsap.set(img, { rotate: 0 });\n    logoTweenRef.current = gsap.to(img, {\n      rotate: 360,\n      duration: 0.2,\n      ease,\n      overwrite: 'auto'\n    });\n  };\n\n  const toggleMobileMenu = () => {\n    const newState = !isMobileMenuOpen;\n    setIsMobileMenuOpen(newState);\n\n    const hamburger = hamburgerRef.current;\n    const menu = mobileMenuRef.current;\n\n    if (hamburger) {\n      const lines = hamburger.querySelectorAll('.hamburger-line');\n      if (newState) {\n        gsap.to(lines[0], { rotation: 45, y: 3, duration: 0.3, ease });\n        gsap.to(lines[1], { rotation: -45, y: -3, duration: 0.3, ease });\n      } else {\n        gsap.to(lines[0], { rotation: 0, y: 0, duration: 0.3, ease });\n        gsap.to(lines[1], { rotation: 0, y: 0, duration: 0.3, ease });\n      }\n    }\n\n    if (menu) {\n      if (newState) {\n        gsap.set(menu, { visibility: 'visible' });\n        gsap.fromTo(\n          menu,\n          { opacity: 0, y: 10, scaleY: 1 },\n          {\n            opacity: 1,\n            y: 0,\n            scaleY: 1,\n            duration: 0.3,\n            ease,\n            transformOrigin: 'top center'\n          }\n        );\n      } else {\n        gsap.to(menu, {\n          opacity: 0,\n          y: 10,\n          scaleY: 1,\n          duration: 0.2,\n          ease,\n          transformOrigin: 'top center',\n          onComplete: () => {\n            gsap.set(menu, { visibility: 'hidden' });\n          }\n        });\n      }\n    }\n\n    onMobileMenuClick?.();\n  };\n\n  const isExternalLink = href =>\n    href.startsWith('http://') ||\n    href.startsWith('https://') ||\n    href.startsWith('//') ||\n    href.startsWith('mailto:') ||\n    href.startsWith('tel:') ||\n    href.startsWith('#');\n\n  const isRouterLink = href => href && !isExternalLink(href);\n\n  const cssVars = {\n    ['--base']: baseColor,\n    ['--pill-bg']: pillColor,\n    ['--hover-text']: hoveredPillTextColor,\n    ['--pill-text']: resolvedPillTextColor,\n    ['--nav-h']: '42px',\n    ['--logo']: '36px',\n    ['--pill-pad-x']: '18px',\n    ['--pill-gap']: '3px'\n  };\n\n  return (\n    <div className=\"absolute top-[1em] z-[1000] w-full left-0 md:w-auto md:left-auto\">\n      <nav\n        className={`w-full md:w-max flex items-center justify-between md:justify-start box-border px-4 md:px-0 ${className}`}\n        aria-label=\"Primary\"\n        style={cssVars}\n      >\n        {isRouterLink(items?.[0]?.href) ? (\n          <Link\n            to={items[0].href}\n            aria-label=\"Home\"\n            onMouseEnter={handleLogoEnter}\n            role=\"menuitem\"\n            ref={el => {\n              logoRef.current = el;\n            }}\n            className=\"rounded-full p-2 inline-flex items-center justify-center overflow-hidden\"\n            style={{\n              width: 'var(--nav-h)',\n              height: 'var(--nav-h)',\n              background: 'var(--base, #000)'\n            }}\n          >\n            <img src={logo} alt={logoAlt} ref={logoImgRef} className=\"w-full h-full object-cover block\" />\n          </Link>\n        ) : (\n          <a\n            href={items?.[0]?.href || '#'}\n            aria-label=\"Home\"\n            onMouseEnter={handleLogoEnter}\n            ref={el => {\n              logoRef.current = el;\n            }}\n            className=\"rounded-full p-2 inline-flex items-center justify-center overflow-hidden\"\n            style={{\n              width: 'var(--nav-h)',\n              height: 'var(--nav-h)',\n              background: 'var(--base, #000)'\n            }}\n          >\n            <img src={logo} alt={logoAlt} ref={logoImgRef} className=\"w-full h-full object-cover block\" />\n          </a>\n        )}\n\n        <div\n          ref={navItemsRef}\n          className=\"relative items-center rounded-full hidden md:flex ml-2\"\n          style={{\n            height: 'var(--nav-h)',\n            background: 'var(--base, #000)'\n          }}\n        >\n          <ul\n            role=\"menubar\"\n            className=\"list-none flex items-stretch m-0 p-[3px] h-full\"\n            style={{ gap: 'var(--pill-gap)' }}\n          >\n            {items.map((item, i) => {\n              const isActive = activeHref === item.href;\n\n              const pillStyle = {\n                background: 'var(--pill-bg, #fff)',\n                color: 'var(--pill-text, var(--base, #000))',\n                paddingLeft: 'var(--pill-pad-x)',\n                paddingRight: 'var(--pill-pad-x)'\n              };\n\n              const PillContent = (\n                <>\n                  <span\n                    className=\"hover-circle absolute left-1/2 bottom-0 rounded-full z-[1] block pointer-events-none\"\n                    style={{\n                      background: 'var(--base, #000)',\n                      willChange: 'transform'\n                    }}\n                    aria-hidden=\"true\"\n                    ref={el => {\n                      circleRefs.current[i] = el;\n                    }}\n                  />\n                  <span className=\"label-stack relative inline-block leading-[1] z-[2]\">\n                    <span\n                      className=\"pill-label relative z-[2] inline-block leading-[1]\"\n                      style={{ willChange: 'transform' }}\n                    >\n                      {item.label}\n                    </span>\n                    <span\n                      className=\"pill-label-hover absolute left-0 top-0 z-[3] inline-block\"\n                      style={{\n                        color: 'var(--hover-text, #fff)',\n                        willChange: 'transform, opacity'\n                      }}\n                      aria-hidden=\"true\"\n                    >\n                      {item.label}\n                    </span>\n                  </span>\n                  {isActive && (\n                    <span\n                      className=\"absolute left-1/2 -bottom-[6px] -translate-x-1/2 w-3 h-3 rounded-full z-[4]\"\n                      style={{ background: 'var(--base, #000)' }}\n                      aria-hidden=\"true\"\n                    />\n                  )}\n                </>\n              );\n\n              const basePillClasses =\n                'relative overflow-hidden inline-flex items-center justify-center h-full no-underline rounded-full box-border font-semibold text-[16px] leading-[0] uppercase tracking-[0.2px] whitespace-nowrap cursor-pointer px-0';\n\n              return (\n                <li key={item.href} role=\"none\" className=\"flex h-full\">\n                  {isRouterLink(item.href) ? (\n                    <Link\n                      role=\"menuitem\"\n                      to={item.href}\n                      className={basePillClasses}\n                      style={pillStyle}\n                      aria-label={item.ariaLabel || item.label}\n                      onMouseEnter={() => handleEnter(i)}\n                      onMouseLeave={() => handleLeave(i)}\n                    >\n                      {PillContent}\n                    </Link>\n                  ) : (\n                    <a\n                      role=\"menuitem\"\n                      href={item.href}\n                      className={basePillClasses}\n                      style={pillStyle}\n                      aria-label={item.ariaLabel || item.label}\n                      onMouseEnter={() => handleEnter(i)}\n                      onMouseLeave={() => handleLeave(i)}\n                    >\n                      {PillContent}\n                    </a>\n                  )}\n                </li>\n              );\n            })}\n          </ul>\n        </div>\n\n        <button\n          ref={hamburgerRef}\n          onClick={toggleMobileMenu}\n          aria-label=\"Toggle menu\"\n          aria-expanded={isMobileMenuOpen}\n          className=\"md:hidden rounded-full border-0 flex flex-col items-center justify-center gap-1 cursor-pointer p-0 relative\"\n          style={{\n            width: 'var(--nav-h)',\n            height: 'var(--nav-h)',\n            background: 'var(--base, #000)'\n          }}\n        >\n          <span\n            className=\"hamburger-line w-4 h-0.5 rounded origin-center transition-all duration-[10ms] ease-[cubic-bezier(0.25,0.1,0.25,1)]\"\n            style={{ background: 'var(--pill-bg, #fff)' }}\n          />\n          <span\n            className=\"hamburger-line w-4 h-0.5 rounded origin-center transition-all duration-[10ms] ease-[cubic-bezier(0.25,0.1,0.25,1)]\"\n            style={{ background: 'var(--pill-bg, #fff)' }}\n          />\n        </button>\n      </nav>\n\n      <div\n        ref={mobileMenuRef}\n        className=\"md:hidden absolute top-[3em] left-4 right-4 rounded-[27px] shadow-[0_8px_32px_rgba(0,0,0,0.12)] z-[998] origin-top\"\n        style={{\n          ...cssVars,\n          background: 'var(--base, #f0f0f0)'\n        }}\n      >\n        <ul className=\"list-none m-0 p-[3px] flex flex-col gap-[3px]\">\n          {items.map(item => {\n            const defaultStyle = {\n              background: 'var(--pill-bg, #fff)',\n              color: 'var(--pill-text, #fff)'\n            };\n            const hoverIn = e => {\n              e.currentTarget.style.background = 'var(--base)';\n              e.currentTarget.style.color = 'var(--hover-text, #fff)';\n            };\n            const hoverOut = e => {\n              e.currentTarget.style.background = 'var(--pill-bg, #fff)';\n              e.currentTarget.style.color = 'var(--pill-text, #fff)';\n            };\n\n            const linkClasses =\n              'block py-3 px-4 text-[16px] font-medium rounded-[50px] transition-all duration-200 ease-[cubic-bezier(0.25,0.1,0.25,1)]';\n\n            return (\n              <li key={item.href}>\n                {isRouterLink(item.href) ? (\n                  <Link\n                    to={item.href}\n                    className={linkClasses}\n                    style={defaultStyle}\n                    onMouseEnter={hoverIn}\n                    onMouseLeave={hoverOut}\n                    onClick={() => setIsMobileMenuOpen(false)}\n                  >\n                    {item.label}\n                  </Link>\n                ) : (\n                  <a\n                    href={item.href}\n                    className={linkClasses}\n                    style={defaultStyle}\n                    onMouseEnter={hoverIn}\n                    onMouseLeave={hoverOut}\n                    onClick={() => setIsMobileMenuOpen(false)}\n                  >\n                    {item.label}\n                  </a>\n                )}\n              </li>\n            );\n          })}\n        </ul>\n      </div>\n    </div>\n  );\n};\n\nexport default PillNav;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"react-router-dom@^6.30.1",
		"gsap@^3.13.0"
	]
}