{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "site-header",
  "title": "Site Header",
  "description": "The marketing header — a sticky glassy bar with desktop mega-menus and a mobile popover menu, all props-driven.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/button",
    "@nx-ui/popover",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/marketing/site-header.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  ArrowRight,\n  ArrowUpRight,\n  ChevronDown,\n  Menu01,\n  XClose,\n} from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\n\n/**\n * SiteHeader — the marketing header (distinct from nx-ui's app-shell sidebar):\n * a sticky, glassy bar that gains a border + shadow on scroll, with desktop\n * click-to-open mega-menus and a full-width mobile menu.\n *\n * The desktop mega-menus are a self-contained controlled group (single-open,\n * outside-click / Escape to close, focus return) built on plain buttons + CSS\n * transitions — no motion dependency. The mobile menu is built on the nx-ui\n * `Popover` (Base UI), replacing the reference's react-aria Dialog/Popover.\n *\n * Nav data is fully props-driven via `navItems`; pass a `logo` node and CTAs.\n */\n\ntype IconC = React.FC<{ className?: string }>\n\nexport interface MegaMenuItem {\n  title: string\n  subtitle: string\n  href: string\n  icon: IconC\n  badge?: string\n  external?: boolean\n}\n\nexport interface MegaMenu {\n  items: MegaMenuItem[]\n  footer?: { note: string; cta: string; href: string }\n  /** \"wide\" renders the desktop panel as a two-column grid. */\n  layout?: \"wide\"\n}\n\nexport interface NavItem {\n  label: string\n  href?: string\n  menu?: MegaMenu\n}\n\ninterface SiteHeaderProps {\n  navItems: NavItem[]\n  logo?: React.ReactNode\n  ctas?: {\n    primary?: { label: string; href: string }\n    secondary?: { label: string; href: string }\n  }\n  className?: string\n}\n\nfunction MenuItemLink({\n  title,\n  subtitle,\n  href,\n  icon: Icon,\n  badge,\n  external,\n}: MegaMenuItem) {\n  return (\n    <a\n      href={href}\n      {...(external ? { target: \"_blank\", rel: \"noopener noreferrer\" } : {})}\n      className=\"group/item flex items-center gap-3.5 rounded-xl p-3 outline-none transition duration-150 ease-linear hover:bg-bg-secondary focus-visible:ring-4 focus-visible:ring-ring/25\"\n    >\n      <span className=\"flex size-10 shrink-0 items-center justify-center rounded-xl bg-brand-600 text-white shadow-sm transition duration-200 ease-linear group-hover/item:rotate-3 group-hover/item:bg-brand-700\">\n        <Icon className=\"size-5\" />\n      </span>\n      <span className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n        <span className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n          {title}\n          {badge ? (\n            <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-success-secondary px-1.5 py-0.5 text-[10px] font-semibold text-green-700\">\n              {badge}\n            </span>\n          ) : null}\n        </span>\n        <span className=\"text-sm leading-snug text-text-tertiary\">\n          {subtitle}\n        </span>\n      </span>\n      <ArrowUpRight className=\"size-4 shrink-0 -translate-x-1 text-fg-quaternary opacity-0 transition duration-150 ease-linear group-hover/item:translate-x-0 group-hover/item:opacity-100\" />\n    </a>\n  )\n}\n\nfunction DesktopNavMenu({\n  item,\n  isOpen,\n  onToggle,\n  onClose,\n}: {\n  item: NavItem & { menu: MegaMenu }\n  isOpen: boolean\n  onToggle: () => void\n  onClose: () => void\n}) {\n  const triggerRef = React.useRef<HTMLButtonElement>(null)\n  const panelId = React.useId()\n  const { menu } = item\n\n  const onKeyDown = (e: React.KeyboardEvent) => {\n    if (e.key === \"Escape\" && isOpen) {\n      e.preventDefault()\n      onClose()\n      triggerRef.current?.focus()\n    }\n  }\n\n  return (\n    <li className=\"relative\" onKeyDown={onKeyDown}>\n      <button\n        ref={triggerRef}\n        type=\"button\"\n        aria-expanded={isOpen}\n        aria-haspopup=\"true\"\n        aria-controls={isOpen ? panelId : undefined}\n        onClick={onToggle}\n        className={cn(\n          \"flex cursor-pointer items-center gap-1 rounded-lg px-3 py-2 text-sm font-semibold outline-none transition duration-150 ease-linear focus-visible:ring-4 focus-visible:ring-ring/25\",\n          isOpen\n            ? \"bg-bg-secondary text-text-primary\"\n            : \"text-text-secondary hover:bg-bg-secondary\"\n        )}\n      >\n        <span>{item.label}</span>\n        <ChevronDown\n          className={cn(\n            \"size-3.5 stroke-[2.5px] text-fg-quaternary transition duration-150 ease-linear\",\n            isOpen && \"-rotate-180\"\n          )}\n        />\n      </button>\n\n      {isOpen ? (\n        <div\n          id={panelId}\n          className=\"absolute top-full left-0 z-50 pt-2.5\"\n        >\n          <div\n            className={cn(\n              \"overflow-hidden rounded-2xl bg-bg-primary shadow-xl ring-1 ring-border-secondary_alt\",\n              menu.layout === \"wide\" ? \"w-[660px]\" : \"w-[400px]\"\n            )}\n          >\n            <div\n              aria-hidden=\"true\"\n              className=\"h-1 w-full bg-gradient-to-r from-brand-500 via-brand-600 to-brand-700\"\n            />\n            <div className=\"p-2\">\n              <ul\n                className={cn(\n                  \"gap-0.5\",\n                  menu.layout === \"wide\"\n                    ? \"grid grid-cols-2\"\n                    : \"flex flex-col\"\n                )}\n              >\n                {menu.items.map((menuItem) => (\n                  <li key={menuItem.title}>\n                    <MenuItemLink {...menuItem} />\n                  </li>\n                ))}\n              </ul>\n              {menu.footer ? (\n                <div className=\"mt-1 border-t border-border-secondary px-1 pt-1\">\n                  <a\n                    href={menu.footer.href}\n                    className=\"group/footer flex items-center justify-between gap-3 rounded-xl bg-gradient-to-r from-brand-50 to-transparent px-3 py-2.5 outline-none transition duration-150 ease-linear hover:from-brand-100 focus-visible:ring-4 focus-visible:ring-ring/25\"\n                  >\n                    <span className=\"text-xs font-medium text-text-tertiary\">\n                      {menu.footer.note}\n                    </span>\n                    <span className=\"inline-flex items-center gap-1 text-xs font-semibold text-text-brand-secondary\">\n                      {menu.footer.cta}\n                      <ArrowRight className=\"size-3.5 transition duration-150 ease-linear group-hover/footer:translate-x-0.5\" />\n                    </span>\n                  </a>\n                </div>\n              ) : null}\n            </div>\n          </div>\n        </div>\n      ) : null}\n    </li>\n  )\n}\n\nfunction DesktopNav({\n  navItems,\n  onOpenChange,\n}: {\n  navItems: NavItem[]\n  onOpenChange: (open: boolean) => void\n}) {\n  const [openLabel, setOpenLabel] = React.useState<string | null>(null)\n  const navRef = React.useRef<HTMLElement>(null)\n\n  React.useEffect(() => {\n    onOpenChange(openLabel !== null)\n  }, [openLabel, onOpenChange])\n\n  React.useEffect(() => {\n    if (openLabel === null) return\n    const onPointerDown = (e: PointerEvent) => {\n      if (!navRef.current?.contains(e.target as Node)) setOpenLabel(null)\n    }\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") setOpenLabel(null)\n    }\n    document.addEventListener(\"pointerdown\", onPointerDown)\n    document.addEventListener(\"keydown\", onKey)\n    return () => {\n      document.removeEventListener(\"pointerdown\", onPointerDown)\n      document.removeEventListener(\"keydown\", onKey)\n    }\n  }, [openLabel])\n\n  return (\n    <nav ref={navRef} className=\"max-md:hidden\" aria-label=\"Primary\">\n      <ul className=\"flex items-center gap-1\">\n        {navItems.map((item) =>\n          item.menu ? (\n            <DesktopNavMenu\n              key={item.label}\n              item={{ ...item, menu: item.menu }}\n              isOpen={openLabel === item.label}\n              onToggle={() =>\n                setOpenLabel((current) =>\n                  current === item.label ? null : item.label\n                )\n              }\n              onClose={() => setOpenLabel(null)}\n            />\n          ) : (\n            <li key={item.label}>\n              <a\n                href={item.href}\n                className=\"flex cursor-pointer items-center rounded-lg px-3 py-2 text-sm font-semibold text-text-secondary outline-none transition duration-150 ease-linear hover:bg-bg-secondary focus-visible:ring-4 focus-visible:ring-ring/25\"\n              >\n                {item.label}\n              </a>\n            </li>\n          )\n        )}\n      </ul>\n    </nav>\n  )\n}\n\nfunction MobileMenuSection({ label, menu }: { label: string; menu: MegaMenu }) {\n  const [isOpen, setIsOpen] = React.useState(false)\n  return (\n    <li className=\"flex flex-col gap-0.5\">\n      <button\n        type=\"button\"\n        aria-expanded={isOpen}\n        onClick={() => setIsOpen(!isOpen)}\n        className=\"flex w-full items-center justify-between px-4 py-3 text-md font-semibold text-text-primary hover:bg-bg-secondary\"\n      >\n        {label}\n        <ChevronDown\n          className={cn(\n            \"size-4 stroke-[2.625px] text-fg-quaternary transition duration-150 ease-linear\",\n            isOpen ? \"-rotate-180\" : \"rotate-0\"\n          )}\n        />\n      </button>\n      {isOpen ? (\n        <div className=\"flex flex-col gap-1 px-2 pb-3\">\n          {menu.items.map((item) => (\n            <a\n              key={item.title}\n              href={item.href}\n              {...(item.external\n                ? { target: \"_blank\", rel: \"noopener noreferrer\" }\n                : {})}\n              className=\"flex items-center gap-3 rounded-lg px-3 py-2.5 hover:bg-bg-secondary\"\n            >\n              <span className=\"flex size-9 shrink-0 items-center justify-center rounded-lg bg-brand-600 text-white\">\n                <item.icon className=\"size-4\" />\n              </span>\n              <span className=\"flex flex-col\">\n                <span className=\"text-sm font-semibold text-text-primary\">\n                  {item.title}\n                </span>\n                <span className=\"text-xs text-text-tertiary\">\n                  {item.subtitle}\n                </span>\n              </span>\n            </a>\n          ))}\n        </div>\n      ) : null}\n    </li>\n  )\n}\n\nfunction SiteHeader({ navItems, logo, ctas, className }: SiteHeaderProps) {\n  const [isScrolled, setIsScrolled] = React.useState(false)\n  const [isMenuOpen, setIsMenuOpen] = React.useState(false)\n  const [isMobileOpen, setIsMobileOpen] = React.useState(false)\n\n  React.useEffect(() => {\n    const onScroll = () => setIsScrolled(window.scrollY > 8)\n    onScroll()\n    window.addEventListener(\"scroll\", onScroll, { passive: true })\n    return () => window.removeEventListener(\"scroll\", onScroll)\n  }, [])\n\n  const elevated = isScrolled || isMenuOpen || isMobileOpen\n\n  return (\n    <header\n      data-slot=\"site-header\"\n      className={cn(\n        \"sticky top-0 z-40 flex h-16 w-full items-center justify-center backdrop-blur-lg transition duration-150 ease-linear md:h-18\",\n        elevated\n          ? \"border-b border-border-secondary bg-bg-primary shadow-sm\"\n          : \"border-b border-transparent bg-bg-primary/85\",\n        className\n      )}\n    >\n      <div className=\"flex size-full max-w-container flex-1 items-center px-4 md:px-8\">\n        <div className=\"flex w-full items-center justify-between gap-8\">\n          <div className=\"flex flex-1 items-center gap-10\">\n            <a href=\"#\" className=\"flex items-center\" aria-label=\"Home\">\n              {logo}\n            </a>\n            <DesktopNav navItems={navItems} onOpenChange={setIsMenuOpen} />\n          </div>\n\n          <div className=\"hidden items-center gap-2 md:flex\">\n            {ctas?.secondary ? (\n              <Button\n                variant=\"secondary\"\n                size=\"default\"\n                nativeButton={false}\n                render={<a href={ctas.secondary.href} />}\n              >\n                {ctas.secondary.label}\n              </Button>\n            ) : null}\n            {ctas?.primary ? (\n              <Button\n                variant=\"default\"\n                size=\"default\"\n                className=\"cta-arrow\"\n                nativeButton={false}\n                render={<a href={ctas.primary.href} />}\n              >\n                <span data-text>{ctas.primary.label}</span>\n                <ArrowRight data-icon />\n              </Button>\n            ) : null}\n          </div>\n\n          {/* Mobile menu */}\n          <div className=\"md:hidden\">\n            <Popover open={isMobileOpen} onOpenChange={setIsMobileOpen}>\n              <PopoverTrigger\n                render={\n                  <Button\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    aria-label=\"Toggle navigation menu\"\n                  />\n                }\n              >\n                {isMobileOpen ? <XClose /> : <Menu01 />}\n              </PopoverTrigger>\n              <PopoverContent\n                align=\"end\"\n                sideOffset={12}\n                className=\"w-[calc(100vw-2rem)] max-w-sm gap-0 p-0\"\n              >\n                <nav aria-label=\"Mobile\">\n                  <ul className=\"flex flex-col gap-0.5 py-3\">\n                    {navItems.map((item) =>\n                      item.menu ? (\n                        <MobileMenuSection\n                          key={item.label}\n                          label={item.label}\n                          menu={item.menu}\n                        />\n                      ) : (\n                        <li key={item.label}>\n                          <a\n                            href={item.href}\n                            className=\"flex items-center px-4 py-3 text-md font-semibold text-text-primary hover:bg-bg-secondary\"\n                          >\n                            {item.label}\n                          </a>\n                        </li>\n                      )\n                    )}\n                  </ul>\n                  {ctas?.primary || ctas?.secondary ? (\n                    <div className=\"flex flex-col gap-3 border-t border-border-secondary px-4 py-5\">\n                      {ctas?.primary ? (\n                        <Button\n                          variant=\"default\"\n                          size=\"lg\"\n                          className=\"cta-arrow\"\n                          nativeButton={false}\n                          render={<a href={ctas.primary.href} />}\n                        >\n                          <span data-text>{ctas.primary.label}</span>\n                          <ArrowRight data-icon />\n                        </Button>\n                      ) : null}\n                      {ctas?.secondary ? (\n                        <Button\n                          variant=\"secondary\"\n                          size=\"lg\"\n                          nativeButton={false}\n                          render={<a href={ctas.secondary.href} />}\n                        >\n                          {ctas.secondary.label}\n                        </Button>\n                      ) : null}\n                    </div>\n                  ) : null}\n                </nav>\n              </PopoverContent>\n            </Popover>\n          </div>\n        </div>\n      </div>\n    </header>\n  )\n}\n\nexport { SiteHeader }\n",
      "type": "registry:component",
      "target": "components/blocks/marketing/site-header.tsx"
    }
  ],
  "type": "registry:block"
}