{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cart-delivery-eta",
  "type": "registry:component",
  "title": "Cart delivery estimate",
  "description": "Arrival range in business days and a real warehouse cutoff countdown, computed after mount in the warehouse time zone.",
  "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-delivery-eta.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { Clock, Truck } from \"lucide-react\"\nimport { cn } from \"@/components/storekit/lib/cn\"\nimport { useSkBlock } from \"@/components/storekit/lib/sk-analytics\"\n\n/* Delivery estimate for the cart. Works out the real dispatch day from a\n   warehouse cutoff in the warehouse time zone, then adds business days for\n   transit to give an arrival range. Everything is computed after mount, so\n   the server renders a plain policy line and there is no hydration drift.\n   After the cutoff it names the next dispatch day instead of resetting. */\n\nconst DAY = 86_400_000\n\nfunction clockIn(timeZone: string, now: Date) {\n  const parts = new Intl.DateTimeFormat(\"en-US\", { timeZone, weekday: \"short\", hour: \"numeric\", minute: \"numeric\", second: \"numeric\", hourCycle: \"h23\" }).formatToParts(now)\n  const get = (t: string) => parts.find((p) => p.type === t)?.value ?? \"0\"\n  const day = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"].indexOf(get(\"weekday\"))\n  return { day, secs: Number(get(\"hour\")) * 3600 + Number(get(\"minute\")) * 60 + Number(get(\"second\")) }\n}\n\n/** Calendar offset (days) after adding `n` business days to a start weekday. */\nfunction addBusinessDays(startDay: number, n: number) {\n  let offset = 0\n  let added = 0\n  while (added < n) {\n    offset++\n    const wd = (startDay + offset) % 7\n    if (wd !== 0 && wd !== 6) added++\n  }\n  return offset\n}\n\nexport function CartDeliveryEta({\n  cutoffHour = 14,\n  timeZone = \"America/New_York\",\n  zoneLabel = \"ET\",\n  dispatchDays = [1, 2, 3, 4, 5],\n  minDays = 2,\n  maxDays = 4,\n  method = \"Standard shipping\",\n  className,\n}: {\n  /** Warehouse cutoff hour, 24h, in `timeZone`. */\n  cutoffHour?: number\n  timeZone?: string\n  zoneLabel?: string\n  /** Weekdays the warehouse ships, 0 = Sunday. */\n  dispatchDays?: number[]\n  /** Transit time in business days. */\n  minDays?: number\n  maxDays?: number\n  method?: string\n  className?: string\n}) {\n  const reduce = useReducedMotion()\n  const sk = useSkBlock(\"cart\", \"cart-delivery-eta\")\n  const [now, setNow] = React.useState<Date | null>(null)\n\n  React.useEffect(() => {\n    setNow(new Date())\n    const id = window.setInterval(() => setNow(new Date()), 30_000)\n    return () => window.clearInterval(id)\n  }, [])\n\n  const hourLabel = `${cutoffHour % 12 === 0 ? 12 : cutoffHour % 12}${cutoffHour < 12 ? \"am\" : \"pm\"} ${zoneLabel}`\n\n  let info: null | { today: boolean; left: string; shipDay: string; from: string; to: string } = null\n  if (now && dispatchDays.length) {\n    const { day, secs } = clockIn(timeZone, now)\n    const cutoff = cutoffHour * 3600\n    let offset = 0\n    const today = dispatchDays.includes(day) && secs < cutoff\n    if (!today) {\n      offset = 1\n      while (!dispatchDays.includes((day + offset) % 7) && offset < 8) offset++\n    }\n    const shipWd = (day + offset) % 7\n    const fmt = (o: number, opts: Intl.DateTimeFormatOptions) => new Intl.DateTimeFormat(\"en-US\", { timeZone, ...opts }).format(new Date(now.getTime() + o * DAY))\n    const short: Intl.DateTimeFormatOptions = { weekday: \"short\", month: \"short\", day: \"numeric\" }\n    const s = Math.max(0, cutoff - secs)\n    const h = Math.floor(s / 3600)\n    const m = Math.floor((s % 3600) / 60)\n    info = {\n      today,\n      left: h ? `${h}h ${m}m` : `${m}m`,\n      shipDay: offset === 1 ? \"tomorrow\" : fmt(offset, { weekday: \"long\" }),\n      from: fmt(offset + addBusinessDays(shipWd, minDays), short),\n      to: fmt(offset + addBusinessDays(shipWd, maxDays), short),\n    }\n  }\n\n  return (\n    <div ref={sk.ref as React.Ref<HTMLDivElement>} {...sk.attrs} className={cn(\"w-full rounded-sk-card border border-sk-line bg-sk-card p-4\", className)}>\n      <div className=\"flex items-start gap-3\">\n        <span className=\"grid h-9 w-9 shrink-0 place-items-center rounded-sk-chip bg-sk-ink/[0.06] text-sk-ink\">\n          <Truck size={16} />\n        </span>\n        <div className=\"min-w-0 flex-1\">\n          <p className=\"font-sk-mono text-[11px] uppercase tracking-[0.14em] text-sk-muted\">{method}</p>\n          <AnimatePresence mode=\"wait\" initial={false}>\n            <motion.p\n              key={info ? \"on\" : \"off\"}\n              initial={reduce ? { opacity: 0 } : { opacity: 0, y: 6 }}\n              animate={{ opacity: 1, y: 0 }}\n              exit={{ opacity: 0 }}\n              transition={{ duration: reduce ? 0 : 0.35, ease: [0.16, 1, 0.3, 1] }}\n              className=\"mt-1 text-[15px] font-medium text-sk-ink\"\n            >\n              {info ? (\n                <>\n                  Arrives <span className=\"whitespace-nowrap tabular-nums\">{info.from}</span> to <span className=\"whitespace-nowrap tabular-nums\">{info.to}</span>\n                </>\n              ) : (\n                <>\n                  Arrives in <span className=\"tabular-nums\">{minDays}</span> to <span className=\"tabular-nums\">{maxDays}</span> business days\n                </>\n              )}\n            </motion.p>\n          </AnimatePresence>\n        </div>\n      </div>\n      <p className=\"mt-3 flex items-center gap-2 border-t border-sk-line pt-3 text-[13px] text-sk-muted\" aria-live=\"off\">\n        <Clock size={14} className=\"shrink-0\" />\n        {info ? (\n          info.today ? (\n            <span>\n              Order in the next <span className=\"font-medium tabular-nums text-sk-ink\">{info.left}</span> and it ships today.\n            </span>\n          ) : (\n            <span>\n              Today&apos;s {hourLabel} cutoff has passed. Ships <span className=\"font-medium text-sk-ink\">{info.shipDay}</span>.\n            </span>\n          )\n        ) : (\n          <span>Orders placed before {hourLabel} on business days ship the same day.</span>\n        )}\n      </p>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/storekit/cart/cart-delivery-eta.tsx"
    }
  ],
  "meta": {
    "section": "cart",
    "variant": "eta",
    "niche": [
      "any"
    ],
    "commerce": [
      "shipping",
      "delivery"
    ],
    "previewHeight": 320
  }
}