{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flip-clock",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/watermelon-ui/flip-clock.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n  type FC,\n  type HTMLAttributes,\n  memo,\n  type ReactNode,\n  useEffect,\n  useState\n} from \"react\";\n\nconst flipUnitVariants = cva(\n  \"relative subpixel-antialiased perspective-[1000px] rounded-md overflow-hidden\",\n  {\n    variants: {\n      size: {\n        sm: \"w-10 min-w-10 h-14 text-3xl\", // Small (Compact UI)\n        md: \"w-14 min-w-14 h-20 text-5xl\", // Medium (Standard sidebar/header)\n        lg: \"w-17 min-w-17 h-24 text-6xl\", // Large (Focus/Hero)\n        xl: \"w-22 min-w-22 h-32 text-8xl\" // Extra Large (Dashboard/Landing)\n      },\n      variant: {\n        default: \"bg-primary text-primary-foreground\",\n        secondary: \"bg-secondary text-secondary-foreground\",\n        destructive: \"bg-destructive text-destructive-foreground\",\n        outline: \"border border-input bg-background text-foreground\",\n        muted: \"bg-muted text-muted-foreground\"\n      }\n    },\n    defaultVariants: {\n      size: \"md\",\n      variant: \"default\"\n    }\n  }\n);\n\ninterface FlipUnitProps\n  extends\n  HTMLAttributes<HTMLDivElement>,\n  VariantProps<typeof flipUnitVariants> {\n  digit: number | string;\n}\n\nconst commonCardStyle = cn(\n  \"absolute inset-x-0 overflow-hidden h-1/2 bg-inherit text-inherit\"\n);\n\nconst FlipUnit: FC<FlipUnitProps> = memo(function FlipUnit({\n  digit,\n  size,\n  variant,\n  className\n}: FlipUnitProps) {\n  const [prevDigit, setPrevDigit] = useState(digit);\n  const [flipping, setFlipping] = useState(false);\n\n  useEffect(() => {\n    if (digit !== prevDigit) {\n\n      setFlipping(true);\n      // Wait for the full animation (0.3s top + 0.3s bottom) before resetting\n      const timer = setTimeout(() => {\n        setFlipping(false);\n        setPrevDigit(digit);\n      }, 550); // Slightly less than 600ms to ensure smoothness\n      return () => clearTimeout(timer);\n    }\n  }, [digit, prevDigit]);\n\n  return (\n    <div className={cn(flipUnitVariants({ size, variant }), className)}>\n      {/* 1. Background Top (The NEW digit waiting) */}\n      <div className={cn(commonCardStyle, \"rounded-t-lg top-0\")}>\n        <DigitSpan position=\"top\">{digit}</DigitSpan>\n      </div>\n\n      {/* 2. Background Bottom (The OLD digit staying) */}\n      <div className={cn(commonCardStyle, \"rounded-b-lg translate-y-full\")}>\n        <DigitSpan position=\"bottom\">{prevDigit}</DigitSpan>\n      </div>\n\n      {/* 3. Top Flap (The OLD digit falling down) */}\n      <div\n        className={cn(\n          commonCardStyle,\n          \"z-20 origin-bottom backface-hidden rounded-t-lg\",\n          flipping && \"animate-flip-top\"\n        )}\n      >\n        <DigitSpan position=\"top\">{prevDigit}</DigitSpan>\n      </div>\n\n      {/* 4. Bottom Flap (The NEW digit appearing) */}\n      <div\n        className={cn(\n          commonCardStyle,\n          \"z-10 origin-top backface-hidden rounded-b-lg translate-y-full\",\n          flipping && \"animate-flip-bottom\"\n        )}\n        style={{ transform: \"rotateX(90deg)\" }}\n      >\n        <DigitSpan position=\"bottom\">{digit}</DigitSpan>\n      </div>\n\n      {/* Center Divider Shadow */}\n      <div className=\"absolute top-1/2 left-0 w-full h-px -translate-y-1/2 bg-background/50 z-30\" />\n    </div>\n  );\n});\n\ninterface DigitSpanProps {\n  children: ReactNode;\n  position?: \"top\" | \"bottom\";\n}\n\nfunction DigitSpan({ children, position }: DigitSpanProps) {\n  return (\n    <span\n      className={cn(\n        \"absolute left-0 right-0 w-full flex items-center justify-center\",\n        // The span should be the full height of the PARENT FlipUnit (200% of the half-card)\n        \"h-[200%]\"\n      )}\n      style={{\n        // If it's the top half, align the full span to the top\n        // If it's the bottom half, shift the full span up so its bottom half shows\n        top: position === \"top\" ? \"0%\" : \"-100%\"\n      }}\n    >\n      {children}\n    </span>\n  );\n}\n\nconst flipClockVariants = cva(\n  \"relative flex justify-center items-center font-mono font-medium\",\n  {\n    variants: {\n      size: {\n        sm: \"text-3xl space-x-1\",\n        md: \"text-5xl space-x-2\",\n        lg: \"text-6xl space-x-2\",\n        xl: \"text-8xl space-x-3\"\n      },\n      variant: {\n        default: \"\",\n        secondary: \"\",\n        destructive: \"\",\n        outline: \"\",\n        muted: \"\"\n      }\n    },\n    defaultVariants: {\n      size: \"md\",\n      variant: \"default\"\n    }\n  }\n);\n\ninterface FlipClockProps\n  extends\n  VariantProps<typeof flipClockVariants>,\n  HTMLAttributes<HTMLDivElement> {\n  countdown?: boolean;\n  targetDate?: Date;\n  showDays?: \"auto\" | \"always\" | \"never\";\n}\n\ninterface TimeLeft {\n  days: number;\n  hours: number;\n  minutes: number;\n  seconds: number;\n}\n\ntype FlipClockSize = NonNullable<\n  VariantProps<typeof flipClockVariants>[\"size\"]\n>;\n\nconst heightMap: Record<FlipClockSize, string> = {\n  sm: \"text-4xl\",\n  md: \"text-5xl\",\n  lg: \"text-6xl\",\n  xl: \"text-8xl\"\n};\n\nfunction ClockSeparator({ size }: { size?: FlipClockSize }) {\n  return (\n    <span\n      className={cn(\n        \"text-center -translate-y-[8%]\",\n        size ? heightMap[size] : heightMap[\"md\"]\n      )}\n    >\n      :\n    </span>\n  );\n}\n\nexport function FlipClock({\n  countdown = false,\n  targetDate,\n  size,\n  variant,\n  showDays = \"auto\",\n  className,\n  ...props\n}: FlipClockProps) {\n  const [time, setTime] = useState<TimeLeft>(getTime(countdown, targetDate));\n\n  useEffect(() => {\n    // Run a faster heartbeat (250ms) to catch the second rollover immediately\n    const timer = setInterval(() => {\n      const nextTime = getTime(countdown, targetDate);\n\n      // Only update state if the seconds actually changed to prevent unnecessary re-renders\n      setTime((prev) => {\n        if (\n          prev.seconds === nextTime.seconds &&\n          prev.minutes === nextTime.minutes\n        ) {\n          return prev;\n        }\n        return nextTime;\n      });\n    }, 250); // 4fps check is plenty\n\n    return () => clearInterval(timer);\n  }, [countdown, targetDate]);\n\n  const daysStr = String(time.days).padStart(3, \"0\");\n  const hoursStr = String(time.hours).padStart(2, \"0\");\n  const minutesStr = String(time.minutes).padStart(2, \"0\");\n  const secondsStr = String(time.seconds).padStart(2, \"0\");\n\n  const shouldShowDays =\n    countdown &&\n    (showDays === \"always\" || (showDays === \"auto\" && time.days > 0));\n\n  return (\n    <div\n      className={cn(flipClockVariants({ size, variant }), className)}\n      aria-live=\"polite\"\n      {...props}\n    >\n      <span className=\"sr-only absolute\">\n        {`${time.hours}:${time.minutes}:${time.seconds}`}\n      </span>\n\n      {/* Days */}\n      {shouldShowDays && (\n        <>\n          {daysStr.split(\"\").map((digit, i) => (\n            <FlipUnit\n              key={`d-${i}`}\n              digit={digit}\n              size={size}\n              variant={variant}\n            />\n          ))}\n          <ClockSeparator size={size!} />\n        </>\n      )}\n\n      {/* Hours */}\n      {hoursStr.split(\"\").map((digit, index) => (\n        <FlipUnit\n          key={`hour-${index}`}\n          digit={digit}\n          size={size}\n          variant={variant}\n        />\n      ))}\n\n      <ClockSeparator size={size!} />\n\n      {/* Minutes */}\n      {minutesStr.split(\"\").map((digit, index) => (\n        <FlipUnit\n          key={`minute-${index}`}\n          digit={digit}\n          size={size}\n          variant={variant}\n        />\n      ))}\n\n      <ClockSeparator size={size!} />\n\n      {/* Seconds */}\n      {secondsStr.split(\"\").map((digit, index) => (\n        <FlipUnit\n          key={`second-${index}`}\n          digit={digit}\n          size={size}\n          variant={variant}\n        />\n      ))}\n\n      {/* Injected Keyframes (The Shadcn \"Cheat Code\") */}\n      <style>{`\n        /* Use the same duration for both to keep them in sync */\n        .animate-flip-top {\n          animation: flip-top-anim 0.6s ease-in forwards;\n        }\n        .animate-flip-bottom {\n          animation: flip-bottom-anim 0.6s ease-out forwards;\n        }\n\n        @keyframes flip-top-anim {\n          0% {\n            transform: rotateX(0deg);\n            z-index: 30;\n          }\n          50%,\n          100% {\n            transform: rotateX(-90deg);\n            z-index: 10;\n          }\n        }\n\n        @keyframes flip-bottom-anim {\n          0%,\n          50% {\n            transform: rotateX(90deg);\n            z-index: 10;\n          }\n          100% {\n            transform: rotateX(0deg);\n            z-index: 30;\n          }\n        }\n      `}</style>\n    </div>\n  );\n}\n\nfunction getTime(countdown: boolean, targetDate?: Date): TimeLeft {\n  const now = new Date();\n\n  // Real-time Clock Mode\n  if (!countdown) {\n    return {\n      days: 0,\n      hours: now.getHours(),\n      minutes: now.getMinutes(),\n      seconds: now.getSeconds()\n    };\n  }\n\n  // Countdown Mode\n  if (!targetDate) return { days: 0, hours: 0, minutes: 0, seconds: 0 };\n  const diff = Math.max(0, targetDate.getTime() - now.getTime());\n\n  return {\n    days: Math.floor(diff / (1000 * 60 * 60 * 24)),\n    hours: Math.floor((diff / (1000 * 60 * 60)) % 24),\n    minutes: Math.floor((diff / (1000 * 60)) % 60),\n    seconds: Math.floor((diff / 1000) % 60)\n  };\n}\n",
      "type": "registry:ui",
      "target": "components/ui/flip-clock.tsx"
    }
  ],
  "type": "registry:ui"
}
