{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "site-header",
  "title": "Site Header",
  "description": "Sticky site header with editable NAV and CTA.",
  "dependencies": [
    "lucide-react",
    "motion",
    "next"
  ],
  "registryDependencies": [
    "@atroui/utils",
    "@atroui/brand",
    "@atroui/logo",
    "@atroui/theme-toggle"
  ],
  "files": [
    {
      "path": "registry/default/blocks/site-header.tsx",
      "content": "\"use client\"\n\nimport { ArrowRight, Menu, X } from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport Link from \"next/link\"\nimport { usePathname } from \"next/navigation\"\nimport { useEffect, useState } from \"react\"\nimport { createPortal } from \"react-dom\"\n\nimport { LogoWordmark } from \"@/components/brand/logo\"\nimport { getBrand } from \"@/lib/brand\"\nimport { cn } from \"@/lib/utils\"\nimport { ThemeToggle } from \"@/components/ui/theme-toggle\"\n\n/** Edit nav labels and hrefs freely after install. */\nconst NAV = [\n  { label: \"Work\", href: \"/work\" },\n  { label: \"Tools\", href: \"/tools\" },\n  { label: \"Services\", href: \"/services\" },\n  { label: \"Journal\", href: \"/journal\" },\n  { label: \"About\", href: \"/about\" },\n] as const\n\nconst CTA = { label: \"Hire us\", href: \"/contact\" }\n\nconst panelEase = [0.32, 0.72, 0, 1] as const // easeOutSoft — match apps/docs/lib/motion.ts\n\nfunction isActive(pathname: string, href: string) {\n  return pathname === href || pathname.startsWith(`${href}/`)\n}\n\n/**\n * iOS-safe lock — same contract as apps/docs/hooks/use-body-scroll-lock.ts.\n * Inlined so the registry item stays self-contained after install.\n */\nfunction useBodyScrollLock(locked: boolean) {\n  useEffect(() => {\n    if (!locked) return\n    const y = window.scrollY\n    const { style } = document.body\n    const prev = {\n      overflow: style.overflow,\n      position: style.position,\n      top: style.top,\n      width: style.width,\n      left: style.left,\n      right: style.right,\n    }\n    style.overflow = \"hidden\"\n    style.position = \"fixed\"\n    style.top = `-${y}px`\n    style.left = \"0\"\n    style.right = \"0\"\n    style.width = \"100%\"\n    return () => {\n      style.overflow = prev.overflow\n      style.position = prev.position\n      style.top = prev.top\n      style.width = prev.width\n      style.left = prev.left\n      style.right = prev.right\n      window.scrollTo(0, y)\n    }\n  }, [locked])\n}\n\nexport function SiteHeader() {\n  const pathname = usePathname()\n  const [open, setOpen] = useState(false)\n  const [mounted, setMounted] = useState(false)\n  const [scrolled, setScrolled] = useState(false)\n  const reduce = useReducedMotion()\n  const brandName = getBrand().name\n\n  useEffect(() => {\n    setMounted(true)\n  }, [])\n\n  useEffect(() => {\n    setOpen(false)\n  }, [pathname])\n\n  useEffect(() => {\n    const onScroll = () => setScrolled(window.scrollY > 8)\n    onScroll()\n    window.addEventListener(\"scroll\", onScroll, { passive: true })\n    return () => window.removeEventListener(\"scroll\", onScroll)\n  }, [])\n\n  useEffect(() => {\n    if (!open) return\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") setOpen(false)\n    }\n    window.addEventListener(\"keydown\", onKey)\n    return () => window.removeEventListener(\"keydown\", onKey)\n  }, [open])\n\n  useBodyScrollLock(open)\n\n  const drawer =\n    mounted &&\n    createPortal(\n      <AnimatePresence>\n        {open ? (\n          <motion.div\n            key=\"site-header-drawer\"\n            className=\"fixed inset-0 z-[200] flex md:hidden\"\n            role=\"dialog\"\n            aria-modal=\"true\"\n            aria-label=\"Navigation\"\n            initial={reduce ? false : { opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            transition={{ duration: 0.2 }}\n          >\n            <button\n              type=\"button\"\n              className=\"absolute inset-0 bg-background/80 backdrop-blur-sm\"\n              aria-label=\"Close menu\"\n              onClick={() => setOpen(false)}\n            />\n            <motion.div\n              id=\"mobile-nav\"\n              className=\"relative ml-auto flex h-full w-[min(100%,20rem)] flex-col border-l border-border-subtle bg-background shadow-[-20px_0_40px_rgba(0,0,0,0.25)]\"\n              initial={reduce ? false : { x: \"100%\" }}\n              animate={{ x: 0 }}\n              exit={{ x: \"100%\" }}\n              transition={{ duration: 0.28, ease: panelEase }}\n            >\n              <div className=\"flex items-center justify-between gap-3 border-b border-border-subtle px-4 py-3 pt-[max(0.75rem,env(safe-area-inset-top))]\">\n                <LogoWordmark />\n                <button\n                  type=\"button\"\n                  className=\"inline-flex size-9 items-center justify-center border border-border-subtle text-foreground\"\n                  aria-label=\"Close menu\"\n                  onClick={() => setOpen(false)}\n                >\n                  <X className=\"size-4\" aria-hidden />\n                </button>\n              </div>\n              <nav className=\"flex flex-1 flex-col gap-0.5 overflow-y-auto overscroll-contain px-2 py-3 pb-[env(safe-area-inset-bottom)]\">\n                {NAV.map((item) => {\n                  const active = isActive(pathname, item.href)\n                  return (\n                    <Link\n                      key={item.href}\n                      href={item.href}\n                      onClick={() => setOpen(false)}\n                      aria-current={active ? \"page\" : undefined}\n                      className={cn(\n                        \"rounded-lg px-3 py-3 text-[15px] transition-colors\",\n                        active\n                          ? \"bg-muted font-medium text-foreground\"\n                          : \"text-muted-foreground hover:bg-muted/60 hover:text-foreground\"\n                      )}\n                    >\n                      {item.label}\n                    </Link>\n                  )\n                })}\n                <Link\n                  href={CTA.href}\n                  onClick={() => setOpen(false)}\n                  className=\"mt-3 inline-flex h-11 items-center justify-center gap-2 rounded-lg bg-primary px-4 text-sm font-medium text-primary-foreground\"\n                >\n                  {CTA.label}\n                  <ArrowRight className=\"size-3.5\" aria-hidden />\n                </Link>\n              </nav>\n            </motion.div>\n          </motion.div>\n        ) : null}\n      </AnimatePresence>,\n      document.body\n    )\n\n  return (\n    <header\n      className={cn(\n        \"sticky top-0 z-50 pt-[env(safe-area-inset-top)] transition-[background-color,border-color] duration-200\",\n        scrolled || open\n          ? \"border-b border-border-subtle bg-background/85 backdrop-blur-xl backdrop-saturate-150\"\n          : \"border-b border-border-subtle/60 bg-background/50 backdrop-blur-md\"\n      )}\n    >\n      <div className=\"mx-auto max-w-7xl border-x border-border-subtle\">\n        <div className=\"flex h-14 items-center justify-between gap-4 px-4 sm:px-6\">\n          <Link\n            href=\"/\"\n            aria-label={`${brandName} home`}\n            className=\"shrink-0 transition-opacity hover:opacity-80\"\n          >\n            <LogoWordmark />\n          </Link>\n\n          <nav aria-label=\"Primary\" className=\"hidden md:block\">\n            <ul className=\"flex items-center gap-0.5\">\n              {NAV.map((item) => {\n                const active = isActive(pathname, item.href)\n                return (\n                  <li key={item.href}>\n                    <Link\n                      href={item.href}\n                      aria-current={active ? \"page\" : undefined}\n                      className={cn(\n                        \"relative inline-flex h-14 items-center px-3.5 text-[13px] font-medium tracking-tight transition-colors\",\n                        active\n                          ? \"text-foreground\"\n                          : \"text-muted-foreground hover:text-foreground\"\n                      )}\n                    >\n                      {item.label}\n                      {active ? (\n                        <motion.span\n                          layoutId=\"nav-rule\"\n                          className=\"absolute inset-x-3.5 bottom-0 h-0.5 bg-[var(--color-brand,#0b7bff)]\"\n                          transition={\n                            reduce\n                              ? { duration: 0 }\n                              : { type: \"spring\", bounce: 0, duration: 0.35 }\n                          }\n                          aria-hidden\n                        />\n                      ) : null}\n                    </Link>\n                  </li>\n                )\n              })}\n            </ul>\n          </nav>\n\n          <div className=\"flex items-center gap-2\">\n            <ThemeToggle />\n            <Link\n              href={CTA.href}\n              className=\"hidden h-9 items-center gap-1.5 rounded-lg bg-primary px-3.5 text-sm font-medium text-primary-foreground md:inline-flex\"\n            >\n              {CTA.label}\n              <ArrowRight className=\"size-3.5\" aria-hidden />\n            </Link>\n            <button\n              type=\"button\"\n              className=\"inline-flex size-9 items-center justify-center border border-border-subtle text-foreground transition-colors hover:bg-muted md:hidden active:scale-[0.97]\"\n              aria-expanded={open}\n              aria-controls=\"mobile-nav\"\n              aria-label={open ? \"Close menu\" : \"Open menu\"}\n              onClick={() => setOpen((o) => !o)}\n            >\n              {open ? (\n                <X className=\"size-4\" aria-hidden />\n              ) : (\n                <Menu className=\"size-4\" aria-hidden />\n              )}\n            </button>\n          </div>\n        </div>\n      </div>\n\n      {drawer}\n    </header>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/site-header.tsx"
    }
  ],
  "type": "registry:block"
}