{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hero-video",
  "type": "registry:block",
  "title": "Cinemagraph hero",
  "description": "Poster-first muted loop with a centered headline, rotating proof chips and a fade into the page.",
  "author": "StoreKit <hello@storekit.dev>",
  "categories": [
    "hero",
    "any"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://storekit-eight.vercel.app/r/cn.json",
    "https://storekit-eight.vercel.app/r/sk-analytics.json"
  ],
  "files": [
    {
      "path": "hero/hero-video.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useState } from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { cn } from \"@/components/storekit/lib/cn\"\nimport { useSkBlock } from \"@/components/storekit/lib/sk-analytics\"\n\n/* Poster-first cinemagraph hero. The poster paints immediately; a muted,\n   looping video takes over when `src` is set. A centered headline sits on a\n   fixed dark scrim, three proof chips rotate their copy every 2.4s, and the\n   bottom edge fades into the page color. */\n\nconst EASE = [0.23, 1, 0.32, 1] as const\n\ntype Cta = { label: string; href: string }\n\nexport function HeroVideo({\n  poster = \"/placeholder.webp\",\n  src,\n  eyebrow = \"Made in small batches\",\n  headline = \"Feel better by breakfast.\",\n  subcopy = \"Clean daily essentials, third-party tested and made in the USA. Delivered on your schedule.\",\n  primaryCta = { label: \"Shop now\", href: \"#\" },\n  secondaryCta = { label: \"See the process\", href: \"#\" },\n  chips = [\"Third-party tested\", \"Ships same day\", \"30-day guarantee\"],\n  interval = 2400,\n  className,\n}: {\n  poster?: string\n  src?: string\n  eyebrow?: string\n  headline?: string\n  subcopy?: string\n  primaryCta?: Cta\n  secondaryCta?: Cta\n  chips?: string[]\n  interval?: number\n  className?: string\n}) {\n  const reduce = useReducedMotion()\n  const [tick, setTick] = useState(0)\n  const sk = useSkBlock(\"hero\", \"hero-video\")\n\n  useEffect(() => {\n    if (reduce || chips.length < 2) return\n    const id = setInterval(() => setTick((t) => t + 1), interval)\n    return () => clearInterval(id)\n  }, [reduce, chips.length, interval])\n\n  const rise = (i: number) => ({\n    initial: { opacity: 0, y: reduce ? 0 : 8 },\n    animate: { opacity: 1, y: 0 },\n    transition: { duration: 0.45, ease: EASE, delay: reduce ? 0 : 0.1 + i * 0.06 },\n  })\n\n  return (\n    <section ref={sk.ref} {...sk.attrs} className={cn(\"relative isolate flex min-h-[540px] items-center overflow-hidden bg-[rgb(0_0_0)] lg:min-h-[620px]\", className)}>\n      <video\n        key={src ?? poster}\n        poster={poster}\n        autoPlay={!reduce}\n        muted\n        loop\n        playsInline\n        preload=\"metadata\"\n        aria-hidden\n        className=\"absolute inset-0 -z-10 h-full w-full object-cover\"\n      >\n        {src ? <source src={src} /> : null}\n      </video>\n      <div aria-hidden className=\"absolute inset-0 -z-10 bg-[rgb(0_0_0/0.5)]\" />\n      <div aria-hidden className=\"absolute inset-0 -z-10 bg-[radial-gradient(ellipse_70%_60%_at_50%_45%,rgb(0_0_0/0.4),transparent)]\" />\n      <div\n        aria-hidden\n        className=\"absolute inset-x-0 bottom-0 -z-10 h-32 bg-[linear-gradient(to_bottom,transparent,rgb(var(--sk-paper)))] sm:h-40\"\n      />\n\n      <div className=\"mx-auto flex w-full max-w-3xl flex-col items-center px-4 pb-32 pt-16 text-center text-[rgb(255_255_255)] [text-shadow:0_1px_16px_rgb(0_0_0/0.35)] sm:px-8 sm:pb-40\">\n        <motion.p {...rise(0)} className=\"font-sk-mono text-[11px] uppercase tracking-[0.14em] text-[rgb(255_255_255/0.72)]\">\n          {eyebrow}\n        </motion.p>\n        <motion.h1\n          {...rise(1)}\n          className=\"mt-4 text-balance font-sk-display font-semibold leading-[1.1] tracking-[-0.02em]\"\n          style={{ fontSize: \"clamp(2.25rem, 5.4vw, 4.25rem)\" }}\n        >\n          {headline}\n        </motion.h1>\n        <motion.p {...rise(2)} className=\"mt-4 max-w-md text-[15px] leading-[1.5] text-[rgb(255_255_255/0.8)]\">\n          {subcopy}\n        </motion.p>\n        <motion.div {...rise(3)} className=\"mt-8 flex w-full flex-col justify-center gap-3 sm:w-auto sm:flex-row\">\n          <a\n            href={primaryCta.href}\n            onClick={() => sk.emit(\"interact\", \"cta\")}\n            className=\"sk-btn inline-flex min-h-12 items-center justify-center py-2.5 text-center leading-tight rounded-sk-btn bg-sk-accent px-7 text-[15px] font-medium text-sk-accent-ink outline-none transition-[filter,transform] duration-200 ease-sk-out hover:brightness-[1.06] focus-visible:ring-2 focus-visible:ring-[rgb(255_255_255)] focus-visible:ring-offset-2 focus-visible:ring-offset-[rgb(0_0_0)] active:scale-[0.97]\"\n          >\n            {primaryCta.label}\n          </a>\n          <a\n            href={secondaryCta.href}\n            onClick={() => sk.emit(\"interact\", \"cta_secondary\")}\n            className=\"sk-btn inline-flex min-h-12 items-center justify-center py-2.5 text-center leading-tight rounded-sk-btn border border-[rgb(255_255_255/0.4)] px-7 text-[15px] font-medium text-[rgb(255_255_255)] outline-none transition-[background-color,transform] duration-200 ease-sk-out hover:bg-[rgb(255_255_255/0.1)] focus-visible:ring-2 focus-visible:ring-[rgb(255_255_255)] active:scale-[0.97]\"\n          >\n            {secondaryCta.label}\n          </a>\n        </motion.div>\n\n        <motion.ul {...rise(4)} className=\"mt-8 flex flex-wrap justify-center gap-2\" aria-label=\"Why it matters\">\n          {chips.slice(0, 3).map((_, i) => {\n            const label = chips[(i + tick) % chips.length]\n            return (\n              <li\n                key={i}\n                className=\"relative grid h-9 min-w-[11.5rem] place-items-center overflow-hidden rounded-sk-chip border border-[rgb(255_255_255/0.28)] bg-[rgb(0_0_0/0.45)] px-3\"\n              >\n                <AnimatePresence mode=\"popLayout\" initial={false}>\n                  <motion.span\n                    key={label}\n                    initial={{ opacity: 0, y: reduce ? 0 : 8 }}\n                    animate={{ opacity: 1, y: 0 }}\n                    exit={{ opacity: 0, y: reduce ? 0 : -8 }}\n                    transition={{ duration: 0.35, ease: EASE, delay: i * 0.08 }}\n                    className=\"font-sk-mono text-[11px] uppercase tracking-[0.14em]\"\n                  >\n                    {label}\n                  </motion.span>\n                </AnimatePresence>\n              </li>\n            )\n          })}\n        </motion.ul>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:block",
      "target": "components/storekit/hero/hero-video.tsx"
    }
  ],
  "docs": "Pass `src` for the loop; the poster shows until it can play and stays for reduced motion.",
  "meta": {
    "section": "hero",
    "variant": "video",
    "niche": [
      "any"
    ],
    "commerce": [
      "cta"
    ],
    "previewHeight": 620
  }
}