{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cart-timer",
  "type": "registry:component",
  "title": "Reservation timer",
  "description": "Countdown to a real stock-hold expiry. Renders nothing without expiresAt and says plainly when the hold has ended.",
  "author": "StoreKit <hello@storekit.dev>",
  "categories": [
    "cart",
    "any"
  ],
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "https://storekit-eight.vercel.app/r/cn.json",
    "https://storekit-eight.vercel.app/r/sk-analytics.json"
  ],
  "files": [
    {
      "path": "cart/cart-timer.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\nimport { Clock } from \"lucide-react\"\nimport { cn } from \"@/components/storekit/lib/cn\"\nimport { useSkBlock } from \"@/components/storekit/lib/sk-analytics\"\n\n/* Reservation timer, only for real holds. It renders nothing unless you\n   pass the actual expiry your inventory system set, it counts down to that\n   moment (computed after mount), and when the hold ends it says so plainly\n   instead of restarting. */\n\nexport function CartTimer({\n  expiresAt,\n  holdMinutes,\n  className,\n}: {\n  /** When the stock hold ends. Date, ISO string or epoch ms. Omit and nothing renders. */\n  expiresAt?: Date | string | number\n  /** Length of the hold, for the progress bar and copy. */\n  holdMinutes?: number\n  className?: string\n}) {\n  const reduce = useReducedMotion()\n  const sk = useSkBlock(\"cart\", \"cart-timer\")\n  const [now, setNow] = React.useState<number | null>(null)\n  const end = expiresAt === undefined ? null : new Date(expiresAt).getTime()\n\n  React.useEffect(() => {\n    if (end === null || Number.isNaN(end)) return\n    setNow(Date.now())\n    const id = window.setInterval(() => setNow(Date.now()), 1000)\n    return () => window.clearInterval(id)\n  }, [end])\n\n  if (end === null || Number.isNaN(end)) return null\n\n  const left = now === null ? null : Math.max(0, end - now)\n  const expired = left === 0\n  const mm = left === null ? \"--\" : String(Math.floor(left / 60000))\n  const ss = left === null ? \"--\" : String(Math.floor((left % 60000) / 1000)).padStart(2, \"0\")\n  const total = holdMinutes ? holdMinutes * 60000 : null\n  const frac = total && left !== null ? Math.min(1, left / total) : 1\n\n  return (\n    <div ref={sk.ref as React.Ref<HTMLDivElement>} {...sk.attrs} className={cn(\"w-full overflow-hidden rounded-sk-card border border-sk-line bg-sk-card\", className)}>\n      <div className=\"flex items-start gap-3 p-4\">\n        <Clock size={16} className={cn(\"mt-0.5 shrink-0\", expired ? \"text-sk-muted\" : \"text-sk-ink\")} />\n        <div className=\"min-w-0 flex-1\" role=\"timer\" aria-live=\"off\">\n          {expired ? (\n            <>\n              <p className=\"text-sm font-medium text-sk-ink\">Your hold has ended</p>\n              <p className=\"mt-0.5 text-[13px] leading-[1.5] text-sk-muted\">Items are still in your cart. Stock is no longer set aside, so availability is checked again at checkout.</p>\n            </>\n          ) : (\n            <>\n              <p className=\"text-sm font-medium text-sk-ink\">\n                Held for you for <span className=\"tabular-nums\">{mm}:{ss}</span>\n              </p>\n              <p className=\"mt-0.5 text-[13px] leading-[1.5] text-sk-muted\">\n                {holdMinutes ? `We set stock aside for ${holdMinutes} minutes when you add a limited-run item.` : \"We set this stock aside when you added it.\"} After that it returns to general stock.\n              </p>\n            </>\n          )}\n        </div>\n      </div>\n      {total && !expired ? (\n        <div className=\"h-1 bg-sk-ink/[0.06]\">\n          <motion.div className=\"h-full origin-left bg-sk-accent\" initial={false} animate={{ scaleX: frac }} transition={{ duration: reduce ? 0 : 1, ease: \"linear\" }} />\n        </div>\n      ) : null}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/storekit/cart/cart-timer.tsx"
    }
  ],
  "docs": "Pass the expiry your inventory system set. Never reset it client side.",
  "meta": {
    "section": "cart",
    "variant": "timer",
    "niche": [
      "any"
    ],
    "commerce": [
      "reservation"
    ],
    "previewHeight": 220
  }
}