{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "fullscreen-navbar",
  "type": "registry:ui",
  "title": "Fullscreen Navbar",
  "description": "An editorial fullscreen navbar with numbered navigation, smooth dropdowns, spring-driven link entrances, and a Dev Club spotlight panel.",
  "dependencies": [
    "gsap",
    "@gsap/react",
    "@radix-ui/react-dialog",
    "lucide-react",
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/ui/fullscreen-navbar.tsx",
      "content": "\"use client\";\n\nimport {\n  useCallback,\n  useEffect,\n  useId,\n  useRef,\n  useState,\n  type CSSProperties,\n  type ReactNode,\n} from \"react\";\nimport * as Dialog from \"@radix-ui/react-dialog\";\nimport { useGSAP } from \"@gsap/react\";\nimport gsap from \"gsap\";\nimport { ArrowUpRight, ChevronDown, Code2, Menu, X } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ngsap.registerPlugin(useGSAP);\n\nexport interface FullscreenNavbarLink {\n  label: string;\n  href: string;\n}\n\nexport interface FullscreenNavbarItem {\n  label: string;\n  href?: string;\n  links?: readonly FullscreenNavbarLink[];\n}\n\nexport interface FullscreenNavbarSpotlight {\n  title: string;\n  description: string;\n  tag?: string;\n  image: string;\n  href: string;\n}\n\nexport interface FullscreenNavbarProps {\n  brand?: string;\n  brandHref?: string;\n  logo?: ReactNode;\n  items?: readonly FullscreenNavbarItem[];\n  connectLinks?: readonly FullscreenNavbarLink[];\n  spotlight?: FullscreenNavbarSpotlight;\n  actionLabel?: string;\n  actionHref?: string;\n  footerText?: string;\n  contactLabel?: string;\n  contactHref?: string;\n  duration?: number;\n  stiffness?: number;\n  damping?: number;\n  stagger?: number;\n  background?: string;\n  foreground?: string;\n  accent?: string;\n  headerForeground?: string;\n  hoverDropdowns?: boolean;\n  contained?: boolean;\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  className?: string;\n}\n\nexport const FULLSCREEN_NAVBAR_ITEMS: readonly FullscreenNavbarItem[] = [\n  {\n    label: \"Components\",\n    links: [\n      { label: \"Explore the library\", href: \"/components\" },\n      { label: \"Animated buttons\", href: \"/components/animated-button\" },\n      { label: \"Navigation bars\", href: \"/components/curved-navbar\" },\n      { label: \"Image loaders\", href: \"/components/grid-image-loader\" },\n    ],\n  },\n  {\n    label: \"Documentation\",\n    links: [\n      { label: \"Introduction\", href: \"/docs/introduction\" },\n      { label: \"Installation\", href: \"/docs/installation\" },\n      { label: \"Theming\", href: \"/docs/theming\" },\n      { label: \"CLI & registry\", href: \"/docs/cli\" },\n    ],\n  },\n  {\n    label: \"Community\",\n    links: [\n      {\n        label: \"Dev Club on GitHub\",\n        href: \"https://github.com/devclub-nstru/component_library\",\n      },\n      {\n        label: \"Report an issue\",\n        href: \"https://github.com/devclub-nstru/component_library/issues\",\n      },\n      {\n        label: \"Contribute\",\n        href: \"https://github.com/devclub-nstru/component_library/blob/main/CONTRIBUTING.md\",\n      },\n    ],\n  },\n  { label: \"Showcase\", href: \"/components\" },\n  { label: \"Our story\", href: \"/docs/introduction\" },\n  {\n    label: \"Source code\",\n    href: \"https://github.com/devclub-nstru/component_library\",\n  },\n];\n\nexport const FULLSCREEN_NAVBAR_CONNECT_LINKS: readonly FullscreenNavbarLink[] =\n  [\n    {\n      label: \"GitHub\",\n      href: \"https://github.com/devclub-nstru/component_library\",\n    },\n    { label: \"Docs\", href: \"/docs\" },\n    { label: \"Dev Club\", href: \"https://devclub.co\" },\n  ];\n\nconst bounded = (value: number, min: number, max: number, fallback: number) =>\n  Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;\n\nfunction springEase(stiffness: number, damping: number, duration: number) {\n  const frequency = Math.sqrt(stiffness);\n  const ratio = damping / (2 * frequency);\n  const response = (time: number) => {\n    if (ratio < 1) {\n      const rate = frequency * Math.sqrt(1 - ratio * ratio);\n      return (\n        1 -\n        Math.exp((-damping * time) / 2) *\n          (Math.cos(rate * time) +\n            (damping / (2 * rate)) * Math.sin(rate * time))\n      );\n    }\n    if (Math.abs(ratio - 1) < 0.001)\n      return 1 - (1 + frequency * time) * Math.exp(-frequency * time);\n    const delta = Math.sqrt(ratio * ratio - 1);\n    const slow = -frequency * (ratio - delta);\n    const fast = -frequency * (ratio + delta);\n    return (\n      1 -\n      (fast * Math.exp(slow * time) - slow * Math.exp(fast * time)) /\n        (fast - slow)\n    );\n  };\n  const end = response(duration);\n  return (progress: number) =>\n    progress === 1 ? 1 : response(progress * duration) / Math.max(0.001, end);\n}\n\nfunction NavbarHeader({\n  brand,\n  brandHref,\n  logo,\n  actionLabel,\n  actionHref,\n  menuButton,\n  onNavigate,\n}: {\n  brand: string;\n  brandHref: string;\n  logo?: ReactNode;\n  actionLabel: string;\n  actionHref: string;\n  menuButton: ReactNode;\n  onNavigate?: () => void;\n}) {\n  return (\n    <div className=\"flex h-20 w-full items-center justify-between gap-3 px-5 @[760px]/fullnav:px-9\">\n      <a\n        href={brandHref}\n        onClick={onNavigate}\n        className=\"flex min-w-0 items-center gap-2.5 rounded-lg text-xl font-semibold tracking-[-0.04em] focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-(--fullnav-accent)\"\n      >\n        <span\n          aria-hidden=\"true\"\n          className=\"flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-lg\"\n        >\n          {logo ?? <Code2 className=\"size-7\" />}\n        </span>\n        <span className=\"max-w-32 truncate\">{brand}</span>\n      </a>\n      <div className=\"flex shrink-0 items-center gap-2\">\n        <a\n          href={actionHref}\n          onClick={onNavigate}\n          className=\"hidden min-h-10 items-center gap-1.5 rounded-full bg-(--fullnav-accent) px-4 text-xs font-medium text-white focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-(--fullnav-accent) @[480px]/fullnav:flex\"\n        >\n          {actionLabel}\n          <ArrowUpRight aria-hidden=\"true\" className=\"size-3.5\" />\n        </a>\n        {menuButton}\n      </div>\n    </div>\n  );\n}\n\nconst HOVER_CLICK_GRACE_MS = 350;\n\nconst menuButtonClass =\n  \"flex min-h-10 min-w-24 cursor-pointer items-center justify-center gap-2 rounded-full border border-current/15 bg-current/5 px-4 text-xs font-medium backdrop-blur-lg hover:bg-current/10 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[var(--fullnav-accent)]\";\n\nfunction NavbarRow({\n  item,\n  index,\n  expanded,\n  onExpand,\n  onNavigate,\n  mobile,\n  duration,\n  stiffness,\n  damping,\n  hoverDropdowns,\n}: {\n  item: FullscreenNavbarItem;\n  index: number;\n  expanded: boolean;\n  onExpand: () => void;\n  onNavigate: () => void;\n  mobile: boolean;\n  duration: number;\n  stiffness: number;\n  damping: number;\n  hoverDropdowns: boolean;\n}) {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const panelRef = useRef<HTMLDivElement>(null);\n  const contentRef = useRef<HTMLDivElement>(null);\n  const arrowRef = useRef<SVGSVGElement>(null);\n  const animateRef = useRef<((next: boolean) => void) | null>(null);\n  const expandedRef = useRef(expanded);\n  const hoverOpenedAtRef = useRef(0);\n  const id = useId();\n  const hasLinks = Boolean(item.links?.length);\n  useEffect(() => {\n    expandedRef.current = expanded;\n  }, [expanded]);\n\n  useGSAP(\n    () => {\n      const panel = panelRef.current;\n      const content = contentRef.current;\n      const arrow = arrowRef.current;\n      if (!panel || !content || !arrow) return;\n      const media = gsap.matchMedia();\n      media.add(\n        { reduced: \"(prefers-reduced-motion: reduce)\", all: \"all\" },\n        (context) => {\n          const reduced = Boolean(context.conditions?.reduced);\n          const animate = context.add(\"animate\", (next: boolean) => {\n            gsap.killTweensOf([panel, arrow, content]);\n            if (reduced) {\n              gsap.set(panel, {\n                height: next ? \"auto\" : 0,\n                autoAlpha: next ? 1 : 0,\n              });\n              gsap.set(arrow, { rotation: next ? 180 : 0 });\n              gsap.set(content, { y: 0, opacity: 1 });\n              return;\n            }\n            gsap.to(panel, {\n              height: next ? \"auto\" : 0,\n              autoAlpha: next ? 1 : 0,\n              duration: duration * 0.55,\n              ease: \"power3.inOut\",\n              overwrite: \"auto\",\n            });\n            gsap.to(arrow, {\n              rotation: next ? 180 : 0,\n              duration: duration * 0.65,\n              ease: springEase(stiffness, damping, duration * 0.65),\n              overwrite: \"auto\",\n            });\n            gsap.fromTo(\n              content,\n              { y: next ? 8 : 0, opacity: next ? 0 : 1 },\n              {\n                y: 0,\n                opacity: next ? 1 : 0,\n                duration: duration * 0.45,\n                ease: \"power3.out\",\n              },\n            );\n          });\n          animateRef.current = (next) => animate(next);\n          animate(expandedRef.current);\n          return () => {\n            animateRef.current = null;\n          };\n        },\n      );\n      return () => media.revert();\n    },\n    {\n      scope: rootRef,\n      dependencies: [duration, stiffness, damping, item.links],\n      revertOnUpdate: true,\n    },\n  );\n\n  useGSAP(\n    () => {\n      animateRef.current?.(expanded);\n    },\n    { scope: rootRef, dependencies: [expanded] },\n  );\n\n  const label = (\n    <>\n      <span\n        aria-hidden=\"true\"\n        className=\"w-7 shrink-0 pt-1 text-[10px] font-normal tabular-nums tracking-wide opacity-35 @[760px]/fullnav:w-9\"\n      >\n        {String(index + 1).padStart(2, \"0\")}\n      </span>\n      <span\n        data-fullnav-label\n        className=\"min-w-0 wrap-break-words text-[clamp(1.65rem,4.8cqw,3.75rem)] font-semibold leading-[1.08] tracking-[-0.055em]\"\n      >\n        {item.label}\n      </span>\n      {hasLinks ? (\n        <ChevronDown\n          ref={arrowRef}\n          data-fullnav-arrow=\"chevron\"\n          aria-hidden=\"true\"\n          className=\"ml-2 size-5 shrink-0 opacity-35 @[760px]/fullnav:size-7\"\n        />\n      ) : (\n        <ArrowUpRight\n          aria-hidden=\"true\"\n          data-fullnav-arrow=\"link\"\n          className=\"ml-2 size-5 shrink-0 opacity-0\"\n        />\n      )}\n    </>\n  );\n  const rowClass = cn(\n    \"group relative z-10 flex w-full items-center rounded-lg py-2.5 text-left focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--fullnav-accent)]\",\n    expanded && \"text-[var(--fullnav-accent)]\",\n  );\n  return (\n    <div ref={rootRef} data-fullnav-entry>\n      {hasLinks ? (\n        <button\n          type=\"button\"\n          data-fullnav-hit\n          id={`trigger-${id}`}\n          aria-expanded={expanded}\n          aria-controls={`panel-${id}`}\n          onClick={(event) => {\n            const hoverOpenedAt = hoverOpenedAtRef.current;\n            hoverOpenedAtRef.current = 0;\n            if (\n              event.detail > 0 &&\n              hoverOpenedAt > 0 &&\n              performance.now() - hoverOpenedAt < HOVER_CLICK_GRACE_MS\n            ) {\n              return;\n            }\n            onExpand();\n          }}\n          onPointerEnter={(event) => {\n            if (\n              !mobile &&\n              hoverDropdowns &&\n              event.pointerType === \"mouse\" &&\n              !expanded\n            ) {\n              hoverOpenedAtRef.current = performance.now();\n              onExpand();\n            }\n          }}\n          className={cn(rowClass, \"cursor-pointer\")}\n        >\n          {label}\n        </button>\n      ) : (\n        <a\n          data-fullnav-hit\n          href={item.href ?? \"#\"}\n          onClick={onNavigate}\n          className={rowClass}\n        >\n          {label}\n        </a>\n      )}\n      {hasLinks && (\n        <div\n          ref={panelRef}\n          id={`panel-${id}`}\n          role=\"region\"\n          aria-labelledby={`trigger-${id}`}\n          aria-hidden={!expanded}\n          inert={!expanded}\n          className=\"invisible h-0 overflow-hidden\"\n        >\n          <div\n            ref={contentRef}\n            className=\"flex flex-col items-start gap-2.5 pb-4 pl-7 pt-2 @[760px]/fullnav:pl-9\"\n          >\n            {item.links!.map((link) => (\n              <a\n                key={`${link.label}-${link.href}`}\n                data-fullnav-hit\n                href={link.href}\n                onClick={onNavigate}\n                className=\"relative z-10 max-w-full rounded-md text-sm leading-relaxed opacity-60 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-(--fullnav-accent)\"\n              >\n                <span data-fullnav-label className=\"inline-block\">\n                  {link.label}\n                </span>\n              </a>\n            ))}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n\nfunction FullscreenPanel({\n  open,\n  onExit,\n  onClose,\n  mobile,\n  settings,\n}: {\n  open: boolean;\n  onExit: () => void;\n  onClose: () => void;\n  mobile: boolean;\n  settings: Required<\n    Pick<\n      FullscreenNavbarProps,\n      | \"brand\"\n      | \"brandHref\"\n      | \"items\"\n      | \"connectLinks\"\n      | \"actionLabel\"\n      | \"actionHref\"\n      | \"footerText\"\n      | \"contactLabel\"\n      | \"contactHref\"\n      | \"duration\"\n      | \"stiffness\"\n      | \"damping\"\n      | \"stagger\"\n      | \"background\"\n      | \"foreground\"\n      | \"accent\"\n      | \"hoverDropdowns\"\n      | \"contained\"\n    >\n  > &\n    Pick<FullscreenNavbarProps, \"logo\" | \"spotlight\">;\n}) {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const sheetRef = useRef<HTMLDivElement>(null);\n  const overlayRef = useRef<HTMLDivElement>(null);\n  const closeRef = useRef<HTMLButtonElement>(null);\n  const motionRef = useRef<((next: boolean) => void) | null>(null);\n  const enteredRef = useRef(false);\n  const openRef = useRef(open);\n  const [active, setActive] = useState<number | null>(null);\n  const {\n    brand,\n    brandHref,\n    logo,\n    items,\n    connectLinks,\n    spotlight,\n    actionLabel,\n    actionHref,\n    footerText,\n    contactLabel,\n    contactHref,\n    duration,\n    stiffness,\n    damping,\n    stagger,\n    background,\n    foreground,\n    accent,\n    hoverDropdowns,\n    contained,\n  } = settings;\n  useEffect(() => {\n    openRef.current = open;\n  }, [open]);\n\n  useGSAP(\n    () => {\n      const sheet = sheetRef.current;\n      const overlay = overlayRef.current;\n      const root = rootRef.current;\n      if (!sheet || !overlay || !root) return;\n      const entries = root.querySelectorAll(\"[data-fullnav-entry]\");\n      const side = root.querySelector(\"[data-fullnav-side]\");\n      const media = gsap.matchMedia();\n      media.add(\n        { reduced: \"(prefers-reduced-motion: reduce)\", all: \"all\" },\n        (context) => {\n          const reduced = Boolean(context.conditions?.reduced);\n          gsap.set(sheet, {\n            xPercent: mobile ? 0 : 100,\n            yPercent: mobile ? -100 : 0,\n          });\n          gsap.set(entries, { y: 24, opacity: 0 });\n          if (side) gsap.set(side, { y: 16, opacity: 0 });\n          gsap.set(overlay, { opacity: 0 });\n          const timeline = gsap\n            .timeline({\n              paused: true,\n              onComplete: () => {\n                enteredRef.current = true;\n              },\n              onReverseComplete: onExit,\n            })\n            .to(\n              sheet,\n              { xPercent: 0, yPercent: 0, duration, ease: \"power3.inOut\" },\n              0,\n            )\n            .to(overlay, { opacity: 1, duration: duration * 0.6 }, 0)\n            .to(\n              entries,\n              {\n                y: 0,\n                opacity: 1,\n                duration: duration * 0.6,\n                ease: springEase(stiffness, damping, duration * 0.6),\n                stagger,\n              },\n              duration * 0.25,\n            );\n          if (side)\n            timeline.to(\n              side,\n              {\n                y: 0,\n                opacity: 1,\n                duration: duration * 0.6,\n                ease: \"power3.out\",\n              },\n              duration * 0.4,\n            );\n          const nav = root.querySelector<HTMLElement>(\"[data-fullnav-list]\");\n          const highlight = root.querySelector<HTMLElement>(\n            \"[data-fullnav-highlight]\",\n          );\n          const hoverDuration = Math.min(0.5, Math.max(0.25, duration * 0.45));\n          const hoverEase = springEase(stiffness, damping, hoverDuration);\n          let selected: HTMLElement | null = null;\n          const quick =\n            highlight && !reduced\n              ? {\n                  x: gsap.quickTo(highlight, \"x\", {\n                    duration: hoverDuration,\n                    ease: hoverEase,\n                  }),\n                  y: gsap.quickTo(highlight, \"y\", {\n                    duration: hoverDuration,\n                    ease: hoverEase,\n                  }),\n                  width: gsap.quickTo(highlight, \"width\", {\n                    duration: hoverDuration,\n                    ease: \"power3.out\",\n                  }),\n                  height: gsap.quickTo(highlight, \"height\", {\n                    duration: hoverDuration,\n                    ease: \"power3.out\",\n                  }),\n                }\n              : null;\n          const positionHighlight = context.add(\"positionHighlight\", () => {\n            if (!nav || !highlight || !selected) return;\n            const bounds = selected.getBoundingClientRect();\n            const parent = nav.getBoundingClientRect();\n            const geometry = {\n              x: bounds.left - parent.left - 8,\n              y: bounds.top - parent.top - 2,\n              width: bounds.width + 16,\n              height: bounds.height + 4,\n            };\n            if (quick) {\n              quick.x(geometry.x);\n              quick.y(geometry.y);\n              quick.width(geometry.width);\n              quick.height(geometry.height);\n            } else gsap.set(highlight, geometry);\n          });\n          const select = context.add(\"select\", (target: HTMLElement | null) => {\n            if (!highlight || !nav || target === selected) return;\n            if (selected) {\n              const oldLabel = selected.querySelector(\"[data-fullnav-label]\");\n              const oldArrow = selected.querySelector<SVGSVGElement>(\n                \"[data-fullnav-arrow]\",\n              );\n              if (oldLabel)\n                gsap.to(oldLabel, {\n                  x: 0,\n                  duration: reduced ? 0 : hoverDuration,\n                  ease: hoverEase,\n                  overwrite: \"auto\",\n                });\n              if (oldArrow)\n                gsap.to(oldArrow, {\n                  x: 0,\n                  opacity: oldArrow.dataset.fullnavArrow === \"link\" ? 0 : 0.35,\n                  duration: reduced ? 0 : hoverDuration,\n                  ease: \"power3.out\",\n                  overwrite: \"auto\",\n                });\n              const previous = selected;\n              gsap.to(previous, {\n                color:\n                  previous.getAttribute(\"aria-expanded\") === \"true\"\n                    ? accent\n                    : foreground,\n                duration: reduced ? 0 : 0.2,\n                overwrite: \"auto\",\n                onComplete: () => previous.style.removeProperty(\"color\"),\n              });\n            }\n            const first = selected === null;\n            selected = target;\n            if (first && target) {\n              const bounds = target.getBoundingClientRect();\n              const parent = nav.getBoundingClientRect();\n              gsap.set(highlight, {\n                x: bounds.left - parent.left - 8,\n                y: bounds.top - parent.top - 2,\n                width: bounds.width + 16,\n                height: bounds.height + 4,\n              });\n            }\n            positionHighlight();\n            gsap.to(highlight, {\n              opacity: target ? 1 : 0,\n              duration: reduced ? 0 : 0.2,\n              overwrite: \"auto\",\n            });\n            if (!target) return;\n            const label = target.querySelector(\"[data-fullnav-label]\");\n            const arrow = target.querySelector(\"[data-fullnav-arrow]\");\n            if (label)\n              gsap.to(label, {\n                x: reduced ? 0 : 6,\n                duration: reduced ? 0 : hoverDuration,\n                ease: hoverEase,\n                overwrite: \"auto\",\n              });\n            if (arrow)\n              gsap.to(arrow, {\n                x: reduced ? 0 : 3,\n                opacity: 0.65,\n                duration: reduced ? 0 : hoverDuration,\n                ease: hoverEase,\n                overwrite: \"auto\",\n              });\n            gsap.to(target, {\n              color: accent,\n              duration: reduced ? 0 : 0.2,\n              overwrite: \"auto\",\n            });\n          });\n          const point = (event: Event) => {\n            if (\n              event instanceof PointerEvent &&\n              (event.pointerType !== \"mouse\" || mobile)\n            )\n              return;\n            const target =\n              event.target instanceof Element\n                ? event.target.closest<HTMLElement>(\"[data-fullnav-hit]\")\n                : null;\n            if (target && nav?.contains(target)) select(target);\n          };\n          const leave = () => {\n            if (!nav?.contains(document.activeElement)) select(null);\n          };\n          const blur = (event: FocusEvent) => {\n            if (\n              !(event.relatedTarget instanceof Node) ||\n              !nav?.contains(event.relatedTarget)\n            )\n              select(null);\n          };\n          nav?.addEventListener(\"pointerover\", point);\n          nav?.addEventListener(\"focusin\", point);\n          nav?.addEventListener(\"pointerleave\", leave);\n          nav?.addEventListener(\"focusout\", blur);\n          const hoverObserver = new ResizeObserver(() => positionHighlight());\n          if (nav) hoverObserver.observe(nav);\n          const move = context.add(\"move\", (next: boolean) => {\n            if (reduced) {\n              gsap.set(sheet, { xPercent: 0, yPercent: 0 });\n              gsap.set(entries, { y: 0, opacity: 1 });\n              if (side) gsap.set(side, { y: 0, opacity: 1 });\n              gsap.set(overlay, { opacity: 1 });\n              if (!next) onExit();\n            } else if (next) timeline.play();\n            else timeline.timeScale(1.25).reverse();\n          });\n          motionRef.current = (next) => move(next);\n          if (enteredRef.current && openRef.current && !reduced)\n            timeline.progress(1);\n          else move(openRef.current);\n          return () => {\n            motionRef.current = null;\n            nav?.removeEventListener(\"pointerover\", point);\n            nav?.removeEventListener(\"focusin\", point);\n            nav?.removeEventListener(\"pointerleave\", leave);\n            nav?.removeEventListener(\"focusout\", blur);\n            hoverObserver.disconnect();\n          };\n        },\n      );\n      return () => media.revert();\n    },\n    {\n      scope: rootRef,\n      dependencies: [\n        mobile,\n        duration,\n        stiffness,\n        damping,\n        stagger,\n        items,\n        accent,\n        foreground,\n        onExit,\n      ],\n      revertOnUpdate: true,\n    },\n  );\n\n  useGSAP(\n    () => {\n      motionRef.current?.(open);\n    },\n    { scope: rootRef, dependencies: [open] },\n  );\n\n  return (\n    <Dialog.Content\n      ref={rootRef}\n      className={cn(\n        \"@container/fullnav inset-0 z-50 overflow-hidden outline-none pointer-events-auto\",\n        contained ? \"absolute\" : \"fixed\",\n      )}\n      style={{ color: foreground, \"--fullnav-accent\": accent } as CSSProperties}\n      onOpenAutoFocus={(event) => {\n        event.preventDefault();\n        closeRef.current?.focus({ preventScroll: true });\n      }}\n      onEscapeKeyDown={(event) => {\n        event.preventDefault();\n        onClose();\n      }}\n      onPointerDownOutside={(event) => event.preventDefault()}\n    >\n      <Dialog.Title className=\"sr-only\">{brand} navigation</Dialog.Title>\n      <Dialog.Description className=\"sr-only\">\n        Explore components, documentation, and community resources.\n      </Dialog.Description>\n      <div\n        ref={overlayRef}\n        aria-hidden=\"true\"\n        className=\"absolute inset-0 bg-black/25\"\n      />\n      <div ref={sheetRef} className=\"absolute inset-0\" style={{ background }}>\n        <div\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_top_left,rgba(0,0,0,0.04),transparent_65%)]\"\n        />\n        <div className=\"relative flex h-full flex-col\">\n          <div className=\"shrink-0 border-b border-current/8\">\n            <NavbarHeader\n              brand={brand}\n              brandHref={brandHref}\n              logo={logo}\n              actionLabel={actionLabel}\n              actionHref={actionHref}\n              onNavigate={onClose}\n              menuButton={\n                <button\n                  ref={closeRef}\n                  type=\"button\"\n                  aria-label=\"Close menu\"\n                  onClick={onClose}\n                  className={menuButtonClass}\n                >\n                  Close\n                  <X aria-hidden=\"true\" className=\"size-4\" />\n                </button>\n              }\n            />\n          </div>\n          <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 py-7 scrollbar-thin @[760px]/fullnav:px-9 @[760px]/fullnav:py-9\">\n            <div className=\"mx-auto grid w-full max-w-7xl gap-10 @[760px]/fullnav:grid-cols-[1.45fr_1fr] @[760px]/fullnav:gap-10\">\n              <nav\n                data-fullnav-list\n                aria-label={`${brand} main navigation`}\n                className=\"relative isolate min-w-0\"\n                onPointerLeave={(event) => {\n                  if (\n                    !mobile &&\n                    event.pointerType === \"mouse\" &&\n                    !event.currentTarget.contains(document.activeElement)\n                  )\n                    setActive(null);\n                }}\n              >\n                <div\n                  data-fullnav-highlight\n                  aria-hidden=\"true\"\n                  className=\"pointer-events-none absolute left-0 top-0 z-0 rounded-xl bg-[color-mix(in_srgb,var(--fullnav-accent)_7%,transparent)] opacity-0\"\n                />\n                {items.map((item, index) => (\n                  <NavbarRow\n                    key={`${item.label}-${index}`}\n                    item={item}\n                    index={index}\n                    expanded={active === index}\n                    onExpand={() =>\n                      setActive((current) => (current === index ? null : index))\n                    }\n                    onNavigate={onClose}\n                    mobile={mobile}\n                    duration={duration}\n                    stiffness={stiffness}\n                    damping={damping}\n                    hoverDropdowns={hoverDropdowns}\n                  />\n                ))}\n              </nav>\n              <aside\n                data-fullnav-side\n                className=\"min-w-0 border-t border-current/10 pt-7 @[760px]/fullnav:border-l @[760px]/fullnav:border-t-0 @[760px]/fullnav:pl-8 @[760px]/fullnav:pt-0\"\n              >\n                {spotlight && (\n                  <div className=\"mb-7\">\n                    <p className=\"mb-4 text-[10px] font-medium uppercase tracking-[0.2em] opacity-40\">\n                      Spotlight\n                    </p>\n                    <a\n                      href={spotlight.href}\n                      onClick={onClose}\n                      className=\"group relative block aspect-16/10 overflow-hidden rounded-2xl bg-black/10 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-(--fullnav-accent)\"\n                    >\n                      <img\n                        src={spotlight.image}\n                        alt=\"\"\n                        className=\"absolute inset-0 h-full w-full object-cover object-center\"\n                      />\n                      <div className=\"absolute inset-0 bg-linear-to-t from-black/90 via-black/20 to-transparent\" />\n                      {spotlight.tag && (\n                        <span className=\"absolute left-4 top-4 rounded-full border border-white/20 bg-white/15 px-2.5 py-1 text-[9px] uppercase tracking-wider text-white backdrop-blur-lg\">\n                          {spotlight.tag}\n                        </span>\n                      )}\n                      <div className=\"absolute bottom-4 left-4 right-4 text-white\">\n                        <h3 className=\"max-w-64 text-xl font-medium leading-tight tracking-tight\">\n                          {spotlight.title}\n                        </h3>\n                        <p className=\"mt-2 max-w-72 text-[11px] leading-relaxed text-white/65\">\n                          {spotlight.description}\n                        </p>\n                      </div>\n                      <ArrowUpRight\n                        aria-hidden=\"true\"\n                        className=\"absolute top-4 right-4 size-4 text-white/75\"\n                      />\n                    </a>\n                  </div>\n                )}\n                <div className=\"mb-7\">\n                  <p className=\"mb-3 text-[10px] font-medium uppercase tracking-[0.2em] opacity-40\">\n                    Connect\n                  </p>\n                  <div className=\"flex flex-wrap gap-2\">\n                    {connectLinks.map((link) => (\n                      <a\n                        key={`${link.label}-${link.href}`}\n                        href={link.href}\n                        onClick={onClose}\n                        target={\n                          link.href.startsWith(\"http\") ? \"_blank\" : undefined\n                        }\n                        rel={\n                          link.href.startsWith(\"http\")\n                            ? \"noopener noreferrer\"\n                            : undefined\n                        }\n                        className=\"flex min-h-9 items-center gap-2 rounded-full border border-current/12 px-3 text-[11px] hover:bg-current/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--fullnav-accent)\"\n                      >\n                        {link.label}\n                        <ArrowUpRight aria-hidden=\"true\" className=\"size-3\" />\n                      </a>\n                    ))}\n                  </div>\n                </div>\n                <p className=\"mb-2 text-[10px] font-medium uppercase tracking-[0.2em] opacity-40\">\n                  Build with us\n                </p>\n                <a\n                  href={contactHref}\n                  onClick={onClose}\n                  className=\"block w-fit max-w-full wrap-break-words rounded-md text-lg font-medium tracking-tight hover:text-(--fullnav-accent) focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-(--fullnav-accent)\"\n                >\n                  {contactLabel}\n                </a>\n                <a\n                  href={actionHref}\n                  onClick={onClose}\n                  className=\"mt-7 flex min-h-10 w-fit items-center gap-3 rounded-full bg-(--fullnav-accent) px-5 text-xs font-medium text-white focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-(--fullnav-accent)\"\n                >\n                  {actionLabel}\n                  <ArrowUpRight aria-hidden=\"true\" className=\"size-3.5\" />\n                </a>\n              </aside>\n            </div>\n            <p className=\"mx-auto mt-10 max-w-7xl border-t border-current/10 pt-4 text-[10px] tracking-wide opacity-40\">\n              {footerText}\n            </p>\n          </div>\n        </div>\n      </div>\n    </Dialog.Content>\n  );\n}\n\nexport function FullscreenNavbar({\n  brand = \"devclub\",\n  brandHref = \"/\",\n  logo,\n  items = FULLSCREEN_NAVBAR_ITEMS,\n  connectLinks = FULLSCREEN_NAVBAR_CONNECT_LINKS,\n  spotlight,\n  actionLabel = \"Start building\",\n  actionHref = \"/docs/installation\",\n  footerText = \"Built by Dev Club. Made for your next idea.\",\n  contactLabel = \"Let's build something together.\",\n  contactHref = \"https://github.com/devclub-nstru/component_library/issues\",\n  duration = 0.8,\n  stiffness = 220,\n  damping = 26,\n  stagger = 0.045,\n  background = \"#f7f5ef\",\n  foreground = \"#171717\",\n  accent = \"#d34b30\",\n  headerForeground = \"#ffffff\",\n  hoverDropdowns = true,\n  contained = false,\n  open: controlledOpen,\n  defaultOpen = false,\n  onOpenChange,\n  className,\n}: FullscreenNavbarProps) {\n  const [internalOpen, setInternalOpen] = useState(defaultOpen);\n  const open = controlledOpen ?? internalOpen;\n  const [present, setPresent] = useState(open);\n  const [host, setHost] = useState<HTMLDivElement | null>(null);\n  const [mobile, setMobile] = useState(false);\n  const finishExit = useCallback(() => setPresent(false), []);\n  if (open && !present) setPresent(true);\n  const changeOpen = (next: boolean) => {\n    if (controlledOpen === undefined) setInternalOpen(next);\n    onOpenChange?.(next);\n  };\n\n  useEffect(() => {\n    if (!host) return;\n    const observer = new ResizeObserver(() =>\n      setMobile(host.clientWidth < 760),\n    );\n    observer.observe(host);\n    return () => observer.disconnect();\n  }, [host]);\n\n  const settings = {\n    brand,\n    brandHref,\n    logo,\n    items,\n    connectLinks,\n    spotlight,\n    actionLabel,\n    actionHref,\n    footerText,\n    contactLabel,\n    contactHref,\n    duration: bounded(duration, 0.25, 1.5, 0.8),\n    stiffness: bounded(stiffness, 80, 500, 220),\n    damping: bounded(damping, 12, 50, 26),\n    stagger: bounded(stagger, 0, 0.12, 0.045),\n    background,\n    foreground,\n    accent,\n    hoverDropdowns,\n    contained,\n  };\n  return (\n    <div\n      ref={setHost}\n      data-slot=\"fullscreen-navbar\"\n      className={cn(\n        \"pointer-events-none inset-0 z-30\",\n        contained ? \"absolute\" : \"fixed\",\n        className,\n      )}\n    >\n      <Dialog.Root open={present} onOpenChange={changeOpen}>\n        <div\n          aria-hidden={present}\n          inert={present}\n          className=\"@container/fullnav pointer-events-auto absolute inset-x-0 top-0\"\n          style={\n            {\n              color: headerForeground,\n              \"--fullnav-accent\": accent,\n            } as CSSProperties\n          }\n        >\n          <NavbarHeader\n            brand={brand}\n            brandHref={brandHref}\n            logo={logo}\n            actionLabel={actionLabel}\n            actionHref={actionHref}\n            menuButton={\n              <Dialog.Trigger asChild>\n                <button\n                  type=\"button\"\n                  aria-label=\"Open menu\"\n                  className={menuButtonClass}\n                >\n                  Menu\n                  <Menu aria-hidden=\"true\" className=\"size-4\" />\n                </button>\n              </Dialog.Trigger>\n            }\n          />\n        </div>\n        <Dialog.Portal container={contained ? host : undefined}>\n          <FullscreenPanel\n            open={open}\n            onExit={finishExit}\n            onClose={() => changeOpen(false)}\n            mobile={mobile}\n            settings={settings}\n          />\n        </Dialog.Portal>\n      </Dialog.Root>\n    </div>\n  );\n}\n\nexport default FullscreenNavbar;\n",
      "type": "registry:ui",
      "target": "@ui/fullscreen-navbar.tsx"
    }
  ]
}
