{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "countdown",
  "title": "Countdown",
  "description": "Launch and sale countdown timer with flipping digits.",
  "dependencies": [],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/countdown.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { gsap, motionScale } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\ntype CountdownProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  /** The moment to count down to. */\n  to: Date | string | number;\n  /** Units to show, largest first. */\n  units?: (\"days\" | \"hours\" | \"minutes\" | \"seconds\")[];\n  /** Called once when it reaches zero. */\n  onComplete?: () => void;\n  labels?: boolean;\n};\n\nconst MS = { days: 86400000, hours: 3600000, minutes: 60000, seconds: 1000 };\n\n/**\n * A countdown to a launch, sale or event. Each unit is a tile whose digits flip down when they change (GSAP);\n * the server renders the real remaining time inside a <time> element for crawlers and screen readers.\n */\nfunction Countdown({ to, units = [\"days\", \"hours\", \"minutes\", \"seconds\"], onComplete, labels = true, className, ...props }: CountdownProps) {\n  const target = React.useMemo(() => new Date(to).getTime(), [to]);\n  const [now, setNow] = React.useState(() => Date.now());\n  const done = React.useRef(false);\n  const ref = React.useRef<HTMLDivElement>(null);\n\n  React.useEffect(() => {\n    const id = setInterval(() => setNow(Date.now()), 1000);\n    return () => clearInterval(id);\n  }, []);\n\n  let left = Math.max(0, target - now);\n  const parts = units.map((u) => {\n    const v = Math.floor(left / MS[u]);\n    left -= v * MS[u];\n    return { unit: u, value: String(v).padStart(2, \"0\") };\n  });\n\n  React.useEffect(() => {\n    if (target - now <= 0 && !done.current) {\n      done.current = true;\n      onComplete?.();\n    }\n  }, [now, target, onComplete]);\n\n  return (\n    <div ref={ref} data-slot=\"countdown\" role=\"timer\" className={cn(\"inline-flex items-start gap-1 sm:gap-2\", className)} {...props}>\n      <time dateTime={new Date(target).toISOString()} className=\"sr-only\" suppressHydrationWarning>\n        {parts.map((p) => `${Number(p.value)} ${p.unit}`).join(\", \")} remaining\n      </time>\n      {parts.map((p, i) => (\n        <React.Fragment key={p.unit}>\n          {i > 0 && (\n            <span aria-hidden=\"true\" className=\"pt-[clamp(0.375rem,2.4vw,0.75rem)] font-mono text-base text-muted-foreground sm:pt-3 sm:text-xl\">\n              :\n            </span>\n          )}\n          <div aria-hidden=\"true\" className=\"grid justify-items-center gap-1.5\">\n            <div className=\"flex gap-0.5 sm:gap-1\">\n              {[...p.value].map((d, j) => (\n                <Digit key={j} value={d} />\n              ))}\n            </div>\n            {labels && <span className=\"cue-label\">{p.unit}</span>}\n          </div>\n        </React.Fragment>\n      ))}\n    </div>\n  );\n}\n\nfunction Digit({ value }: { value: string }) {\n  const ref = React.useRef<HTMLSpanElement>(null);\n  const prev = React.useRef(value);\n  React.useLayoutEffect(() => {\n    const el = ref.current;\n    if (!el || prev.current === value) return;\n    prev.current = value;\n    if (motionScale(el)) gsap.fromTo(el, { yPercent: -60, opacity: 0, filter: \"blur(2px)\" }, { yPercent: 0, opacity: 1, filter: \"blur(0px)\", duration: 0.35, ease: \"cue\" });\n  }, [value]);\n  return (\n    <span className=\"relative grid h-[clamp(2.25rem,12vw,3.5rem)] w-[clamp(1.375rem,7.2vw,2.5rem)] place-items-center overflow-hidden rounded-md border border-border bg-card font-mono text-[clamp(1.125rem,6vw,1.875rem)] sm:h-14 sm:w-10 sm:rounded-lg sm:text-3xl font-semibold tabular-nums shadow-raised after:absolute after:inset-x-0 after:top-1/2 after:h-px after:bg-border/70\">\n      <span ref={ref} suppressHydrationWarning>\n        {value}\n      </span>\n    </span>\n  );\n}\n\nexport { Countdown };\nexport type { CountdownProps };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/countdown",
  "categories": [
    "display"
  ],
  "type": "registry:ui"
}