{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "navbar-gooey-bloom",
  "type": "registry:block",
  "title": "Navbar Gooey Bloom",
  "description": "A glass navbar whose hover highlight is liquid: a lime pool stretches from link to link with a droplet dragging behind, while the label inverts to ink. Menus swell out of a drop into one gooey panel that leans toward its trigger and morphs between layouts, and inside it a lime blob flows from icon to icon.",
  "dependencies": [
    "gsap",
    "@gsap/react"
  ],
  "registryDependencies": [
    "https://tween-ui.vercel.app/r/dot-bloom-button.json"
  ],
  "cssVars": {
    "theme": {}
  },
  "files": [
    {
      "path": "components/tweenui/navbar-gooey-bloom.tsx",
      "type": "registry:component",
      "target": "components/tweenui/navbar-gooey-bloom.tsx",
      "content": "'use client';\n\nimport {\n  useCallback,\n  useEffect,\n  useId,\n  useRef,\n  useState,\n  type ComponentPropsWithoutRef,\n  type MouseEventHandler,\n  type ReactNode,\n} from 'react';\nimport { useGSAP } from '@gsap/react';\nimport gsap from 'gsap';\nimport { cn } from '@/lib/utils';\nimport DotBloomButton from './dot-bloom-button';\n\ngsap.registerPlugin(useGSAP);\n\nconst prefersReducedMotion = () =>\n  typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\nexport type NavbarGooeyBloomIcon =\n  'bloom' | 'drop' | 'orbit' | 'spark' | 'layers' | 'wave' | 'bolt' | 'shield';\n\nexport interface NavbarGooeyBloomLink {\n  label: string;\n  href: string;\n  description?: string;\n  icon?: NavbarGooeyBloomIcon;\n}\n\nexport interface NavbarGooeyBloomFeature {\n  title: string;\n  description: string;\n  href: string;\n  cta?: string;\n  image?: string;\n}\n\nexport interface NavbarGooeyBloomMenu {\n  label: string;\n  href?: string;\n  columns?: NavbarGooeyBloomLink[][];\n  feature?: NavbarGooeyBloomFeature;\n}\n\nexport interface NavbarGooeyBloomProps extends Omit<\n  ComponentPropsWithoutRef<'section'>,\n  'children'\n> {\n  brand?: string;\n  logo?: ReactNode;\n  homeHref?: string;\n  menus?: NavbarGooeyBloomMenu[];\n  currentHref?: string;\n  actionLabel?: string;\n  onActionClick?: MouseEventHandler<HTMLButtonElement>;\n  children?: ReactNode;\n}\n\nconst CLOSE_DELAY = 160;\nconst HOVER_INTENT = 70;\nconst HIDE_AFTER = 220;\nconst PANEL_GAP = 12;\nconst PANEL_RADIUS = 26;\nconst DROP_SIZE = 22;\nconst EDGE_LEAD = 520;\nconst EDGE_TRAIL = 170;\nconst TAIL_PULL = 110;\nconst PANEL_LEAD = 300;\nconst PANEL_TRAIL = 140;\nconst PANEL_PULL = 260;\nconst BUMP_RADIUS = 14;\nconst BUMP_RISE = 6;\nconst BUMP_PULL = 150;\nconst ICON_SIZE = 32;\nconst ICON_DROP = 14;\nconst SCROLL_TOLERANCE = 6;\nconst DESKTOP_QUERY = '(min-width: 1024px)';\n\nconst CONTAINER = 'mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8';\nconst EASE_OUT_EXPO = 'ease-[cubic-bezier(0.16,1,0.3,1)]';\nconst focusRing =\n  'focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[#c6f56f]';\n\nconst TWEEN_MARK = (\n  <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" className=\"size-7 shrink-0\">\n    <rect width=\"24\" height=\"24\" rx=\"6\" fill=\"#045f64\" />\n    <path\n      d=\"M5 19 C 9.5 8.9, 5 5, 19 5\"\n      fill=\"none\"\n      stroke=\"#c6f56f\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n    />\n    <circle cx=\"19\" cy=\"5\" r=\"1.8\" fill=\"#c6f56f\" />\n  </svg>\n);\n\nconst ICON_PATHS: Record<NavbarGooeyBloomIcon, ReactNode> = {\n  bloom: (\n    <>\n      <circle cx=\"12\" cy=\"9\" r=\"3.6\" />\n      <circle cx=\"8.6\" cy=\"15\" r=\"3.6\" />\n      <circle cx=\"15.4\" cy=\"15\" r=\"3.6\" />\n    </>\n  ),\n  drop: <path d=\"M12 3.5c3.2 3.4 5.5 6 5.5 9a5.5 5.5 0 1 1-11 0c0-3 2.3-5.6 5.5-9Z\" />,\n  orbit: (\n    <>\n      <circle cx=\"12\" cy=\"12\" r=\"3.1\" />\n      <ellipse cx=\"12\" cy=\"12\" rx=\"9\" ry=\"4.4\" transform=\"rotate(-24 12 12)\" />\n    </>\n  ),\n  spark: <path d=\"M12 3.5l2 6.5 6.5 2-6.5 2-2 6.5-2-6.5-6.5-2 6.5-2 2-6.5Z\" />,\n  layers: (\n    <>\n      <path d=\"M12 4 4 8.5l8 4.5 8-4.5L12 4Z\" />\n      <path d=\"m4 12.5 8 4.5 8-4.5\" />\n    </>\n  ),\n  wave: <path d=\"M3 13.5c2-3.6 4-3.6 6 0s4 3.6 6 0 4-3.6 6 0\" />,\n  bolt: <path d=\"M13.5 3 6 13.5h5l-1 7.5 8-11h-5l.5-7Z\" />,\n  shield: <path d=\"M12 3.5 19 6v6c0 4.2-3 7-7 8.5-4-1.5-7-4.3-7-8.5V6l7-2.5Z\" />,\n};\n\nconst DEFAULT_MENUS: NavbarGooeyBloomMenu[] = [\n  {\n    label: 'Work',\n    columns: [\n      [\n        {\n          label: 'Case studies',\n          href: '#',\n          description: 'Shipped products, start to finish.',\n          icon: 'bloom',\n        },\n        {\n          label: 'Experiments',\n          href: '#',\n          description: 'Small ideas, pushed too far.',\n          icon: 'spark',\n        },\n        {\n          label: 'Playground',\n          href: '#',\n          description: 'Live code you can poke at.',\n          icon: 'orbit',\n        },\n        {\n          label: 'Archive',\n          href: '#',\n          description: 'Everything since 2019.',\n          icon: 'layers',\n        },\n      ],\n      [\n        {\n          label: 'Motion kit',\n          href: '#',\n          description: 'Easing, springs and timing.',\n          icon: 'wave',\n        },\n        {\n          label: 'Interface kit',\n          href: '#',\n          description: 'Bars, panels and overlays.',\n          icon: 'drop',\n        },\n        {\n          label: 'Brand systems',\n          href: '#',\n          description: 'Marks that know how to move.',\n          icon: 'shield',\n        },\n        {\n          label: 'Performance',\n          href: '#',\n          description: 'Sixty frames, no excuses.',\n          icon: 'bolt',\n        },\n      ],\n    ],\n    feature: {\n      title: 'Bloom 2.0',\n      description: 'The metaball engine under this bar, documented end to end.',\n      href: '#',\n      cta: 'Read the notes',\n    },\n  },\n  {\n    label: 'Components',\n    columns: [\n      [\n        {\n          label: 'Navbars',\n          href: '/components',\n          description: 'Eleven ways to hold a menu.',\n          icon: 'layers',\n        },\n        {\n          label: 'Buttons',\n          href: '/components',\n          description: 'Press, roll, bloom.',\n          icon: 'drop',\n        },\n        {\n          label: 'Carousels',\n          href: '/components',\n          description: 'Arcs, shutters and reels.',\n          icon: 'orbit',\n        },\n        {\n          label: 'Loaders',\n          href: '/components',\n          description: 'Waiting, but on purpose.',\n          icon: 'spark',\n        },\n        {\n          label: 'Type',\n          href: '/components',\n          description: 'Letters that misbehave.',\n          icon: 'wave',\n        },\n      ],\n    ],\n  },\n  {\n    label: 'Studio',\n    columns: [\n      [\n        { label: 'About', href: '#', description: 'Four people, one room.', icon: 'bloom' },\n        { label: 'Team', href: '#', description: 'Who makes what.', icon: 'shield' },\n        { label: 'Careers', href: '#', description: 'Two seats open.', icon: 'bolt' },\n        { label: 'Press', href: '#', description: 'Logos and words.', icon: 'layers' },\n      ],\n      [\n        { label: 'Contact', href: '#', description: 'Say hello properly.', icon: 'drop' },\n        {\n          label: 'Changelog',\n          href: '#',\n          description: 'What moved this week.',\n          icon: 'wave',\n        },\n        { label: 'License', href: '#', description: 'What you may ship.', icon: 'orbit' },\n        {\n          label: 'Support',\n          href: '#',\n          description: 'We answer on weekdays.',\n          icon: 'spark',\n        },\n      ],\n    ],\n  },\n  { label: 'Journal', href: '#' },\n];\n\nconst cleanHref = (href: string) => href.split(/[?#]/)[0]?.replace(/\\/+$/, '') || '/';\n\nconst isExternal = (href: string) => /^https?:/.test(href);\n\nconst externalProps = (href: string) =>\n  isExternal(href) ? { target: '_blank', rel: 'noopener noreferrer' } : {};\n\nconst clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);\n\nconst panelWidth = (menu: NavbarGooeyBloomMenu) => {\n  const columns = menu.columns?.length ?? 0;\n  if (menu.feature) return columns > 1 ? 880 : 720;\n  return columns > 1 ? 720 : 360;\n};\n\nconst panelGrid = (menu: NavbarGooeyBloomMenu) => {\n  const columns = menu.columns?.length ?? 0;\n  if (menu.feature) return columns > 1 ? 'grid-cols-[1fr_1fr_15rem]' : 'grid-cols-[1fr_15rem]';\n  return columns > 1 ? 'grid-cols-2' : 'grid-cols-1';\n};\n\ninterface Spring {\n  x: number;\n  v: number;\n  t: number;\n}\n\ninterface GooMotion {\n  left: Spring;\n  right: Spring;\n  tail: Spring;\n  panelLeft: Spring;\n  panelRight: Spring;\n  panelHeight: Spring;\n  bump: Spring;\n  rowTop: Spring;\n  rowBottom: Spring;\n  rowLeft: Spring;\n  rowWidth: Spring;\n  iconTop: Spring;\n  iconBottom: Spring;\n  iconTail: Spring;\n  iconOffset: number;\n  look: { scale: number; alpha: number };\n  hover: { alpha: number };\n  pillShown: boolean;\n  panelShown: boolean;\n  hoverShown: boolean;\n  barBottom: number;\n  awake: boolean;\n}\n\nconst spring = (): Spring => ({ x: 0, v: 0, t: 0 });\n\nconst place = (target: Spring, value: number) => {\n  target.x = value;\n  target.v = 0;\n  target.t = value;\n};\n\nconst snap = (target: Spring) => {\n  target.x = target.t;\n  target.v = 0;\n};\n\nconst stepSpring = (target: Spring, stiffness: number, dt: number) => {\n  const force = stiffness * (target.t - target.x) - 2 * Math.sqrt(stiffness) * target.v;\n  target.v += force * dt;\n  target.x += target.v * dt;\n};\n\nconst atRest = (target: Spring, epsilon: number) =>\n  Math.abs(target.t - target.x) < epsilon && Math.abs(target.v) < epsilon;\n\nconst createMotion = (): GooMotion => ({\n  left: spring(),\n  right: spring(),\n  tail: spring(),\n  panelLeft: spring(),\n  panelRight: spring(),\n  panelHeight: spring(),\n  bump: spring(),\n  rowTop: spring(),\n  rowBottom: spring(),\n  rowLeft: spring(),\n  rowWidth: spring(),\n  iconTop: spring(),\n  iconBottom: spring(),\n  iconTail: spring(),\n  iconOffset: 0,\n  look: { scale: 0.4, alpha: 0 },\n  hover: { alpha: 0 },\n  pillShown: false,\n  panelShown: false,\n  hoverShown: false,\n  barBottom: 60,\n  awake: false,\n});\n\nconst springsOf = (motion: GooMotion) => [\n  motion.left,\n  motion.right,\n  motion.tail,\n  motion.panelLeft,\n  motion.panelRight,\n  motion.panelHeight,\n  motion.bump,\n  motion.rowTop,\n  motion.rowBottom,\n  motion.rowLeft,\n  motion.rowWidth,\n  motion.iconTop,\n  motion.iconBottom,\n  motion.iconTail,\n];\n\nconst settleMotion = (motion: GooMotion) => {\n  const center = (motion.left.t + motion.right.t) / 2;\n  motion.tail.t = center;\n  motion.bump.t = center;\n  motion.iconTail.t = (motion.iconTop.t + motion.iconBottom.t) / 2;\n  springsOf(motion).forEach(snap);\n};\n\nconst motionAtRest = (motion: GooMotion) =>\n  springsOf(motion).every((item) => atRest(item, 0.05)) &&\n  !gsap.isTweening(motion.look) &&\n  !gsap.isTweening(motion.hover);\n\nconst steer = (start: Spring, end: Spring) => {\n  const heading = start.t + end.t - start.x - end.x;\n  return {\n    start: heading > 1 ? EDGE_TRAIL : EDGE_LEAD,\n    end: heading < -1 ? EDGE_TRAIL : EDGE_LEAD,\n  };\n};\n\nconst px = (value: number) => Math.round(value * 100) / 100;\n\nconst roundedRect = (left: number, right: number, top: number, height: number) => {\n  const radius = px(Math.max(0, Math.min(PANEL_RADIUS, (right - left) / 2, height / 2)));\n  const bottom = px(top + height);\n  const l = px(left);\n  const r = px(right);\n  return `M${px(l + radius)},${top}H${px(r - radius)}A${radius},${radius} 0 0 1 ${r},${px(top + radius)}V${px(bottom - radius)}A${radius},${radius} 0 0 1 ${px(r - radius)},${bottom}H${px(l + radius)}A${radius},${radius} 0 0 1 ${l},${px(bottom - radius)}V${px(top + radius)}A${radius},${radius} 0 0 1 ${px(l + radius)},${top}Z`;\n};\n\nconst ArrowIcon = ({ className }: { className?: string }) => {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"1.8\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <path d=\"M5 12h13M13 6l6 6-6 6\" />\n    </svg>\n  );\n};\n\nconst ChevronIcon = ({ className }: { className?: string }) => {\n  return (\n    <svg aria-hidden=\"true\" viewBox=\"0 0 10 6\" fill=\"none\" className={className}>\n      <path\n        d=\"M1 1l4 4 4-4\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.4\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n};\n\nconst MenuGlyph = ({ icon, className }: { icon: NavbarGooeyBloomIcon; className?: string }) => {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"1.5\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      {ICON_PATHS[icon]}\n    </svg>\n  );\n};\n\nconst FeatureArt = () => {\n  const rawId = useId().replace(/[^a-zA-Z0-9_-]/g, '');\n  const gooId = `feature-goo-${rawId}`;\n  const limeId = `feature-lime-${rawId}`;\n\n  return (\n    <span aria-hidden=\"true\" className=\"absolute inset-0 overflow-hidden bg-[#081f1a]\">\n      <span className=\"absolute inset-0 bg-[radial-gradient(120%_130%_at_50%_-10%,#17493c,#081f1a_70%)]\" />\n      <span className=\"absolute inset-0 [background-image:radial-gradient(rgb(159_212_214/0.16)_1px,transparent_1px)] [mask-image:radial-gradient(72%_72%_at_50%_50%,#000,transparent)] [background-size:10px_10px] opacity-60\" />\n      <span className=\"absolute top-1/2 left-1/2 size-32 -translate-x-1/2 -translate-y-1/2 rounded-full bg-[#c6f56f]/15 blur-2xl\" />\n      <svg viewBox=\"0 0 240 112\" className=\"absolute inset-0 size-full\">\n        <defs>\n          <filter id={gooId} x=\"-25%\" y=\"-80%\" width=\"150%\" height=\"260%\">\n            <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"3\" result=\"blur\" />\n            <feColorMatrix\n              in=\"blur\"\n              mode=\"matrix\"\n              values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7\"\n              result=\"goo\"\n            />\n            <feBlend in=\"SourceGraphic\" in2=\"goo\" />\n          </filter>\n          <linearGradient id={limeId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n            <stop offset=\"0\" stopColor=\"#e9ffc2\" />\n            <stop offset=\"0.45\" stopColor=\"#c6f56f\" />\n            <stop offset=\"1\" stopColor=\"#a6df58\" />\n          </linearGradient>\n        </defs>\n        <g stroke=\"#9fd4d6\" fill=\"none\">\n          <ellipse cx=\"120\" cy=\"56\" rx=\"88\" ry=\"30\" strokeOpacity=\"0.14\" />\n          <ellipse cx=\"120\" cy=\"56\" rx=\"62\" ry=\"42\" strokeOpacity=\"0.07\" />\n        </g>\n        <g filter={`url(#${gooId})`} fill={`url(#${limeId})`}>\n          <rect x=\"74\" y=\"42\" width=\"84\" height=\"30\" rx=\"15\" />\n          <circle cx=\"66\" cy=\"60\" r=\"8.5\" />\n          <circle cx=\"162\" cy=\"52\" r=\"7\" />\n          <circle cx=\"168\" cy=\"63\" r=\"4.5\" />\n        </g>\n      </svg>\n    </span>\n  );\n};\n\nconst SamplePage = () => {\n  return (\n    <div id=\"top\" className=\"[container-type:size] relative h-full overflow-hidden bg-[#03110f]\">\n      <span\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 bg-[radial-gradient(46%_60%_at_50%_-10%,rgb(198_245_111/0.12),transparent_70%),radial-gradient(50%_60%_at_90%_20%,rgb(4_95_100/0.55),transparent_72%),radial-gradient(45%_55%_at_4%_60%,rgb(4_95_100/0.3),transparent_70%)]\"\n      />\n      <span\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 bg-[repeating-linear-gradient(90deg,rgb(159_212_214/0.05)_0_1px,transparent_1px_56px),repeating-linear-gradient(0deg,rgb(159_212_214/0.04)_0_1px,transparent_1px_56px)] [mask-image:radial-gradient(ellipse_70%_60%_at_50%_30%,black,transparent)]\"\n      />\n\n      <section className=\"relative mx-auto flex h-full max-w-7xl flex-col justify-center px-4 pt-[clamp(5rem,16cqh,8rem)] pb-[clamp(1.5rem,5cqh,3.5rem)] sm:px-6 lg:px-8\">\n        <p className=\"inline-flex w-fit items-center gap-2.5 rounded-full bg-white/[0.04] py-[clamp(0.4rem,1.1cqh,0.55rem)] pr-4 pl-2.5 text-[10px] leading-none font-medium tracking-[0.22em] text-[#9fd4d6] uppercase ring-1 ring-white/10\">\n          <span\n            aria-hidden=\"true\"\n            className=\"size-2 rounded-full bg-[#c6f56f] shadow-[0_0_6px_#c6f56f,0_0_16px_#c6f56f]\"\n          />\n          Metaball navigation\n        </p>\n        <h2 className=\"mt-[clamp(1.1rem,3.4cqh,1.75rem)] max-w-[36rem] text-[clamp(2.35rem,7.8cqh,4.75rem)] leading-[0.95] font-medium tracking-[-0.045em] text-balance text-[#e8f3f2]\">\n          The menu that <span className=\"text-[#c6f56f]\">melts</span> between links.\n        </h2>\n        <p className=\"mt-[clamp(0.9rem,2.4cqh,1.5rem)] max-w-[36rem] text-[clamp(0.84rem,1.9cqh,1rem)] leading-relaxed text-pretty text-[#9fd4d6]/70\">\n          Point at a link. A pool of lime stretches across the bar, sheds a few droplets on the way,\n          and settles under the label. Open a menu and the card below re-forms around it.\n        </p>\n        <div className=\"mt-[clamp(1.4rem,4cqh,2.25rem)] flex flex-wrap items-center gap-3\">\n          <a\n            href=\"#top\"\n            className={cn(\n              'inline-flex h-[clamp(2.5rem,6.2cqh,2.75rem)] items-center gap-2 rounded-full bg-[#c6f56f] px-[clamp(1rem,2.4cqh,1.25rem)] text-[clamp(0.82rem,1.8cqh,0.875rem)] font-medium text-[#03110f] transition-transform duration-300 active:scale-[0.97]',\n              EASE_OUT_EXPO,\n              focusRing\n            )}\n          >\n            See it again\n            <ArrowIcon className=\"size-4\" />\n          </a>\n          <a\n            href=\"#top\"\n            className={cn(\n              'inline-flex h-[clamp(2.5rem,6.2cqh,2.75rem)] items-center rounded-full border border-[#9fd4d6]/20 px-[clamp(1rem,2.4cqh,1.25rem)] text-[clamp(0.82rem,1.8cqh,0.875rem)] text-[#e8f3f2]/80 transition-colors duration-300 hover:border-[#9fd4d6]/45 hover:text-[#e8f3f2]',\n              focusRing\n            )}\n          >\n            Read the props\n          </a>\n        </div>\n      </section>\n    </div>\n  );\n};\n\nconst NavbarGooeyBloom = ({\n  brand = 'Tween UI',\n  logo = TWEEN_MARK,\n  homeHref = '/',\n  menus = DEFAULT_MENUS,\n  currentHref = '/',\n  actionLabel = 'Get started',\n  onActionClick,\n  children,\n  className,\n  ...props\n}: NavbarGooeyBloomProps) => {\n  const [openKey, setOpenKey] = useState<string | null>(null);\n  const [mobileOpen, setMobileOpen] = useState(false);\n  const [accordion, setAccordion] = useState<string | null>(null);\n\n  const rawId = useId().replace(/[^a-zA-Z0-9_-]/g, '');\n  const gooId = `goo-${rawId}`;\n  const linkGooId = `link-goo-${rawId}`;\n  const panelGooId = `panel-goo-${rawId}`;\n  const panelId = `panel-${rawId}`;\n  const mobileId = `mobile-${rawId}`;\n\n  const rootRef = useRef<HTMLElement>(null);\n  const barRef = useRef<HTMLDivElement>(null);\n  const shellRef = useRef<HTMLDivElement>(null);\n  const scrollerRef = useRef<HTMLDivElement>(null);\n  const panelRef = useRef<HTMLDivElement>(null);\n  const bodyRef = useRef<HTMLDivElement>(null);\n  const shapeRef = useRef<SVGPathElement>(null);\n  const bumpRef = useRef<SVGCircleElement>(null);\n  const hoverRef = useRef<{ row: HTMLElement; blob: HTMLElement; drop: HTMLElement } | null>(null);\n  const pillRef = useRef<HTMLSpanElement>(null);\n  const coreRef = useRef<HTMLSpanElement>(null);\n  const dropRef = useRef<HTMLSpanElement>(null);\n  const inkRefs = useRef<Array<HTMLSpanElement | null>>([]);\n  const inkBoxes = useRef<Array<{ left: number; width: number } | null>>([]);\n  const motion = useRef<GooMotion>(createMotion());\n  const activeRef = useRef<HTMLElement | null>(null);\n  const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({});\n  const triggerRefs = useRef<Record<string, HTMLButtonElement | null>>({});\n  const closeTimer = useRef<number | null>(null);\n  const hoverTimer = useRef<number | null>(null);\n  const pendingHover = useRef<{ menu: NavbarGooeyBloomMenu; el: HTMLElement } | null>(null);\n  const pointerPos = useRef({ x: 0, y: 0 });\n  const mobileRef = useRef<HTMLDivElement>(null);\n  const toggleRef = useRef<HTMLButtonElement>(null);\n  const mobileCloseRef = useRef<HTMLButtonElement>(null);\n  const openRef = useRef<string | null>(null);\n  const skipFocusOpen = useRef(false);\n\n  const current = (href: string) => {\n    if (href.startsWith('#')) return false;\n    const link = cleanHref(href);\n    const page = cleanHref(currentHref);\n    return link === page || (link !== '/' && page.startsWith(`${link}/`));\n  };\n\n  const { contextSafe } = useGSAP(\n    () => {\n      if (prefersReducedMotion()) return;\n      const bar = barRef.current;\n      if (!bar) return;\n      const intro = bar.querySelectorAll('[data-intro]');\n      gsap.fromTo(\n        bar,\n        { y: -28, autoAlpha: 0 },\n        {\n          y: 0,\n          autoAlpha: 1,\n          duration: 1,\n          ease: 'expo.out',\n          clearProps: 'transform,opacity,visibility',\n        }\n      );\n      gsap.fromTo(\n        intro,\n        { y: -8, autoAlpha: 0 },\n        {\n          y: 0,\n          autoAlpha: 1,\n          duration: 0.7,\n          ease: 'power3.out',\n          stagger: 0.06,\n          delay: 0.12,\n          clearProps: 'transform,opacity,visibility',\n        }\n      );\n    },\n    { scope: rootRef }\n  );\n\n  const measureInk = useCallback(() => {\n    const bar = barRef.current;\n    if (!bar) return;\n    const barRect = bar.getBoundingClientRect();\n    inkRefs.current.forEach((ink, index) => {\n      const host = ink?.parentElement;\n      if (!host) return;\n      const box = host.getBoundingClientRect();\n      inkBoxes.current[index] = { left: box.left - barRect.left, width: box.width };\n    });\n  }, []);\n\n  const draw = useCallback(() => {\n    const m = motion.current;\n    const left = m.left.x;\n    const right = m.right.x;\n    const width = Math.max(right - left, 0);\n    const stretch = width - (m.right.t - m.left.t);\n    const thin = 1 - clamp(stretch / 320, 0, 0.18);\n    const { scale, alpha } = m.look;\n    const pill = pillRef.current;\n    const core = coreRef.current;\n    const drop = dropRef.current;\n\n    if (pill && core && drop) {\n      pill.style.width = `${width}px`;\n      pill.style.transform = `translate3d(${left}px,0,0)`;\n      pill.style.opacity = `${alpha}`;\n      core.style.transform = `scale(${scale},${scale * thin})`;\n      drop.style.transform = `translate3d(${m.tail.x - DROP_SIZE / 2}px,0,0) scale(${scale})`;\n      drop.style.opacity = `${alpha}`;\n    }\n\n    inkRefs.current.forEach((ink, index) => {\n      const box = inkBoxes.current[index];\n      if (!ink || !box) return;\n      const start = clamp(left - box.left, 0, box.width);\n      const end = clamp(right - box.left, 0, box.width);\n      ink.style.clipPath = `inset(-4px ${box.width - end}px -4px ${start}px)`;\n      ink.style.opacity = `${alpha}`;\n    });\n\n    const hover = hoverRef.current;\n    if (hover) {\n      const alpha = m.hover.alpha;\n      const iconHeight = Math.max(m.iconBottom.x - m.iconTop.x, 0);\n      const iconStretch = iconHeight - (m.iconBottom.t - m.iconTop.t);\n      const iconWidth = ICON_SIZE * (1 - clamp(iconStretch / 160, 0, 0.3));\n      const iconX = m.rowLeft.x + m.iconOffset;\n      hover.row.style.transform = `translate3d(${m.rowLeft.x}px,${m.rowTop.x}px,0)`;\n      hover.row.style.width = `${Math.max(m.rowWidth.x, 0)}px`;\n      hover.row.style.height = `${Math.max(m.rowBottom.x - m.rowTop.x, 0)}px`;\n      hover.row.style.opacity = `${alpha}`;\n      hover.blob.style.transform = `translate3d(${iconX + (ICON_SIZE - iconWidth) / 2}px,${m.iconTop.x}px,0)`;\n      hover.blob.style.width = `${iconWidth}px`;\n      hover.blob.style.height = `${iconHeight}px`;\n      hover.blob.style.opacity = `${alpha}`;\n      hover.drop.style.transform = `translate3d(${iconX + (ICON_SIZE - ICON_DROP) / 2}px,${m.iconTail.x - ICON_DROP / 2}px,0)`;\n      hover.drop.style.opacity = `${alpha}`;\n    }\n\n    const shape = shapeRef.current;\n    const bump = bumpRef.current;\n    const body = bodyRef.current;\n    if (!m.panelShown || !shape || !bump || !body) return;\n    const top = m.barBottom + PANEL_GAP;\n    const height = Math.max(m.panelHeight.x, 0);\n    const panelWidth = Math.max(m.panelRight.x - m.panelLeft.x, 0);\n    const bumpRadius = Math.max(0, Math.min(BUMP_RADIUS, panelWidth / 4, height / 3));\n    shape.setAttribute('d', roundedRect(m.panelLeft.x, m.panelRight.x, top, height));\n    const edge = Math.min(PANEL_RADIUS, panelWidth / 2) + bumpRadius;\n    const bumpX =\n      panelWidth > edge * 2\n        ? clamp(m.bump.x, m.panelLeft.x + edge, m.panelRight.x - edge)\n        : m.panelLeft.x + panelWidth / 2;\n    bump.setAttribute('cx', `${px(bumpX)}`);\n    bump.setAttribute('cy', `${px(top + bumpRadius - BUMP_RISE * (bumpRadius / BUMP_RADIUS))}`);\n    bump.setAttribute('r', `${px(bumpRadius)}`);\n    body.style.top = `${top}px`;\n    body.style.clipPath = `path('${roundedRect(m.panelLeft.x, m.panelRight.x, 0, height)}')`;\n  }, []);\n\n  useEffect(() => {\n    const tick = (_time: number, deltaTime: number) => {\n      const m = motion.current;\n      if (!m.awake) return;\n      const elapsed = Math.min(deltaTime / 1000, 0.05);\n      const steps = Math.max(1, Math.ceil(elapsed * 120));\n      const dt = elapsed / steps;\n      const pill = steer(m.left, m.right);\n      const row = steer(m.rowTop, m.rowBottom);\n      const icon = steer(m.iconTop, m.iconBottom);\n      const panelHeading = m.panelLeft.t + m.panelRight.t - m.panelLeft.x - m.panelRight.x;\n      const panelLeftPull = panelHeading > 1 ? PANEL_TRAIL : PANEL_LEAD;\n      const panelRightPull = panelHeading < -1 ? PANEL_TRAIL : PANEL_LEAD;\n\n      for (let step = 0; step < steps; step += 1) {\n        stepSpring(m.left, pill.start, dt);\n        stepSpring(m.right, pill.end, dt);\n        const center = (m.left.x + m.right.x) / 2;\n        m.tail.t = center;\n        stepSpring(m.tail, TAIL_PULL, dt);\n        if (m.panelShown) {\n          m.bump.t = center;\n          stepSpring(m.panelLeft, panelLeftPull, dt);\n          stepSpring(m.panelRight, panelRightPull, dt);\n          stepSpring(m.panelHeight, PANEL_PULL, dt);\n          stepSpring(m.bump, BUMP_PULL, dt);\n        }\n        stepSpring(m.rowTop, row.start, dt);\n        stepSpring(m.rowBottom, row.end, dt);\n        stepSpring(m.rowLeft, EDGE_LEAD, dt);\n        stepSpring(m.rowWidth, EDGE_LEAD, dt);\n        stepSpring(m.iconTop, icon.start, dt);\n        stepSpring(m.iconBottom, icon.end, dt);\n        m.iconTail.t = (m.iconTop.x + m.iconBottom.x) / 2;\n        stepSpring(m.iconTail, TAIL_PULL, dt);\n      }\n\n      const center = (m.left.x + m.right.x) / 2;\n      m.tail.x = m.right.x - m.left.x > 4 ? clamp(m.tail.x, m.left.x + 2, m.right.x - 2) : center;\n      m.iconTail.x = clamp(m.iconTail.x, m.iconTop.x - 2, m.iconBottom.x + 2);\n\n      if (motionAtRest(m)) {\n        settleMotion(m);\n        m.awake = false;\n      }\n      draw();\n    };\n\n    gsap.ticker.add(tick);\n    return () => gsap.ticker.remove(tick);\n  }, [draw]);\n\n  const hideBlob = contextSafe(() => {\n    const m = motion.current;\n    if (!m.pillShown) return;\n    m.pillShown = false;\n    const center = (m.left.t + m.right.t) / 2;\n    m.left.t = center - 20;\n    m.right.t = center + 20;\n    gsap.killTweensOf(m.look);\n    if (prefersReducedMotion()) {\n      m.look.scale = 0.4;\n      m.look.alpha = 0;\n      settleMotion(m);\n      draw();\n      return;\n    }\n    gsap.to(m.look, { scale: 0.4, alpha: 0, duration: 0.32, ease: 'power2.inOut' });\n    m.awake = true;\n  });\n\n  const moveBlob = contextSafe((el: HTMLElement) => {\n    const bar = barRef.current;\n    if (!bar) return;\n    const m = motion.current;\n    const base = bar.getBoundingClientRect();\n    const box = el.getBoundingClientRect();\n    const left = box.left - base.left;\n    const center = left + box.width / 2;\n    activeRef.current = el;\n    measureInk();\n    gsap.killTweensOf(m.look);\n\n    if (!m.pillShown && m.look.alpha < 0.05) {\n      place(m.left, center - 20);\n      place(m.right, center + 20);\n      place(m.tail, center);\n      m.look.scale = 0.4;\n    }\n    m.pillShown = true;\n    m.left.t = left;\n    m.right.t = left + box.width;\n\n    if (prefersReducedMotion()) {\n      m.look.scale = 1;\n      m.look.alpha = 1;\n      settleMotion(m);\n      draw();\n      return;\n    }\n    gsap.to(m.look, { scale: 1, alpha: 1, duration: 0.45, ease: 'power3.out' });\n    m.awake = true;\n  });\n\n  const resetHover = () => {\n    const m = motion.current;\n    const hover = hoverRef.current;\n    gsap.killTweensOf(m.hover);\n    m.hover.alpha = 0;\n    m.hoverShown = false;\n    hoverRef.current = null;\n    if (hover) gsap.set([hover.row, hover.blob, hover.drop], { opacity: 0 });\n  };\n\n  const moveHover = contextSafe((link: HTMLElement) => {\n    const section = link.closest<HTMLElement>('[data-section]');\n    const icon = link.querySelector<HTMLElement>('[data-icon]');\n    const row = section?.querySelector<HTMLElement>('[data-hover-row]');\n    const blob = section?.querySelector<HTMLElement>('[data-hover-blob]');\n    const drop = section?.querySelector<HTMLElement>('[data-hover-drop]');\n    if (!section || !icon || !row || !blob || !drop) return;\n\n    const m = motion.current;\n    if (hoverRef.current && hoverRef.current.row !== row) resetHover();\n    const fresh = !hoverRef.current || (!m.hoverShown && m.hover.alpha < 0.05);\n    hoverRef.current = { row, blob, drop };\n\n    const base = section.getBoundingClientRect();\n    const box = link.getBoundingClientRect();\n    const tile = icon.getBoundingClientRect();\n    const top = box.top - base.top;\n    const left = box.left - base.left;\n    const iconTop = tile.top - base.top;\n    const iconCenter = iconTop + tile.height / 2;\n    m.iconOffset = tile.left - box.left;\n    gsap.killTweensOf(m.hover);\n\n    if (fresh) {\n      place(m.rowTop, top);\n      place(m.rowBottom, top + box.height);\n      place(m.rowLeft, left);\n      place(m.rowWidth, box.width);\n      place(m.iconTop, iconCenter - 4);\n      place(m.iconBottom, iconCenter + 4);\n      place(m.iconTail, iconCenter);\n    }\n    m.hoverShown = true;\n    m.rowTop.t = top;\n    m.rowBottom.t = top + box.height;\n    m.rowLeft.t = left;\n    m.rowWidth.t = box.width;\n    m.iconTop.t = iconTop;\n    m.iconBottom.t = iconTop + tile.height;\n\n    if (prefersReducedMotion()) {\n      m.hover.alpha = 1;\n      settleMotion(m);\n      draw();\n      return;\n    }\n    gsap.to(m.hover, { alpha: 1, duration: 0.3, ease: 'power2.out' });\n    m.awake = true;\n  });\n\n  const hideHover = contextSafe(() => {\n    const m = motion.current;\n    if (!m.hoverShown) return;\n    m.hoverShown = false;\n    const center = (m.iconTop.t + m.iconBottom.t) / 2;\n    m.iconTop.t = center - 4;\n    m.iconBottom.t = center + 4;\n    gsap.killTweensOf(m.hover);\n    if (prefersReducedMotion()) {\n      m.hover.alpha = 0;\n      settleMotion(m);\n      draw();\n      return;\n    }\n    gsap.to(m.hover, { alpha: 0, duration: 0.28, ease: 'power2.out' });\n    m.awake = true;\n  });\n\n  const getLayout = useCallback((trigger: HTMLElement, section: HTMLElement) => {\n    const bar = barRef.current;\n    const width = section.offsetWidth;\n    const height = section.offsetHeight;\n    if (!bar) return { left: 0, width, height, center: width / 2 };\n    const barRect = bar.getBoundingClientRect();\n    const triggerRect = trigger.getBoundingClientRect();\n    const center = triggerRect.left - barRect.left + triggerRect.width / 2;\n    const min = 8 - barRect.left;\n    const max = window.innerWidth - 8 - width - barRect.left;\n    return { left: clamp(center - width / 2, Math.min(min, max), max), width, height, center };\n  }, []);\n\n  const sectionNodes = () =>\n    Object.values(sectionRefs.current).filter((node): node is HTMLDivElement => Boolean(node));\n\n  const openMenu = contextSafe((menu: NavbarGooeyBloomMenu, trigger: HTMLElement) => {\n    const section = sectionRefs.current[menu.label];\n    const panel = panelRef.current;\n    const shell = shellRef.current;\n    if (!section || !panel || !shell) return;\n    if (openRef.current === menu.label) return;\n\n    const previous = openRef.current;\n    const previousSection = previous ? sectionRefs.current[previous] : null;\n    openRef.current = menu.label;\n    setOpenKey(menu.label);\n\n    const m = motion.current;\n    const layout = getLayout(trigger, section);\n    const reduced = prefersReducedMotion();\n    const items = section.querySelectorAll('[data-item]');\n    m.barBottom = shell.offsetHeight;\n    resetHover();\n\n    gsap.killTweensOf([panel, ...sectionNodes()]);\n    sectionNodes().forEach((node) => {\n      if (node !== section && node !== previousSection) gsap.set(node, { opacity: 0 });\n    });\n    gsap.set(section, { left: layout.left, opacity: 1 });\n    gsap.set(panel, { opacity: 1 });\n\n    if (!m.panelShown) {\n      place(m.panelLeft, layout.center - 12);\n      place(m.panelRight, layout.center + 12);\n      place(m.panelHeight, 24);\n      place(m.bump, layout.center);\n      m.panelShown = true;\n    }\n    m.panelLeft.t = layout.left;\n    m.panelRight.t = layout.left + layout.width;\n    m.panelHeight.t = layout.height;\n\n    if (reduced) {\n      if (previousSection && previousSection !== section) {\n        gsap.set(previousSection, { opacity: 0 });\n      }\n      gsap.set(items, { y: 0, opacity: 1 });\n      settleMotion(m);\n      draw();\n      return;\n    }\n\n    m.awake = true;\n    if (previousSection && previousSection !== section) {\n      gsap.to(previousSection, { opacity: 0, duration: 0.14, ease: 'power2.out' });\n    }\n    if (items.length) {\n      gsap.fromTo(\n        items,\n        { y: 10, opacity: 0 },\n        {\n          y: 0,\n          opacity: 1,\n          duration: 0.42,\n          ease: 'power3.out',\n          stagger: 0.02,\n          delay: previous ? 0.05 : 0.12,\n          overwrite: 'auto',\n        }\n      );\n    }\n  });\n\n  const closeMenu = contextSafe((options?: { clearHighlight?: boolean }) => {\n    cancelHover();\n    if (options?.clearHighlight) {\n      hideBlob();\n    }\n    if (!openRef.current) return;\n    openRef.current = null;\n    setOpenKey(null);\n    setAccordion(null);\n\n    const panel = panelRef.current;\n    if (!panel) return;\n    const m = motion.current;\n    const sections = sectionNodes();\n    const center = (m.left.t + m.right.t) / 2;\n    gsap.killTweensOf([panel, ...sections]);\n    m.panelLeft.t = center - 12;\n    m.panelRight.t = center + 12;\n    m.panelHeight.t = 24;\n\n    const finish = () => {\n      m.panelShown = false;\n      resetHover();\n      gsap.set(sections, { opacity: 0 });\n      gsap.set(panel, { opacity: 0 });\n    };\n\n    if (prefersReducedMotion()) {\n      finish();\n      return;\n    }\n    m.awake = true;\n    gsap.to(sections, { opacity: 0, duration: 0.12, ease: 'power2.out' });\n    gsap.to(panel, {\n      opacity: 0,\n      duration: 0.24,\n      delay: 0.08,\n      ease: 'power2.in',\n      onComplete: finish,\n    });\n  });\n\n  const cancelClose = () => {\n    if (closeTimer.current !== null) {\n      window.clearTimeout(closeTimer.current);\n      closeTimer.current = null;\n    }\n  };\n\n  const scheduleClose = contextSafe(() => {\n    cancelHover();\n    cancelClose();\n    closeTimer.current = window.setTimeout(() => {\n      const bar = barRef.current;\n      if (bar) {\n        const rect = bar.getBoundingClientRect();\n        const { x, y } = pointerPos.current;\n        if (\n          x >= rect.left - 2 &&\n          x <= rect.right + 2 &&\n          y >= rect.top - 2 &&\n          y <= rect.bottom + 2\n        ) {\n          return;\n        }\n      }\n      closeMenu({ clearHighlight: true });\n    }, CLOSE_DELAY);\n  });\n\n  const cancelHover = () => {\n    if (hoverTimer.current !== null) {\n      window.clearTimeout(hoverTimer.current);\n      hoverTimer.current = null;\n    }\n    pendingHover.current = null;\n  };\n\n  const handleTriggerEnter = (menu: NavbarGooeyBloomMenu, el: HTMLElement) => {\n    cancelClose();\n    moveBlob(el);\n    pendingHover.current = { menu, el };\n    if (hoverTimer.current !== null) window.clearTimeout(hoverTimer.current);\n    hoverTimer.current = window.setTimeout(() => {\n      hoverTimer.current = null;\n      const next = pendingHover.current;\n      pendingHover.current = null;\n      if (!next) return;\n      if (next.menu.columns?.length) openMenu(next.menu, next.el);\n      else closeMenu();\n    }, HOVER_INTENT);\n  };\n\n  useEffect(() => {\n    const scroller = scrollerRef.current;\n    const bar = barRef.current;\n    if (!scroller || !bar) return;\n    let last = scroller.scrollTop;\n    let hidden = false;\n\n    const onScroll = () => {\n      const y = scroller.scrollTop;\n      const delta = y - last;\n      if (Math.abs(delta) < SCROLL_TOLERANCE) return;\n      last = y;\n      const next = delta > 0 && y > HIDE_AFTER && !openRef.current;\n      if (next === hidden) return;\n      hidden = next;\n      gsap.to(bar, {\n        yPercent: next ? -170 : 0,\n        duration: prefersReducedMotion() ? 0 : next ? 0.5 : 0.7,\n        ease: next ? 'power3.in' : 'expo.out',\n        overwrite: 'auto',\n      });\n    };\n\n    scroller.addEventListener('scroll', onScroll, { passive: true });\n    return () => scroller.removeEventListener('scroll', onScroll);\n  }, []);\n\n  useEffect(() => {\n    const onResize = () => {\n      const bar = barRef.current;\n      const shell = shellRef.current;\n      if (!bar || !shell) return;\n      const m = motion.current;\n      measureInk();\n      const active = activeRef.current;\n      if (active && m.pillShown) {\n        const base = bar.getBoundingClientRect();\n        const box = active.getBoundingClientRect();\n        m.left.t = box.left - base.left;\n        m.right.t = m.left.t + box.width;\n      }\n      const key = openRef.current;\n      const section = key ? sectionRefs.current[key] : null;\n      const trigger = key ? triggerRefs.current[key] : null;\n      if (section && trigger) {\n        const layout = getLayout(trigger, section);\n        m.barBottom = shell.offsetHeight;\n        m.panelLeft.t = layout.left;\n        m.panelRight.t = layout.left + layout.width;\n        m.panelHeight.t = layout.height;\n        gsap.set(section, { left: layout.left });\n      }\n      settleMotion(m);\n      draw();\n    };\n\n    window.addEventListener('resize', onResize);\n    return () => window.removeEventListener('resize', onResize);\n  }, [draw, getLayout, measureInk]);\n\n  useEffect(() => {\n    if (!openKey) return;\n    const desktop = window.matchMedia(DESKTOP_QUERY);\n\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (event.key !== 'Escape') return;\n      const key = openRef.current;\n      closeMenu({ clearHighlight: true });\n      if (key) {\n        skipFocusOpen.current = true;\n        triggerRefs.current[key]?.focus();\n        skipFocusOpen.current = false;\n      }\n    };\n\n    const onPointerDown = (event: PointerEvent) => {\n      if (!barRef.current?.contains(event.target as Node)) {\n        closeMenu({ clearHighlight: true });\n      }\n    };\n\n    const onDesktopChange = (event: MediaQueryListEvent) => {\n      if (!event.matches) closeMenu({ clearHighlight: true });\n    };\n\n    document.addEventListener('keydown', onKeyDown);\n    document.addEventListener('pointerdown', onPointerDown);\n    desktop.addEventListener('change', onDesktopChange);\n    return () => {\n      document.removeEventListener('keydown', onKeyDown);\n      document.removeEventListener('pointerdown', onPointerDown);\n      desktop.removeEventListener('change', onDesktopChange);\n    };\n  }, [openKey, closeMenu]);\n\n  useEffect(() => {\n    const menu = mobileRef.current;\n    if (!menu) return;\n    const reduced = prefersReducedMotion();\n    const items = menu.querySelectorAll('[data-mobile-item]');\n\n    if (mobileOpen) {\n      gsap.set(menu, { visibility: 'visible' });\n      gsap.fromTo(\n        menu,\n        { opacity: 0 },\n        { opacity: 1, duration: reduced ? 0 : 0.28, ease: 'power2.out' }\n      );\n      gsap.fromTo(\n        items,\n        { y: 22, opacity: 0 },\n        {\n          y: 0,\n          opacity: 1,\n          duration: reduced ? 0 : 0.45,\n          ease: 'power3.out',\n          stagger: reduced ? 0 : 0.035,\n          delay: reduced ? 0 : 0.08,\n        }\n      );\n      mobileCloseRef.current?.focus();\n    } else {\n      gsap.to(menu, {\n        opacity: 0,\n        duration: reduced ? 0 : 0.22,\n        ease: 'power2.out',\n        onComplete: () => gsap.set(menu, { visibility: 'hidden' }),\n      });\n    }\n  }, [mobileOpen]);\n\n  useEffect(() => {\n    if (!mobileOpen) return;\n    const desktop = window.matchMedia(DESKTOP_QUERY);\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (event.key !== 'Escape') return;\n      setMobileOpen(false);\n      toggleRef.current?.focus();\n    };\n    const onChange = (event: MediaQueryListEvent) => {\n      if (event.matches) setMobileOpen(false);\n    };\n    document.addEventListener('keydown', onKeyDown);\n    desktop.addEventListener('change', onChange);\n    return () => {\n      document.removeEventListener('keydown', onKeyDown);\n      desktop.removeEventListener('change', onChange);\n    };\n  }, [mobileOpen]);\n\n  useEffect(() => {\n    return () => {\n      cancelClose();\n      if (hoverTimer.current !== null) window.clearTimeout(hoverTimer.current);\n      pendingHover.current = null;\n    };\n  }, []);\n\n  return (\n    <section\n      ref={rootRef}\n      aria-label={`${brand} site`}\n      className={cn(\n        'relative isolate h-svh w-full overflow-hidden bg-[#03110f] text-[#e8f3f2]',\n        className\n      )}\n      {...props}\n    >\n      <svg aria-hidden=\"true\" className=\"pointer-events-none absolute size-0\">\n        <defs>\n          <filter\n            id={gooId}\n            x=\"-40%\"\n            y=\"-250%\"\n            width=\"180%\"\n            height=\"600%\"\n            colorInterpolationFilters=\"sRGB\"\n          >\n            <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"5\" result=\"blur\" />\n            <feColorMatrix\n              in=\"blur\"\n              mode=\"matrix\"\n              values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9\"\n              result=\"goo\"\n            />\n            <feBlend in=\"SourceGraphic\" in2=\"goo\" />\n          </filter>\n          <filter\n            id={linkGooId}\n            x=\"-10%\"\n            y=\"-10%\"\n            width=\"120%\"\n            height=\"120%\"\n            colorInterpolationFilters=\"sRGB\"\n          >\n            <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"3.5\" result=\"blur\" />\n            <feColorMatrix\n              in=\"blur\"\n              mode=\"matrix\"\n              values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -8\"\n              result=\"goo\"\n            />\n            <feComposite in=\"SourceGraphic\" in2=\"goo\" operator=\"atop\" />\n          </filter>\n          <filter\n            id={panelGooId}\n            x=\"-15%\"\n            y=\"-15%\"\n            width=\"130%\"\n            height=\"130%\"\n            colorInterpolationFilters=\"sRGB\"\n          >\n            <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"7\" result=\"blur\" />\n            <feColorMatrix\n              in=\"blur\"\n              mode=\"matrix\"\n              values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -10\"\n            />\n          </filter>\n        </defs>\n      </svg>\n\n      <header\n        className={cn(CONTAINER, 'pointer-events-none absolute inset-x-0 top-0 z-30 pt-5')}\n        onBlur={(event) => {\n          if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {\n            closeMenu({ clearHighlight: true });\n          }\n        }}\n      >\n        <div\n          ref={barRef}\n          className=\"pointer-events-auto relative mx-auto w-full max-w-[26rem] md:max-w-[40rem] lg:max-w-[61.25rem] xl:max-w-[73.75rem]\"\n        >\n          <div\n            ref={shellRef}\n            onPointerEnter={cancelClose}\n            onPointerLeave={(event) => {\n              pointerPos.current = { x: event.clientX, y: event.clientY };\n              scheduleClose();\n              if (!openRef.current) hideBlob();\n            }}\n            className=\"relative flex h-15 items-center justify-between gap-3 rounded-full border border-[#9fd4d6]/12 bg-[#071d1b]/70 px-3 shadow-[0_24px_60px_-32px_rgb(0_0_0/0.95)] backdrop-blur-xl\"\n          >\n            <span\n              aria-hidden=\"true\"\n              className=\"pointer-events-none absolute inset-0 overflow-hidden rounded-full\"\n            >\n              <span\n                className=\"absolute inset-0\"\n                style={{\n                  filter: `url(#${gooId}) drop-shadow(0 0 14px rgb(198 245 111 / 0.2))`,\n                }}\n              >\n                <span\n                  ref={pillRef}\n                  className=\"absolute top-1/2 left-0 h-10 w-0 -translate-y-1/2 opacity-0\"\n                >\n                  <span\n                    ref={coreRef}\n                    className=\"block size-full rounded-full bg-[radial-gradient(120%_160%_at_30%_0%,#eaffc4,#c6f56f_48%,#a8e35a)]\"\n                  />\n                </span>\n                <span\n                  ref={dropRef}\n                  style={{ width: DROP_SIZE, height: DROP_SIZE }}\n                  className=\"absolute top-1/2 left-0 -translate-y-1/2 rounded-full bg-[#c6f56f] opacity-0\"\n                />\n              </span>\n            </span>\n\n            <a\n              href={homeHref}\n              data-intro=\"\"\n              aria-label={`${brand} home`}\n              className={cn(\n                'relative z-10 flex shrink-0 items-center gap-2.5 rounded-full pr-1',\n                focusRing\n              )}\n            >\n              {logo}\n              <span className=\"hidden text-[17px] font-medium tracking-tight sm:block\">\n                {brand}\n              </span>\n            </a>\n\n            <nav aria-label=\"Primary\" className=\"pointer-events-auto relative hidden lg:block\">\n              <ul className=\"relative z-10 flex items-center\">\n                {menus.map((menu, index) => {\n                  const panelMenu = Boolean(menu.columns?.length);\n                  const isOpen = openKey === menu.label;\n                  const labelClass = cn(\n                    'relative flex h-10 items-center rounded-full px-3.5 text-[13.5px] font-medium tracking-[-0.01em] transition-colors duration-200 text-[#e8f3f2]/75 hover:text-[#e8f3f2]'\n                  );\n                  const chevronClass = cn(\n                    'w-[9px] transition-transform duration-400',\n                    EASE_OUT_EXPO,\n                    isOpen && 'rotate-180'\n                  );\n\n                  return (\n                    <li key={menu.label} className=\"relative\">\n                      {panelMenu ? (\n                        <button\n                          ref={(node) => {\n                            triggerRefs.current[menu.label] = node;\n                          }}\n                          type=\"button\"\n                          aria-expanded={isOpen}\n                          aria-controls={panelId}\n                          aria-haspopup=\"true\"\n                          onPointerEnter={(event) => handleTriggerEnter(menu, event.currentTarget)}\n                          onFocus={(event) => {\n                            cancelHover();\n                            moveBlob(event.currentTarget);\n                            if (skipFocusOpen.current) {\n                              skipFocusOpen.current = false;\n                              return;\n                            }\n                            openMenu(menu, event.currentTarget);\n                          }}\n                          onClick={() => {\n                            const trigger = triggerRefs.current[menu.label];\n                            if (!trigger) return;\n                            if (isOpen) closeMenu();\n                            else openMenu(menu, trigger);\n                          }}\n                          className={cn(labelClass, 'cursor-pointer', focusRing)}\n                        >\n                          <span className=\"relative flex items-center gap-1.5\">\n                            <span>{menu.label}</span>\n                            <ChevronIcon className={chevronClass} />\n                            <span\n                              aria-hidden=\"true\"\n                              ref={(node) => {\n                                inkRefs.current[index] = node;\n                              }}\n                              style={{ clipPath: 'inset(0 100% 0 0)' }}\n                              className=\"pointer-events-none absolute inset-0 flex items-center gap-1.5 text-[#03110f] opacity-0\"\n                            >\n                              {menu.label}\n                              <ChevronIcon className={chevronClass} />\n                            </span>\n                          </span>\n                        </button>\n                      ) : (\n                        <a\n                          href={menu.href ?? '#'}\n                          {...externalProps(menu.href ?? '')}\n                          aria-current={menu.href && current(menu.href) ? 'page' : undefined}\n                          onPointerEnter={(event) => handleTriggerEnter(menu, event.currentTarget)}\n                          onFocus={(event) => {\n                            cancelHover();\n                            moveBlob(event.currentTarget);\n                            closeMenu();\n                          }}\n                          className={cn(labelClass, focusRing)}\n                        >\n                          <span className=\"relative block\">\n                            {menu.label}\n                            <span\n                              aria-hidden=\"true\"\n                              ref={(node) => {\n                                inkRefs.current[index] = node;\n                              }}\n                              style={{ clipPath: 'inset(0 100% 0 0)' }}\n                              className=\"pointer-events-none absolute inset-0 block text-[#03110f] opacity-0\"\n                            >\n                              {menu.label}\n                            </span>\n                          </span>\n                        </a>\n                      )}\n                    </li>\n                  );\n                })}\n              </ul>\n            </nav>\n\n            <div data-intro=\"\" className=\"relative z-10 flex shrink-0 items-center gap-2\">\n              <DotBloomButton\n                variant=\"secondary\"\n                onClick={onActionClick}\n                className=\"hidden h-10 rounded-full px-4 text-[12.5px] focus-visible:ring-[#c6f56f] focus-visible:ring-offset-[#071d1b] sm:inline-flex\"\n              >\n                {actionLabel}\n              </DotBloomButton>\n\n              <button\n                ref={toggleRef}\n                type=\"button\"\n                aria-label={mobileOpen ? 'Close menu' : 'Open menu'}\n                aria-expanded={mobileOpen}\n                aria-controls={mobileId}\n                onClick={() => setMobileOpen((value) => !value)}\n                className={cn(\n                  'grid size-10 cursor-pointer place-items-center rounded-full border border-[#9fd4d6]/15 bg-white/[0.03] lg:hidden',\n                  focusRing\n                )}\n              >\n                <span aria-hidden=\"true\" className=\"relative block h-2.5 w-5\">\n                  <span\n                    className={cn(\n                      'absolute inset-x-0 top-0 h-px bg-[#e8f3f2] transition-transform duration-500 motion-reduce:transition-none',\n                      EASE_OUT_EXPO,\n                      mobileOpen && 'translate-y-[4.5px] rotate-45'\n                    )}\n                  />\n                  <span\n                    className={cn(\n                      'absolute inset-x-0 bottom-0 h-px bg-[#e8f3f2] transition-transform duration-500 motion-reduce:transition-none',\n                      EASE_OUT_EXPO,\n                      mobileOpen && '-translate-y-[4.5px] -rotate-45'\n                    )}\n                  />\n                </span>\n              </button>\n            </div>\n          </div>\n\n          <div\n            ref={panelRef}\n            id={panelId}\n            inert={!openKey}\n            onPointerEnter={cancelClose}\n            onPointerLeave={scheduleClose}\n            className=\"pointer-events-none absolute inset-x-0 top-0 z-40 h-0 opacity-0\"\n          >\n            <svg\n              aria-hidden=\"true\"\n              className=\"absolute top-0 left-0 h-[44rem] w-full overflow-visible\"\n              style={{\n                filter:\n                  'drop-shadow(0 0 0.6px rgb(159 212 214 / 0.45)) drop-shadow(0 32px 40px rgb(0 0 0 / 0.55))',\n              }}\n            >\n              <g\n                filter={`url(#${panelGooId})`}\n                fill=\"#071d1b\"\n                className={openKey ? 'pointer-events-auto' : 'pointer-events-none'}\n              >\n                <path ref={shapeRef} />\n                <circle ref={bumpRef} r=\"0\" />\n              </g>\n            </svg>\n            <div\n              ref={bodyRef}\n              className={cn(\n                'absolute top-0 left-0',\n                openKey ? 'pointer-events-auto' : 'pointer-events-none'\n              )}\n            >\n              {menus.map((menu) => {\n                if (!menu.columns?.length) return null;\n                const width = panelWidth(menu);\n\n                return (\n                  <div\n                    key={menu.label}\n                    ref={(node) => {\n                      sectionRefs.current[menu.label] = node;\n                    }}\n                    data-section=\"\"\n                    inert={openKey !== menu.label}\n                    onPointerLeave={hideHover}\n                    style={{ width }}\n                    className={cn(\n                      'absolute top-0 left-0 p-2.5 opacity-0',\n                      openKey === menu.label ? 'pointer-events-auto' : 'pointer-events-none'\n                    )}\n                  >\n                    <span aria-hidden=\"true\" className=\"pointer-events-none absolute inset-0\">\n                      <span\n                        data-hover-row=\"\"\n                        className=\"absolute top-0 left-0 rounded-2xl bg-[radial-gradient(140%_120%_at_0%_50%,rgb(198_245_111/0.12),transparent_60%),linear-gradient(#0c2924,#0c2924)] opacity-0 shadow-[inset_0_1px_0_rgb(232_243_242/0.06)]\"\n                      />\n                      <span className=\"absolute inset-0\" style={{ filter: `url(#${linkGooId})` }}>\n                        <span\n                          data-hover-blob=\"\"\n                          className=\"absolute top-0 left-0 rounded-[11px] bg-[#c6f56f] opacity-0\"\n                        />\n                        <span\n                          data-hover-drop=\"\"\n                          style={{ width: ICON_DROP, height: ICON_DROP }}\n                          className=\"absolute top-0 left-0 rounded-full bg-[#c6f56f] opacity-0\"\n                        />\n                      </span>\n                    </span>\n                    <div className={cn('relative grid gap-2.5', panelGrid(menu))}>\n                      {menu.columns.map((column, columnIndex) => (\n                        <ul key={columnIndex} className=\"flex flex-col\">\n                          {column.map((link) => (\n                            <li key={`${link.label}-${link.href}`} data-item=\"\">\n                              <a\n                                href={link.href}\n                                {...externalProps(link.href)}\n                                aria-current={current(link.href) ? 'page' : undefined}\n                                onClick={() => closeMenu()}\n                                onPointerEnter={(event) => moveHover(event.currentTarget)}\n                                onFocus={(event) => moveHover(event.currentTarget)}\n                                className={cn(\n                                  'group/link relative flex items-start gap-3 rounded-2xl p-2.5',\n                                  focusRing\n                                )}\n                              >\n                                <span\n                                  data-icon=\"\"\n                                  className=\"mt-0.5 grid size-8 shrink-0 place-items-center rounded-[11px] border border-[#9fd4d6]/12 bg-[#0a2420]/45 text-[#c6f56f] transition-[color,background-color,border-color] duration-300 group-hover/link:border-transparent group-hover/link:bg-transparent group-hover/link:text-[#03110f] group-hover/link:delay-75 group-focus-visible/link:border-transparent group-focus-visible/link:bg-transparent group-focus-visible/link:text-[#03110f]\"\n                                >\n                                  <MenuGlyph icon={link.icon ?? 'bloom'} className=\"size-4\" />\n                                </span>\n                                <span\n                                  className={cn(\n                                    'min-w-0 transition-transform duration-500 group-hover/link:translate-x-1',\n                                    EASE_OUT_EXPO\n                                  )}\n                                >\n                                  <span className=\"flex items-center gap-1.5 text-[13.5px] leading-tight font-medium text-[#e8f3f2]\">\n                                    {link.label}\n                                    <ArrowIcon\n                                      className={cn(\n                                        'size-3 -translate-x-1 text-[#c6f56f] opacity-0 transition-[opacity,translate] duration-500 group-hover/link:translate-x-0 group-hover/link:opacity-100',\n                                        EASE_OUT_EXPO\n                                      )}\n                                    />\n                                  </span>\n                                  {link.description ? (\n                                    <span className=\"mt-1 block text-[12.5px] leading-snug text-[#9fd4d6]/60 transition-colors duration-300 group-hover/link:text-[#9fd4d6]/85\">\n                                      {link.description}\n                                    </span>\n                                  ) : null}\n                                </span>\n                              </a>\n                            </li>\n                          ))}\n                        </ul>\n                      ))}\n\n                      {menu.feature ? (\n                        <div\n                          data-item=\"\"\n                          onPointerEnter={hideHover}\n                          className=\"flex flex-col overflow-hidden rounded-2xl border border-[#9fd4d6]/12 bg-[#0a2420]\"\n                        >\n                          <div className=\"relative h-28 shrink-0 overflow-hidden\">\n                            {menu.feature.image ? (\n                              <img\n                                src={menu.feature.image}\n                                alt=\"\"\n                                className=\"size-full object-cover\"\n                                loading=\"lazy\"\n                              />\n                            ) : (\n                              <FeatureArt />\n                            )}\n                          </div>\n                          <div className=\"flex flex-1 flex-col p-3\">\n                            <p className=\"text-[13px] font-medium\">{menu.feature.title}</p>\n                            <p className=\"mt-1 text-[12px] leading-snug text-[#9fd4d6]/60\">\n                              {menu.feature.description}\n                            </p>\n                            <a\n                              href={menu.feature.href}\n                              {...externalProps(menu.feature.href)}\n                              onClick={() => closeMenu()}\n                              className={cn(\n                                'group/feature mt-auto inline-flex items-center gap-1.5 pt-3 text-[12px] font-medium text-[#c6f56f]',\n                                focusRing\n                              )}\n                            >\n                              {menu.feature.cta ?? 'Explore'}\n                              <ArrowIcon className=\"size-3.5 transition-transform duration-300 group-hover/feature:translate-x-0.5\" />\n                            </a>\n                          </div>\n                        </div>\n                      ) : null}\n                    </div>\n                  </div>\n                );\n              })}\n            </div>\n          </div>\n        </div>\n      </header>\n\n      <div\n        ref={scrollerRef}\n        inert={mobileOpen}\n        data-lenis-prevent=\"\"\n        className=\"absolute inset-0 z-10 [scrollbar-width:none] overflow-y-auto [&::-webkit-scrollbar]:hidden\"\n      >\n        {children ?? <SamplePage />}\n      </div>\n\n      <div\n        ref={mobileRef}\n        id={mobileId}\n        inert={!mobileOpen}\n        className=\"invisible fixed inset-0 z-50 flex flex-col bg-[#04140f] lg:hidden\"\n      >\n        <div className=\"flex items-center justify-between px-4 pt-5 pb-4 sm:px-6\">\n          <a href={homeHref} className={cn('flex items-center gap-2.5 rounded-full', focusRing)}>\n            {logo}\n            <span className=\"text-[17px] font-medium tracking-tight\">{brand}</span>\n          </a>\n          <button\n            ref={mobileCloseRef}\n            type=\"button\"\n            aria-label=\"Close menu\"\n            onClick={() => {\n              setMobileOpen(false);\n              toggleRef.current?.focus();\n            }}\n            className={cn(\n              'grid size-10 cursor-pointer place-items-center rounded-full border border-[#9fd4d6]/15 bg-white/[0.04]',\n              focusRing\n            )}\n          >\n            <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" className=\"size-4\" fill=\"none\">\n              <path\n                d=\"M6 6l12 12M18 6L6 18\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.4\"\n                strokeLinecap=\"round\"\n              />\n            </svg>\n          </button>\n        </div>\n\n        <div className=\"min-h-0 flex-1 overflow-y-auto px-4 pb-8 sm:px-6\">\n          <ul className=\"flex flex-col\">\n            {menus.map((menu) => {\n              const panelMenu = Boolean(menu.columns?.length);\n              const isOpen = accordion === menu.label;\n              const links = menu.columns?.flat() ?? [];\n              const contentId = `${mobileId}-${menu.label.replace(/[^a-zA-Z0-9]/g, '')}`;\n\n              return (\n                <li\n                  key={menu.label}\n                  data-mobile-item=\"\"\n                  className=\"border-b border-[#9fd4d6]/10 last:border-b-0\"\n                >\n                  {panelMenu ? (\n                    <>\n                      <button\n                        type=\"button\"\n                        aria-expanded={isOpen}\n                        aria-controls={contentId}\n                        onClick={() => setAccordion(isOpen ? null : menu.label)}\n                        className={cn(\n                          'flex w-full cursor-pointer items-center justify-between py-4 text-left',\n                          focusRing\n                        )}\n                      >\n                        <span className=\"text-[26px] leading-none font-medium tracking-[-0.03em]\">\n                          {menu.label}\n                        </span>\n                        <ChevronIcon\n                          className={cn(\n                            'w-3 text-[#c6f56f] transition-transform duration-300',\n                            isOpen && 'rotate-180'\n                          )}\n                        />\n                      </button>\n                      <div\n                        id={contentId}\n                        inert={!isOpen}\n                        className={cn(\n                          'grid transition-[grid-template-rows] duration-400 motion-reduce:transition-none',\n                          EASE_OUT_EXPO,\n                          isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n                        )}\n                      >\n                        <div className=\"overflow-hidden\">\n                          <ul className=\"flex flex-col gap-1 pb-3\">\n                            {links.map((link) => (\n                              <li key={`${link.label}-${link.href}`}>\n                                <a\n                                  href={link.href}\n                                  {...externalProps(link.href)}\n                                  onClick={() => setMobileOpen(false)}\n                                  className={cn(\n                                    'flex items-center gap-3 rounded-xl px-2 py-2.5 text-[15px] text-[#e8f3f2]/75',\n                                    focusRing\n                                  )}\n                                >\n                                  <MenuGlyph\n                                    icon={link.icon ?? 'bloom'}\n                                    className=\"size-4 text-[#c6f56f]\"\n                                  />\n                                  {link.label}\n                                </a>\n                              </li>\n                            ))}\n                          </ul>\n                        </div>\n                      </div>\n                    </>\n                  ) : (\n                    <a\n                      href={menu.href ?? '#'}\n                      {...externalProps(menu.href ?? '')}\n                      onClick={() => setMobileOpen(false)}\n                      className={cn(\n                        'block py-4 text-[26px] leading-none font-medium tracking-[-0.03em]',\n                        focusRing\n                      )}\n                    >\n                      {menu.label}\n                    </a>\n                  )}\n                </li>\n              );\n            })}\n          </ul>\n\n          <div data-mobile-item=\"\" className=\"mt-6\">\n            <DotBloomButton\n              variant=\"secondary\"\n              onClick={(event) => {\n                setMobileOpen(false);\n                onActionClick?.(event);\n              }}\n              className=\"h-12 w-full rounded-full focus-visible:ring-[#c6f56f] focus-visible:ring-offset-[#04140f]\"\n            >\n              {actionLabel}\n            </DotBloomButton>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nexport default NavbarGooeyBloom;\n"
    }
  ],
  "docs": "Docs, props and examples: https://tween-ui.vercel.app/block/navbar-gooey-bloom\nPass `menus` (a `label` plus `columns` and an optional `feature`, or just an `href` for a plain link) and put your page inside as `children`. The goo filter is generated per instance, so several bars can share a page."
}