{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "page-store",
  "type": "registry:page",
  "title": "Example store",
  "description": "The full example storefront in one install: announcement, nav with a mobile drawer, hero, proof, categories, grid, product detail, lab results, reviews, guarantees, how it works, FAQ, CTA, footer and a working cart drawer.",
  "author": "StoreKit <hello@storekit.dev>",
  "categories": [
    "page",
    "any"
  ],
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "https://storekit-eight.vercel.app/r/cn.json",
    "https://storekit-eight.vercel.app/r/announcement-countdown.json",
    "https://storekit-eight.vercel.app/r/nav-minimal.json",
    "https://storekit-eight.vercel.app/r/nav-drawer.json",
    "https://storekit-eight.vercel.app/r/hero-split.json",
    "https://storekit-eight.vercel.app/r/trust-stats.json",
    "https://storekit-eight.vercel.app/r/collection-categories.json",
    "https://storekit-eight.vercel.app/r/collection-grid.json",
    "https://storekit-eight.vercel.app/r/pdp-gallery.json",
    "https://storekit-eight.vercel.app/r/pdp-buybox.json",
    "https://storekit-eight.vercel.app/r/pdp-coa-band.json",
    "https://storekit-eight.vercel.app/r/reviews-summary.json",
    "https://storekit-eight.vercel.app/r/reviews-list.json",
    "https://storekit-eight.vercel.app/r/trust-guarantees.json",
    "https://storekit-eight.vercel.app/r/how-it-works.json",
    "https://storekit-eight.vercel.app/r/faq-accordion.json",
    "https://storekit-eight.vercel.app/r/cta-band.json",
    "https://storekit-eight.vercel.app/r/footer-columns.json",
    "https://storekit-eight.vercel.app/r/cart-drawer.json"
  ],
  "files": [
    {
      "path": "page/page-store.tsx",
      "content": "\"use client\"\n\nimport { useState } from \"react\"\nimport { cn } from \"@/components/storekit/lib/cn\"\nimport { AnnouncementCountdown } from \"@/components/storekit/announcement-bar/announcement-countdown\"\nimport { NavMinimal } from \"@/components/storekit/nav/nav-minimal\"\nimport { NavDrawer } from \"@/components/storekit/nav/nav-drawer\"\nimport { CartDrawer, type CartDrawerLine } from \"@/components/storekit/cart/cart-drawer\"\nimport { HeroSplit } from \"@/components/storekit/hero/hero-split\"\nimport { TrustStats } from \"@/components/storekit/trust/trust-stats\"\nimport { TrustGuarantees } from \"@/components/storekit/trust/trust-guarantees\"\nimport { CollectionCategories } from \"@/components/storekit/collection/collection-categories\"\nimport { CollectionGrid, type GridProduct } from \"@/components/storekit/collection/collection-grid\"\nimport { PdpGallery } from \"@/components/storekit/pdp/pdp-gallery\"\nimport { PdpBuybox } from \"@/components/storekit/pdp/pdp-buybox\"\nimport { PdpCoaBand } from \"@/components/storekit/pdp/pdp-coa-band\"\nimport { ReviewsSummary } from \"@/components/storekit/reviews/reviews-summary\"\nimport { ReviewsList } from \"@/components/storekit/reviews/reviews-list\"\nimport { HowItWorks } from \"@/components/storekit/marketing/how-it-works\"\nimport { FaqAccordion } from \"@/components/storekit/marketing/faq-accordion\"\nimport { CtaBand } from \"@/components/storekit/marketing/cta-band\"\nimport { FooterColumns } from \"@/components/storekit/footer/footer-columns\"\n\n/* A complete storefront composed from eighteen StoreKit blocks and nothing\n   else: announcement, nav (desktop bar plus mobile drawer), hero, proof,\n   categories, grid, product detail, lab results, reviews, guarantees, how it\n   works, FAQ, CTA, footer and a cart drawer that every add-to-cart opens.\n   Swap the sample catalog for yours; the brand tokens do the rest. */\n\nexport type StoreProduct = {\n  id: string\n  title: string\n  /** Integer cents. */\n  price: number\n  compareAt?: number\n  image: string\n  /** Shop category, e.g. \"Powders\". Drives the category tiles and grid filters. */\n  category: string\n  /** Optional product form (\"powder\", \"capsule\", \"drink\"...). */\n  form?: string\n}\n\nexport type StoreCartLine = { productId: string; variantId?: string; quantity: number; subscribe?: boolean }\n\nconst SAMPLE_PRODUCTS: StoreProduct[] = [\n  { id: \"daily-greens\", title: \"Daily Greens\", price: 4900, compareAt: 5900, image: \"/placeholder.webp\", category: \"Powders\", form: \"powder\" },\n  { id: \"electrolytes\", title: \"Electrolytes\", price: 3200, image: \"/placeholder.webp\", category: \"Powders\", form: \"powder\" },\n  { id: \"magnesium-night\", title: \"Magnesium Night\", price: 2900, image: \"/placeholder.webp\", category: \"Capsules & gummies\", form: \"gummy\" },\n  { id: \"protein\", title: \"Protein\", price: 5400, compareAt: 6200, image: \"/placeholder.webp\", category: \"Powders\", form: \"powder\" },\n  { id: \"focus\", title: \"Focus\", price: 3900, image: \"/placeholder.webp\", category: \"Capsules & gummies\", form: \"capsule\" },\n  { id: \"sparkling-recovery\", title: \"Sparkling Recovery\", price: 3600, image: \"/placeholder.webp\", category: \"Drinks\", form: \"drink\" },\n]\n\nexport function PageStore({\n  brand = { name: \"Morrow\", mark: \"/placeholder.webp\" },\n  products = SAMPLE_PRODUCTS,\n  hero = {\n    headline: \"Daily wellness, made simple.\",\n    subcopy: \"Clean ingredients, third-party tested, made in the USA. One scoop a day, delivered on your schedule.\",\n    image: products[0]?.image ?? \"/placeholder.webp\",\n  },\n  categoryImages,\n  ctaImage,\n  onAddToCart,\n  className,\n}: {\n  brand?: { name: string; mark: string }\n  products?: StoreProduct[]\n  hero?: { headline: string; subcopy: string; image: string }\n  /** Tile image per category title. Defaults to the first product in that category. */\n  categoryImages?: Partial<Record<string, string>>\n  /** Image for the closing CTA band. Defaults to the lead product. */\n  ctaImage?: string\n  onAddToCart?: (line: StoreCartLine) => void\n  className?: string\n}) {\n  const lead = products[0] ?? SAMPLE_PRODUCTS[0]\n  const leadVariants = [\n    { id: \"30\", label: \"30 servings\", price: lead.price },\n    { id: \"60\", label: \"60 servings\", price: Math.round(lead.price * 1.8), compareAt: Math.round(lead.price * 2) },\n    { id: \"bundle\", label: \"3-pouch bundle\", price: Math.round(lead.price * 2.55), compareAt: Math.round(lead.price * 3) },\n  ]\n  const subscribeDiscount = 0.15\n\n  // The page owns the cart lines; the drawer edits them and reports back.\n  // Adding remounts the drawer (it is closed at that moment) with the new lines.\n  const [lines, setLines] = useState<CartDrawerLine[]>([])\n  const [cartOpen, setCartOpen] = useState(false)\n  const [cartRev, setCartRev] = useState(0)\n  // Upsells are picked when the drawer mounts so the row does not reshuffle mid-session.\n  const pickUpsells = (inCart: string[]) =>\n    products\n      .filter((x) => !inCart.includes(x.id))\n      .slice(0, 2)\n      .map((x) => ({ id: x.id, title: x.title, price: x.price, image: x.image }))\n  const [upsells, setUpsells] = useState(() => pickUpsells([]))\n  const cartCount = lines.reduce((n, l) => n + l.quantity, 0)\n\n  const add = (line: StoreCartLine) => {\n    const product = products.find((x) => x.id === line.productId)\n    if (product) {\n      const variant = product.id === lead.id ? leadVariants.find((v) => v.id === line.variantId) : undefined\n      const base = variant?.price ?? product.price\n      const price = line.subscribe ? Math.round(base * (1 - subscribeDiscount)) : base\n      const compareAt = line.subscribe ? base : variant?.compareAt ?? product.compareAt\n      const id = [product.id, line.variantId, line.subscribe ? \"sub\" : \"\"].filter(Boolean).join(\":\")\n      const label = [variant?.label, line.subscribe ? \"Subscribe and save\" : \"\"].filter(Boolean).join(\" / \") || undefined\n      setLines((ls) =>\n        ls.some((l) => l.id === id)\n          ? ls.map((l) => (l.id === id ? { ...l, quantity: l.quantity + line.quantity } : l))\n          : [...ls, { id, title: product.title, variant: label, price, compareAt, image: product.image, quantity: line.quantity }]\n      )\n      setUpsells(pickUpsells([...lines.map((l) => l.id.split(\":\")[0]), product.id]))\n      setCartRev((r) => r + 1)\n      setCartOpen(true)\n    }\n    onAddToCart?.(line)\n  }\n\n  const links = [\n    { label: \"Shop\", href: \"#shop\" },\n    { label: \"Lab results\", href: \"#coa\" },\n    { label: \"Reviews\", href: \"#reviews\" },\n    { label: \"FAQ\", href: \"#faq\" },\n  ]\n\n  const grid: GridProduct[] = products.map((x, i) => ({\n    id: x.id,\n    title: x.title,\n    price: x.price,\n    compareAt: x.compareAt,\n    image: x.image,\n    category: x.category,\n    form: x.form,\n    stock: i === 3 ? \"low\" : \"in\",\n    isNew: i === 2,\n  }))\n\n  const titles = Array.from(new Set(products.map((x) => x.category))).slice(0, 3)\n  const categories = titles.map((t) => ({\n    title: t,\n    count: products.filter((x) => x.category === t).length,\n    image: categoryImages?.[t] ?? products.find((x) => x.category === t)?.image ?? lead.image,\n    href: \"#shop\",\n  }))\n  const filters = [{ label: \"All\" }, ...titles.map((t) => ({ label: t, category: t }))]\n\n  return (\n    <div data-sk-section=\"page\" data-sk-variant=\"page-store\" className={cn(\"bg-sk-paper text-sk-ink\", className)}>\n      <AnnouncementCountdown cutoffHour={14} />\n      <NavMinimal className=\"hidden md:block\" brandName={brand.name} markSrc={brand.mark} cartCount={cartCount} onCart={() => setCartOpen(true)} links={links} />\n      <NavDrawer className=\"md:hidden\" brandName={brand.name} markSrc={brand.mark} cartCount={cartCount} onCart={() => setCartOpen(true)} categories={links} />\n      <HeroSplit\n        eyebrow={brand.name}\n        headline={hero.headline}\n        subcopy={hero.subcopy}\n        image={hero.image}\n        imageAlt={lead.title}\n        primaryCta={{ label: \"Shop the range\", href: \"#shop\" }}\n        secondaryCta={{ label: \"See lab results\", href: \"#coa\" }}\n        specs={[\n          { label: \"Bestseller\", value: lead.title },\n          { label: \"Servings\", value: \"30 per pouch\" },\n          { label: \"Per serving\", value: `$${(lead.price / 3000).toFixed(2)}`, highlight: true },\n          { label: \"Tested\", value: \"Third party\" },\n        ]}\n      />\n      <section aria-label=\"Proof\" className=\"mx-auto max-w-6xl px-4 sm:px-8\">\n        <TrustStats />\n      </section>\n      <section id=\"shop\" className=\"mx-auto max-w-6xl scroll-mt-20 px-4 py-12 sm:px-8 sm:py-16\">\n        <CollectionCategories categories={categories} />\n      </section>\n      <section className=\"mx-auto max-w-6xl px-4 pb-12 sm:px-8 sm:pb-16\">\n        <CollectionGrid products={grid} filters={filters} onAdd={(p) => add({ productId: (p as GridProduct).id, quantity: 1 })} />\n      </section>\n      <section className=\"border-y border-sk-line bg-sk-surface/60\">\n        <div className=\"mx-auto grid max-w-6xl gap-10 px-4 py-12 sm:px-8 sm:py-16 lg:grid-cols-[1.05fr_1fr] lg:gap-16\">\n          <PdpGallery images={products.slice(0, 4).map((x) => ({ src: x.image, alt: x.title }))} />\n          <PdpBuybox\n            title={lead.title}\n            variants={leadVariants}\n            subscribeDiscount={subscribeDiscount}\n            onAddToCart={(line) => add({ productId: lead.id, variantId: line.variantId, quantity: line.quantity, subscribe: line.subscribe })}\n          />\n        </div>\n      </section>\n      <div id=\"coa\" className=\"scroll-mt-20\">\n        <PdpCoaBand product={lead.title} lab={`${brand.name} partner lab`} />\n      </div>\n      <section id=\"reviews\" className=\"mx-auto grid scroll-mt-20 max-w-6xl gap-10 px-4 py-12 sm:px-8 sm:py-16 lg:grid-cols-[1fr_1.3fr]\">\n        <ReviewsSummary className=\"self-start lg:sticky lg:top-24 lg:grid-cols-1 lg:gap-8\" />\n        <ReviewsList />\n      </section>\n      <section className=\"border-t border-sk-line\">\n        <div className=\"mx-auto max-w-6xl px-4 py-12 sm:px-8 sm:py-16\">\n          <TrustGuarantees />\n        </div>\n      </section>\n      <HowItWorks\n        className=\"sm:py-16\"\n        steps={[\n          { title: \"Pick your routine\", body: \"Choose a product or two. Subscribe and save 15%, skip or cancel anytime.\", meta: \"2 min\", image: lead.image },\n          { title: \"Mix and go\", body: \"One scoop in water or a smoothie. No clumps, no chalky aftertaste.\", meta: \"30 sec\", image: products[1]?.image ?? lead.image },\n          { title: \"Feel the difference\", body: \"Most people notice steadier energy within two weeks. If not, it's on us.\", meta: \"30 days\", image: products[2]?.image ?? lead.image },\n        ]}\n      />\n      <div id=\"faq\" className=\"scroll-mt-20\">\n        <FaqAccordion className=\"pt-0 sm:pt-0\" />\n      </div>\n      <CtaBand image={ctaImage ?? lead.image} />\n      <FooterColumns brandName={brand.name} markSrc={brand.mark} />\n      <CartDrawer\n        key={cartRev}\n        open={cartOpen}\n        onOpenChange={setCartOpen}\n        items={lines}\n        onLinesChange={setLines}\n        upsells={upsells}\n      />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/storekit/page/page-store.tsx"
    },
    {
      "path": "page/store-page.tsx",
      "content": "import { PageStore } from \"@/components/storekit/page/page-store\"\n\n/* Installed as app/store/page.tsx. Next.js page files may only export the\n   page itself, so the store lives in components/storekit/page/page-store.tsx\n   and this file feeds it your catalog. Prices are integer cents. */\n\nexport default function StorePage() {\n  return (\n    <PageStore\n      brand={{ name: \"Morrow\", mark: \"/placeholder.webp\" }}\n      products={[\n        { id: \"daily-greens\", title: \"Daily Greens\", price: 4900, compareAt: 5900, image: \"/placeholder.webp\", category: \"Powders\", form: \"powder\" },\n        { id: \"electrolytes\", title: \"Electrolytes\", price: 3200, image: \"/placeholder.webp\", category: \"Powders\", form: \"powder\" },\n        { id: \"magnesium-night\", title: \"Magnesium Night\", price: 2900, image: \"/placeholder.webp\", category: \"Capsules & gummies\", form: \"gummy\" },\n        { id: \"protein\", title: \"Protein\", price: 5400, compareAt: 6200, image: \"/placeholder.webp\", category: \"Powders\", form: \"powder\" },\n        { id: \"focus\", title: \"Focus\", price: 3900, image: \"/placeholder.webp\", category: \"Capsules & gummies\", form: \"capsule\" },\n        { id: \"sparkling-recovery\", title: \"Sparkling Recovery\", price: 3600, image: \"/placeholder.webp\", category: \"Drinks\", form: \"drink\" },\n      ]}\n      hero={{\n        headline: \"Daily wellness, made simple.\",\n        subcopy: \"Clean ingredients, third-party tested, made in the USA. One scoop a day, delivered on your schedule.\",\n        image: \"/placeholder.webp\",\n      }}\n    />\n  )\n}\n",
      "type": "registry:page",
      "target": "app/store/page.tsx"
    }
  ],
  "docs": "Installs the full example store at app/store/page.tsx. Replace the sample products with your catalog.",
  "meta": {
    "section": "page",
    "variant": "store",
    "niche": [
      "any"
    ],
    "commerce": [
      "cart",
      "atc",
      "subscribe",
      "reviews",
      "coa"
    ],
    "previewHeight": 900
  }
}