{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "nav-mega",
  "type": "registry:block",
  "title": "Mega menu header",
  "description": "Shop panel with categories, a featured product, and a promo tile. Hover, click, or keyboard.",
  "author": "StoreKit <hello@storekit.dev>",
  "categories": [
    "nav",
    "any"
  ],
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "https://storekit-eight.vercel.app/r/cn.json",
    "https://storekit-eight.vercel.app/r/price.json",
    "https://storekit-eight.vercel.app/r/product-image.json",
    "https://storekit-eight.vercel.app/r/sk-analytics.json"
  ],
  "files": [
    {
      "path": "nav/nav-mega.tsx",
      "content": "\"use client\"\n\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { ArrowRight, ChevronDown, Search, ShoppingBag } from \"lucide-react\"\nimport { cn } from \"@/components/storekit/lib/cn\"\nimport { Price } from \"@/components/storekit/ui/price\"\nimport { ProductImage } from \"@/components/storekit/ui/product-image\"\nimport { useSkBlock } from \"@/components/storekit/lib/sk-analytics\"\n\n/* Header with a \"Shop\" mega panel. Opens on hover, click, or ArrowDown from\n   the trigger; closes on mouse leave (with a short grace delay), Escape, or\n   when focus leaves the menu. Escape returns focus to the trigger. Pass\n   `defaultOpen` to render with the panel showing (previews, screenshots). */\n\ntype Category = { label: string; href: string; note?: string }\ntype Featured = { title: string; price: number; compareAt?: number; image: string; href: string }\ntype Promo = { eyebrow: string; title: string; cta: string; href: string }\n\nconst ring =\n  \"outline-none focus-visible:ring-2 focus-visible:ring-sk-accent focus-visible:ring-offset-2 focus-visible:ring-offset-sk-paper\"\n\nexport function NavMega({\n  brandName = \"Morrow\",\n  markSrc,\n  categories = [\n    { label: \"Powders\", href: \"#powders\", note: \"Greens, protein, electrolytes\" },\n    { label: \"Capsules and gummies\", href: \"#capsules-gummies\", note: \"Focus, Magnesium Night\" },\n    { label: \"Drinks\", href: \"#drinks\", note: \"Sparkling Recovery\" },\n    { label: \"Bundles\", href: \"#bundles\", note: \"Morning and night sets\" },\n    { label: \"Subscribe and save\", href: \"#subscribe\", note: \"15% off every order\" },\n  ],\n  featured = { title: \"Daily Greens\", price: 4900, image: \"/brands/morrow/greens.webp\", href: \"#daily-greens\" },\n  promo = { eyebrow: \"Bundle\", title: \"Any three pouches, 15% off\", cta: \"Build a routine\", href: \"#bundles\" },\n  links = [\n    { label: \"Lab results\", href: \"#lab-results\" },\n    { label: \"Journal\", href: \"#journal\" },\n  ],\n  cartCount = 0,\n  onCart,\n  defaultOpen = false,\n  className,\n}: {\n  brandName?: string\n  markSrc?: string\n  categories?: Category[]\n  featured?: Featured\n  promo?: Promo\n  links?: { label: string; href: string }[]\n  cartCount?: number\n  onCart?: () => void\n  defaultOpen?: boolean\n  className?: string\n}) {\n  const reduce = useReducedMotion()\n  const sk = useSkBlock(\"nav\", \"nav-mega\")\n  const [open, setOpen] = useState(defaultOpen)\n  const panelId = useId()\n  const wrapRef = useRef<HTMLDivElement>(null)\n  const triggerRef = useRef<HTMLButtonElement>(null)\n  const panelRef = useRef<HTMLDivElement>(null)\n  const closeTimer = useRef<number | undefined>(undefined)\n  const focusFirst = useRef(false)\n  const [origin, setOrigin] = useState(\"top left\")\n\n  const show = useCallback(() => {\n    window.clearTimeout(closeTimer.current)\n    const t = triggerRef.current\n    const header = t?.closest(\"header\")\n    if (t && header) {\n      const a = t.getBoundingClientRect()\n      const b = header.getBoundingClientRect()\n      setOrigin(`${Math.round(a.left + a.width / 2 - b.left)}px top`)\n    }\n    setOpen(true)\n  }, [])\n  const hide = useCallback(() => {\n    window.clearTimeout(closeTimer.current)\n    closeTimer.current = window.setTimeout(() => setOpen(false), 120)\n  }, [])\n\n  useEffect(() => () => window.clearTimeout(closeTimer.current), [])\n\n  useEffect(() => {\n    if (!open) return\n    if (focusFirst.current) {\n      focusFirst.current = false\n      requestAnimationFrame(() => panelRef.current?.querySelector<HTMLElement>(\"a\")?.focus())\n    }\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") {\n        setOpen(false)\n        triggerRef.current?.focus()\n      }\n    }\n    document.addEventListener(\"keydown\", onKey)\n    return () => document.removeEventListener(\"keydown\", onKey)\n  }, [open])\n\n  const linkCls = cn(\"inline-flex h-11 items-center gap-1 rounded-sk-btn px-2.5 text-sm sm:px-3 text-sk-muted transition-colors duration-200 hover:text-sk-ink\", ring)\n\n  return (\n    <header ref={sk.ref} {...sk.attrs} className={cn(\"relative z-30 border-b border-sk-line bg-sk-paper\", className)}>\n      <div className=\"mx-auto flex h-16 max-w-6xl items-center gap-1 px-4 sm:gap-6 sm:px-8\">\n        <a href=\"/\" className={cn(\"flex min-h-11 min-w-0 items-center gap-2 sm:gap-2.5 rounded-sk-btn pr-1\", ring)}>\n          {markSrc ? <img src={markSrc} alt=\"\" className=\"h-7 w-7 shrink-0 object-contain\" /> : null}\n          <span className=\"truncate whitespace-nowrap font-sk-display text-[15px] font-semibold tracking-[-0.02em] text-sk-ink\">{brandName}</span>\n        </a>\n\n        <nav aria-label=\"Primary\" className=\"flex shrink-0 items-center sm:flex-1\">\n          <ul className=\"flex items-center gap-1\">\n            <li>\n              <div\n                ref={wrapRef}\n                onMouseEnter={show}\n                onMouseLeave={hide}\n                onBlur={(e) => {\n                  if (!wrapRef.current?.contains(e.relatedTarget as Node)) setOpen(false)\n                }}\n              >\n                <button\n                  ref={triggerRef}\n                  type=\"button\"\n                  aria-expanded={open}\n                  aria-controls={panelId}\n                  onClick={() => (open ? setOpen(false) : show())}\n                  onKeyDown={(e) => {\n                    if (e.key === \"ArrowDown\") {\n                      e.preventDefault()\n                      focusFirst.current = true\n                      if (open) panelRef.current?.querySelector<HTMLElement>(\"a\")?.focus()\n                      else show()\n                    }\n                  }}\n                  className={cn(linkCls, open && \"text-sk-ink\")}\n                >\n                  Shop\n                  <ChevronDown size={14} aria-hidden className={cn(\"transition-transform duration-200\", open && \"rotate-180\")} />\n                </button>\n                <AnimatePresence>\n                  {open ? (\n                    <motion.div\n                      ref={panelRef}\n                      id={panelId}\n                      initial={reduce ? { opacity: 0 } : { opacity: 0, y: 6, scale: 0.95 }}\n                      animate={{ opacity: 1, y: 0, scale: 1 }}\n                      exit={reduce ? { opacity: 0, transition: { duration: 0.15 } } : { opacity: 0, y: 6, scale: 0.95, transition: { duration: 0.15 } }}\n                      transition={{ duration: 0.2, ease: [0.16, 1, 0.3, 1] }}\n                      style={{ transformOrigin: origin }}\n                      className=\"absolute inset-x-0 top-full max-h-[calc(100dvh-4rem)] overflow-y-auto overscroll-contain border-b border-sk-line bg-sk-paper shadow-[0_24px_60px_-24px_rgb(0_0_0/0.35)]\"\n                    >\n                      <div className=\"mx-auto grid max-w-6xl gap-6 px-4 py-6 sm:px-8 md:grid-cols-[1.1fr_1fr_1fr]\">\n                        <div>\n                          <p className=\"font-sk-mono text-[11px] uppercase tracking-[0.14em] text-sk-muted\">Categories</p>\n                          <ul className=\"mt-3 grid\">\n                            {categories.map((c) => (\n                              <li key={c.href}>\n                                <a\n                                  href={c.href}\n                                  className={cn(\"group flex min-h-11 items-center justify-between gap-3 rounded-sk-btn px-2 -mx-2 transition-colors duration-200 hover:bg-sk-ink/[0.04]\", ring)}\n                                >\n                                  <span className=\"text-sm font-medium text-sk-ink\">{c.label}</span>\n                                  {c.note ? <span className=\"hidden text-right text-[13px] text-sk-muted sm:inline\">{c.note}</span> : null}\n                                </a>\n                              </li>\n                            ))}\n                          </ul>\n                        </div>\n                        <a\n                          href={featured.href}\n                          className={cn(\"group flex gap-4 rounded-sk-card border border-sk-line bg-sk-card p-4 transition-colors duration-200 hover:border-sk-ink/20 md:flex-col\", ring)}\n                        >\n                          <ProductImage src={featured.image} alt={featured.title} className=\"w-28 shrink-0 rounded-sk-card md:w-full md:aspect-[16/10]\" />\n                          <span className=\"flex flex-col gap-1\">\n                            <span className=\"font-sk-mono text-[11px] uppercase tracking-[0.14em] text-sk-muted\">Featured</span>\n                            <span className=\"text-sm font-medium text-sk-ink\">{featured.title}</span>\n                            <Price amount={featured.price} compareAt={featured.compareAt} size=\"sm\" />\n                          </span>\n                        </a>\n                        <a\n                          href={promo.href}\n                          className={cn(\"group hidden flex-col justify-between gap-6 rounded-sk-card bg-sk-ink p-5 md:flex text-sk-paper transition-opacity duration-200 hover:opacity-95\", ring)}\n                        >\n                          <span className=\"font-sk-mono text-[11px] uppercase tracking-[0.14em] text-sk-paper/60\">{promo.eyebrow}</span>\n                          <span className=\"font-sk-display text-xl font-semibold leading-[1.1] tracking-[-0.02em] text-balance\">{promo.title}</span>\n                          <span className=\"inline-flex items-center gap-1.5 text-sm font-medium\">\n                            {promo.cta}\n                            <ArrowRight size={14} aria-hidden className=\"transition-transform duration-200 group-hover:translate-x-0.5\" />\n                          </span>\n                        </a>\n                      </div>\n                    </motion.div>\n                  ) : null}\n                </AnimatePresence>\n              </div>\n            </li>\n            {links.map((l) => (\n              <li key={l.href} className=\"hidden sm:block\">\n                <a href={l.href} className={linkCls}>{l.label}</a>\n              </li>\n            ))}\n          </ul>\n        </nav>\n\n        <div className=\"ml-auto flex shrink-0 items-center\">\n          <button type=\"button\" aria-label=\"Search\" className={cn(\"grid h-11 w-11 place-items-center rounded-sk-btn text-sk-ink transition-colors duration-200 hover:bg-sk-ink/[0.05]\", ring)}>\n            <Search size={18} aria-hidden />\n          </button>\n          <button\n            type=\"button\"\n            aria-label={`Cart, ${cartCount} ${cartCount === 1 ? \"item\" : \"items\"}`}\n            onClick={() => {\n              sk.emit(\"interact\", \"cart_open\")\n              onCart?.()\n            }}\n            className={cn(\"relative grid h-11 w-11 place-items-center rounded-sk-btn text-sk-ink transition-colors duration-200 hover:bg-sk-ink/[0.05]\", ring)}>\n            <ShoppingBag size={18} aria-hidden />\n            {cartCount > 0 ? (\n              <span aria-hidden className=\"absolute right-1 top-1 grid h-[18px] min-w-[18px] place-items-center rounded-full bg-sk-accent px-1 text-[10px] font-semibold leading-none text-sk-accent-ink\">{cartCount}</span>\n            ) : null}\n          </button>\n        </div>\n      </div>\n    </header>\n  )\n}\n",
      "type": "registry:block",
      "target": "components/storekit/nav/nav-mega.tsx"
    }
  ],
  "meta": {
    "section": "nav",
    "variant": "mega",
    "niche": [
      "any"
    ],
    "commerce": [
      "merchandising",
      "promo"
    ],
    "previewHeight": 500
  }
}