{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hover-video-player",
  "type": "registry:ui",
  "description": "Video player component that plays on hover with smooth animations",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "slider"
  ],
  "files": [
    {
      "path": "registry/default/ui/hover-video-player.tsx",
      "content": "\"use client\"\n\n/**\n * HoverVideoPlayer Component\n *\n * A React component that plays video on hover/touch with advanced features like:\n * - Lazy loading and intersection observer support\n * - Mobile touch support\n * - Picture-in-Picture\n * - Custom overlay support\n * - Thumbnail support\n * - Playback controls\n *\n * @example\n * ```tsx\n * <HoverVideoPlayer\n *   videoSrc=\"/path/to/video.mp4\"\n *   thumbnailSrc=\"/path/to/thumbnail.jpg\"\n *   pausedOverlay={<PlayIcon />}\n *   loadingOverlay={<Spinner />}\n *   enableControls\n * />\n * ```\n */\nimport React, {\n  createContext,\n  ReactNode,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\"\nimport Image from \"next/image\"\nimport { Maximize, Minimize, Pause, Play, Volume2, VolumeX } from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Slider } from \"@/components/ui/slider\"\n\nconst hoverTransition = {\n  duration: 0.18,\n  ease: [0.2, 0, 0, 1],\n} as const\n\nconst hoverSpring = {\n  type: \"spring\",\n  duration: 0.3,\n  bounce: 0,\n} as const\n\n// Types\ninterface VideoPlayerState {\n  isHovering: boolean\n  isPlaying: boolean\n  isLoading: boolean\n  progress: number\n  muted: boolean\n  volume: number\n  isPiP: boolean\n  isMobile: boolean\n  controlsVisible: boolean\n  showThumbnail: boolean\n  isInView: boolean\n}\n\ninterface HoverVideoPlayerProps {\n  videoSrc: string\n  thumbnailSrc?: string\n  hoverOverlay?: React.ReactNode\n  pausedOverlay?: React.ReactNode\n  loadingOverlay?: React.ReactNode\n  playbackStartDelay?: number\n  restartOnPaused?: boolean\n  unloadVideoOnPaused?: boolean\n  playbackRangeStart?: number\n  playbackRangeEnd?: number\n  muted?: boolean\n  loop?: boolean\n  preload?: \"auto\" | \"metadata\" | \"none\"\n  className?: string\n  style?: React.CSSProperties\n  onHoverStart?: () => void\n  onHoverEnd?: () => void\n  enableControls?: boolean\n  cropTop?: number\n  cropBottom?: number\n  isVimeo?: boolean\n}\n\ninterface HoverVideoPlayerContextType {\n  isPlaying: boolean\n  isHovering: boolean\n  isLoading: boolean\n  progress: number\n  volume: number\n  muted: boolean\n  isPiP: boolean\n  isMobile: boolean\n  controlsVisible: boolean\n  videoRef: React.RefObject<HTMLVideoElement | null>\n  togglePlay: () => void\n  toggleMute: () => void\n  togglePiP: () => void\n  setVolume: (value: number) => void\n  setProgress: (value: number) => void\n  seekProgress: (value: number) => void\n  setSeekProgressHandler: (\n    handler: ((value: number) => void | Promise<void>) | null\n  ) => void\n  setVideoLoading: (isLoading: boolean) => void\n  setVideoPlaying: (isPlaying: boolean) => void\n  cropTop: number\n  cropBottom: number\n  thumbnailSrc?: string\n}\n\ninterface VimeoPlayer {\n  destroy: () => void\n  ready: () => Promise<void>\n  setVolume: (volume: number) => Promise<void>\n  getDuration: () => Promise<number>\n  setCurrentTime: (seconds: number) => Promise<number>\n  play: () => Promise<void>\n  pause: () => Promise<void>\n  on: (event: string, callback: (...args: any[]) => void) => void\n  off: (event: string, callback: (...args: any[]) => void) => void\n}\n\ninterface VimeoConstructor {\n  Player: {\n    new (element: HTMLElement, options: any): VimeoPlayer\n  }\n}\n\n// Context\nconst HoverVideoPlayerContext =\n  createContext<HoverVideoPlayerContextType | null>(null)\n\n/**\n * Custom hook to access HoverVideoPlayer context\n * @throws {Error} If used outside of HoverVideoPlayerContext\n */\nconst useHoverVideoPlayer = () => {\n  const context = useContext(HoverVideoPlayerContext)\n  if (!context) {\n    throw new Error(\n      \"useHoverVideoPlayer must be used within a HoverVideoPlayer\"\n    )\n  }\n  return context\n}\n\n// Add this helper function before the HoverVideoPlayer component\nfunction debounce<T extends (...args: any[]) => any>(\n  func: T,\n  wait: number\n): {\n  (...args: Parameters<T>): void\n  cancel: () => void\n} {\n  let timeoutId: NodeJS.Timeout | null = null\n\n  const debouncedFn = (...args: Parameters<T>) => {\n    if (timeoutId) clearTimeout(timeoutId)\n    timeoutId = setTimeout(() => func(...args), wait)\n  }\n\n  debouncedFn.cancel = () => {\n    if (timeoutId) {\n      clearTimeout(timeoutId)\n      timeoutId = null\n    }\n  }\n\n  return debouncedFn\n}\n\n// Add this helper function\nfunction isVimeoUrl(url: string): boolean {\n  return url.includes(\"player.vimeo.com/video/\") || url.includes(\"vimeo.com/\")\n}\n\n// Add this helper function\nfunction loadVimeoSDK(): Promise<VimeoConstructor> {\n  return new Promise((resolve, reject) => {\n    if ((window as any).Vimeo) {\n      resolve((window as any).Vimeo)\n      return\n    }\n\n    const script = document.createElement(\"script\")\n    script.src = \"https://player.vimeo.com/api/player.js\"\n    script.async = true\n    script.onload = () => resolve((window as any).Vimeo)\n    script.onerror = reject\n    document.body.appendChild(script)\n  })\n}\n\n// Main Component\nconst HoverVideoPlayer: React.FC<HoverVideoPlayerProps> = ({\n  videoSrc,\n  thumbnailSrc,\n  hoverOverlay,\n  pausedOverlay,\n  loadingOverlay,\n  playbackStartDelay = 0,\n  restartOnPaused = false,\n  unloadVideoOnPaused = false,\n  playbackRangeStart,\n  playbackRangeEnd,\n  muted: initialMuted = false,\n  loop = true,\n  preload = \"metadata\",\n  className,\n  style,\n  onHoverStart,\n  onHoverEnd,\n  enableControls = false,\n  cropTop = 0,\n  cropBottom = 0,\n  isVimeo = false,\n}) => {\n  const isVimeoVideo = isVimeo || isVimeoUrl(videoSrc)\n\n  // Refs for DOM elements and timing\n  const containerRef = useRef<HTMLDivElement>(null)\n  const videoRef = useRef<HTMLVideoElement>(null)\n  const playbackTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n  const lastPlayAttemptRef = useRef<number>(0)\n  const seekProgressHandlerRef = useRef<\n    ((value: number) => void | Promise<void>) | null\n  >(null)\n\n  // Consolidated state management\n  const [state, setState] = useState<VideoPlayerState>({\n    isHovering: false,\n    isPlaying: false,\n    isLoading: false,\n    progress: 0,\n    muted: initialMuted,\n    volume: 1,\n    isPiP: false,\n    isMobile: false,\n    controlsVisible: false,\n    showThumbnail: true,\n    isInView: false,\n  })\n\n  // Mobile detection\n  const checkMobile = useMemo(\n    () =>\n      debounce(() => {\n        setState((prev) => ({ ...prev, isMobile: window.innerWidth <= 768 }))\n      }, 200),\n    []\n  )\n\n  // Event Handlers\n  const handleTouchStart = useCallback(() => {\n    if (state.isMobile) {\n      setState((prev) => ({ ...prev, controlsVisible: !prev.controlsVisible }))\n    }\n  }, [state.isMobile])\n\n  const handleHoverStart = useCallback(() => {\n    if (!state.isMobile) {\n      console.log(\"Hover start\")\n      setState((prev) => ({ ...prev, isHovering: true }))\n      onHoverStart?.()\n    }\n  }, [state.isMobile, onHoverStart])\n\n  const handleHoverEnd = useCallback(() => {\n    if (!state.isMobile) {\n      console.log(\"Hover end\")\n      setState((prev) => ({ ...prev, isHovering: false }))\n      onHoverEnd?.()\n    }\n  }, [state.isMobile, onHoverEnd])\n\n  // Video Playback Controls\n  const playVideo = useCallback(() => {\n    if (!videoRef.current || !state.isInView) {\n      console.log(\"PlayVideo blocked:\", {\n        hasVideo: !!videoRef.current,\n        isInView: state.isInView,\n      })\n      return\n    }\n\n    const video = videoRef.current\n    console.log(\"Attempting to play video:\", {\n      readyState: video.readyState,\n      paused: video.paused,\n      currentSrc: video.currentSrc,\n    })\n\n    // Reset loading state when attempting to play\n    setState((prev) => ({ ...prev, isLoading: true }))\n\n    // Ensure video is ready to play\n    const attemptPlay = () => {\n      video\n        .play()\n        .then(() => {\n          console.log(\"Video play success\")\n          setState((prev) => ({\n            ...prev,\n            isLoading: false,\n            isPlaying: true,\n            showThumbnail: false,\n          }))\n        })\n        .catch((error) => {\n          console.log(\"Video play error:\", error.name)\n          if (error.name === \"NotAllowedError\") {\n            // User interaction required - show thumbnail\n            setState((prev) => ({\n              ...prev,\n              isLoading: false,\n              showThumbnail: true,\n            }))\n          } else if (error.name !== \"AbortError\") {\n            console.error(\"HoverVideoPlayer: Playback error:\", error)\n            setState((prev) => ({\n              ...prev,\n              isLoading: false,\n              showThumbnail: true,\n            }))\n          }\n        })\n    }\n\n    if (video.readyState >= 3) {\n      console.log(\"Video ready, attempting immediate play\")\n      attemptPlay()\n    } else {\n      console.log(\"Video not ready, waiting for canplay event\")\n      const handleCanPlay = () => {\n        console.log(\"Canplay event fired\")\n        video.removeEventListener(\"canplay\", handleCanPlay)\n        attemptPlay()\n      }\n      video.addEventListener(\"canplay\", handleCanPlay)\n    }\n  }, [state.isInView])\n\n  const pauseVideo = useCallback(() => {\n    if (!videoRef.current) return\n\n    videoRef.current.pause()\n    setState((prev) => ({\n      ...prev,\n      isPlaying: false,\n      showThumbnail: true,\n    }))\n\n    if (restartOnPaused) {\n      videoRef.current.currentTime = playbackRangeStart || 0\n    }\n  }, [restartOnPaused, playbackRangeStart])\n\n  // Effects\n\n  // Mobile detection effect\n  useEffect(() => {\n    checkMobile()\n    window.addEventListener(\"resize\", checkMobile)\n    return () => {\n      window.removeEventListener(\"resize\", checkMobile)\n      checkMobile.cancel()\n    }\n  }, [checkMobile])\n\n  // Intersection Observer effect\n  useEffect(() => {\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          const isNowInView = entry.isIntersecting\n          setState((prev) => ({ ...prev, isInView: isNowInView }))\n\n          if (!isNowInView) {\n            pauseVideo()\n          }\n        })\n      },\n      {\n        root: null,\n        rootMargin: \"50px\",\n        threshold: 0.1,\n      }\n    )\n\n    if (containerRef.current) {\n      observer.observe(containerRef.current)\n    }\n\n    return () => observer.disconnect()\n  }, [pauseVideo])\n\n  // Video event handlers effect\n  useEffect(() => {\n    const video = videoRef.current\n    if (!video || !state.isInView) return\n\n    const handlers = {\n      loadstart: () => {\n        console.log(\"Video loadstart\")\n        setState((prev) => ({ ...prev, isLoading: true }))\n      },\n      loadeddata: () => {\n        console.log(\"Video loadeddata\", {\n          readyState: video.readyState,\n          duration: video.duration,\n          paused: video.paused,\n          isHovering: state.isHovering,\n          controlsVisible: state.controlsVisible,\n        })\n        setState((prev) => ({ ...prev, isLoading: false }))\n        // Only attempt to play if we're hovering or controls are visible\n        if (state.isHovering || state.controlsVisible) {\n          console.log(\"Video loaded and hover/controls active, attempting play\")\n          playVideo()\n        }\n      },\n      timeupdate: () => {\n        if (!Number.isFinite(video.duration) || video.duration <= 0) return\n\n        const nextProgress = (video.currentTime / video.duration) * 100\n        setState((prev) => ({ ...prev, progress: nextProgress }))\n      },\n      canplay: () => {\n        console.log(\"Video canplay event\")\n      },\n      playing: () => {\n        console.log(\"Video playing\")\n        setState((prev) => ({\n          ...prev,\n          isLoading: false,\n          isPlaying: true,\n          showThumbnail: false,\n        }))\n      },\n      pause: () => {\n        console.log(\"Video paused\")\n        setState((prev) => ({\n          ...prev,\n          isPlaying: false,\n          showThumbnail: true,\n        }))\n      },\n      error: (e: Event) => {\n        const videoError = (e.target as HTMLVideoElement).error\n        console.error(\"Video error:\", {\n          code: videoError?.code,\n          message: videoError?.message,\n          currentSrc: video.currentSrc,\n        })\n        setState((prev) => ({\n          ...prev,\n          isLoading: false,\n          showThumbnail: true,\n        }))\n      },\n    }\n\n    // Add event listeners\n    Object.entries(handlers).forEach(([event, handler]) => {\n      video.addEventListener(event, handler)\n    })\n\n    // Set initial source and load the video\n    if (!video.src) {\n      console.log(\"Setting video source:\", videoSrc)\n      video.src = videoSrc\n      video.load()\n    }\n\n    return () => {\n      Object.entries(handlers).forEach(([event, handler]) => {\n        video.removeEventListener(event, handler)\n      })\n    }\n  }, [\n    state.isInView,\n    state.isHovering,\n    state.controlsVisible,\n    playVideo,\n    videoSrc,\n  ])\n\n  // Playback control effect\n  useEffect(() => {\n    console.log(\"Playback control effect:\", {\n      isInView: state.isInView,\n      isMobile: state.isMobile,\n      controlsVisible: state.controlsVisible,\n      isHovering: state.isHovering,\n    })\n\n    if (!state.isInView) {\n      console.log(\"Not in view, skipping playback\")\n      return\n    }\n\n    if (isVimeoVideo) {\n      return\n    }\n\n    let playbackTimeout: NodeJS.Timeout | undefined\n\n    if (state.isMobile) {\n      if (state.controlsVisible) {\n        console.log(\"Mobile controls visible, playing\")\n        playVideo()\n      } else {\n        console.log(\"Mobile controls hidden, pausing\")\n        pauseVideo()\n      }\n    } else if (state.isHovering) {\n      console.log(\n        \"Hovering, scheduling playback with delay:\",\n        playbackStartDelay\n      )\n      playbackTimeout = setTimeout(() => {\n        console.log(\"Playback delay completed, attempting to play\")\n        playVideo()\n      }, playbackStartDelay)\n    } else {\n      console.log(\"Not hovering, pausing\")\n      pauseVideo()\n    }\n\n    return () => {\n      if (playbackTimeout) {\n        console.log(\"Clearing playback timeout\")\n        clearTimeout(playbackTimeout)\n      }\n    }\n  }, [\n    state.isInView,\n    state.isMobile,\n    state.controlsVisible,\n    state.isHovering,\n    playVideo,\n    pauseVideo,\n    playbackStartDelay,\n    isVimeoVideo,\n  ])\n\n  // Volume effect\n  useEffect(() => {\n    const video = videoRef.current\n    if (!video) return\n\n    video.muted = state.muted\n    video.volume = state.volume\n  }, [state.muted, state.volume])\n\n  // Control handlers\n  const togglePlayPause = useCallback(() => {\n    if (state.isPlaying) {\n      pauseVideo()\n    } else {\n      playVideo()\n    }\n  }, [state.isPlaying, pauseVideo, playVideo])\n\n  const toggleMute = useCallback(() => {\n    setState((prev) => ({ ...prev, muted: !prev.muted }))\n  }, [])\n\n  const togglePiP = useCallback(async () => {\n    if (!document.pictureInPictureEnabled) return\n\n    try {\n      if (document.pictureInPictureElement) {\n        await document.exitPictureInPicture()\n        setState((prev) => ({ ...prev, isPiP: false }))\n      } else if (videoRef.current) {\n        await videoRef.current.requestPictureInPicture()\n        setState((prev) => ({ ...prev, isPiP: true }))\n      }\n    } catch (error) {\n      console.error(\"PiP error:\", error)\n    }\n  }, [])\n\n  const setSeekProgressHandler = useCallback(\n    (handler: ((value: number) => void | Promise<void>) | null) => {\n      seekProgressHandlerRef.current = handler\n    },\n    []\n  )\n\n  const seekProgress = useCallback((value: number) => {\n    const nextProgress = Math.min(100, Math.max(0, value))\n    setState((prev) => ({ ...prev, progress: nextProgress }))\n\n    if (seekProgressHandlerRef.current) {\n      void seekProgressHandlerRef.current(nextProgress)\n      return\n    }\n\n    const video = videoRef.current\n    if (!video || !Number.isFinite(video.duration) || video.duration <= 0) {\n      return\n    }\n\n    video.currentTime = (nextProgress / 100) * video.duration\n  }, [])\n\n  const setProgress = useCallback((value: number) => {\n    setState((prev) => ({ ...prev, progress: value }))\n  }, [])\n\n  const setVolume = useCallback((value: number) => {\n    setState((prev) => ({ ...prev, volume: value }))\n  }, [])\n\n  const setVideoLoading = useCallback((isLoading: boolean) => {\n    setState((prev) => ({ ...prev, isLoading }))\n  }, [])\n\n  const setVideoPlaying = useCallback((isPlaying: boolean) => {\n    setState((prev) => ({\n      ...prev,\n      isPlaying,\n      showThumbnail: !isPlaying,\n    }))\n  }, [])\n\n  // Context value\n  const contextValue = useMemo<HoverVideoPlayerContextType>(\n    () => ({\n      isPlaying: state.isPlaying,\n      isHovering: state.isHovering,\n      isLoading: state.isLoading,\n      progress: state.progress,\n      volume: state.volume,\n      muted: state.muted,\n      isPiP: state.isPiP,\n      isMobile: state.isMobile,\n      controlsVisible: state.controlsVisible,\n      videoRef,\n      togglePlay: togglePlayPause,\n      toggleMute,\n      togglePiP,\n      setVolume,\n      setProgress,\n      seekProgress,\n      setSeekProgressHandler,\n      setVideoLoading,\n      setVideoPlaying,\n      cropTop,\n      cropBottom,\n      thumbnailSrc,\n    }),\n    [\n      state,\n      togglePlayPause,\n      toggleMute,\n      togglePiP,\n      setVolume,\n      setProgress,\n      seekProgress,\n      setSeekProgressHandler,\n      setVideoLoading,\n      setVideoPlaying,\n      cropTop,\n      cropBottom,\n      thumbnailSrc,\n    ]\n  )\n\n  // Render\n  return (\n    <HoverVideoPlayerContext.Provider value={contextValue}>\n      <motion.div\n        ref={containerRef}\n        className={cn(\n          \"group relative cursor-pointer overflow-hidden\",\n          className\n        )}\n        style={{\n          ...style,\n          paddingTop: `${cropTop}%`,\n          paddingBottom: `${cropBottom}%`,\n        }}\n        onHoverStart={handleHoverStart}\n        onHoverEnd={handleHoverEnd}\n        onTouchStart={handleTouchStart}\n      >\n        {/* Video Element */}\n        {state.isInView && (\n          <HoverVideoPlayerVideo\n            src={videoSrc}\n            unloadVideoOnPaused={unloadVideoOnPaused}\n            loop={loop}\n            preload={preload}\n          />\n        )}\n\n        {/* Thumbnail */}\n        {thumbnailSrc && <HoverVideoPlayerThumbnail src={thumbnailSrc} />}\n\n        {/* Overlays */}\n        {pausedOverlay && (\n          <HoverVideoPlayerPausedOverlay>\n            {pausedOverlay}\n          </HoverVideoPlayerPausedOverlay>\n        )}\n        {loadingOverlay && (\n          <HoverVideoPlayerLoadingOverlay>\n            {loadingOverlay}\n          </HoverVideoPlayerLoadingOverlay>\n        )}\n        {hoverOverlay && !state.isMobile && (\n          <HoverVideoPlayerHoverOverlay>\n            {hoverOverlay}\n          </HoverVideoPlayerHoverOverlay>\n        )}\n\n        {/* Controls */}\n        {enableControls && (\n          <HoverVideoPlayerControls>\n            <div className=\"flex items-center space-x-2\">\n              <HoverVideoPlayerPlayPauseButton />\n              <HoverVideoPlayerVolumeControl />\n              <HoverVideoPlayerPiPButton />\n            </div>\n            <HoverVideoPlayerProgressBar />\n          </HoverVideoPlayerControls>\n        )}\n      </motion.div>\n    </HoverVideoPlayerContext.Provider>\n  )\n}\n\n/**\n * Video component that handles the actual video element\n */\nconst HoverVideoPlayerVideo: React.FC<{\n  src: string\n  unloadVideoOnPaused: boolean\n  loop: boolean\n  preload: string\n}> = ({ src, loop, preload }) => {\n  const {\n    videoRef,\n    muted,\n    cropTop,\n    cropBottom,\n    isHovering,\n    isMobile,\n    controlsVisible,\n    setProgress,\n    setSeekProgressHandler,\n    setVideoLoading,\n    setVideoPlaying,\n  } = useHoverVideoPlayer()\n  const isVimeoVideo = isVimeoUrl(src)\n  const containerRef = useRef<HTMLDivElement>(null)\n  const playerRef = useRef<VimeoPlayer | null>(null)\n  const shouldPlayVimeo = isMobile ? controlsVisible : isHovering\n  const shouldPlayVimeoRef = useRef(shouldPlayVimeo)\n\n  useEffect(() => {\n    shouldPlayVimeoRef.current = shouldPlayVimeo\n  }, [shouldPlayVimeo])\n\n  useEffect(() => {\n    if (!isVimeoVideo || !containerRef.current) return\n\n    const videoId = src.split(\"/\").pop() || \"\"\n    let player: VimeoPlayer | null = null\n    setVideoLoading(true)\n\n    loadVimeoSDK()\n      .then((Vimeo) => {\n        if (!containerRef.current) return\n\n        player = new Vimeo.Player(containerRef.current, {\n          id: videoId,\n          autopause: false,\n          muted,\n          loop,\n          responsive: true,\n          controls: false,\n          autoplay: false,\n          volume: 1,\n        })\n\n        playerRef.current = player\n        setSeekProgressHandler(async (value) => {\n          const duration = await player?.getDuration()\n          if (!duration || !Number.isFinite(duration)) return\n\n          await player?.setCurrentTime((value / 100) * duration)\n        })\n\n        player.ready().then(() => {\n          console.log(\"Vimeo video loaded\")\n          setVideoLoading(false)\n\n          if (shouldPlayVimeoRef.current) {\n            player\n              ?.play()\n              .then(() => setVideoPlaying(true))\n              .catch((error) => {\n                setVideoPlaying(false)\n                console.error(\"Vimeo play error:\", error)\n              })\n          }\n        })\n\n        player.on(\"play\", () => {\n          console.log(\"Vimeo video playing\")\n          setVideoLoading(false)\n          setVideoPlaying(true)\n        })\n        player.on(\"pause\", () => {\n          console.log(\"Vimeo video paused\")\n          setVideoPlaying(false)\n        })\n        player.on(\"timeupdate\", (data) => {\n          if (!data.duration || !Number.isFinite(data.duration)) return\n\n          setProgress((data.seconds / data.duration) * 100)\n        })\n        player.on(\"error\", (err) => {\n          setVideoLoading(false)\n          setVideoPlaying(false)\n          console.error(\"Vimeo player error:\", err)\n        })\n      })\n      .catch((error) => {\n        setVideoLoading(false)\n        console.error(\"Failed to load Vimeo SDK:\", error)\n      })\n\n    return () => {\n      if (player) {\n        player.destroy()\n        playerRef.current = null\n      }\n      setSeekProgressHandler(null)\n    }\n  }, [\n    isVimeoVideo,\n    src,\n    muted,\n    loop,\n    setProgress,\n    setSeekProgressHandler,\n    setVideoLoading,\n    setVideoPlaying,\n  ])\n\n  useEffect(() => {\n    if (!isVimeoVideo) return\n\n    const player = playerRef.current\n    if (!player) return\n\n    if (shouldPlayVimeo) {\n      setVideoLoading(true)\n      player\n        .play()\n        .then(() => {\n          setVideoLoading(false)\n          setVideoPlaying(true)\n        })\n        .catch((error) => {\n          setVideoLoading(false)\n          setVideoPlaying(false)\n          console.error(\"Vimeo play error:\", error)\n        })\n    } else {\n      player\n        .pause()\n        .then(() => setVideoPlaying(false))\n        .catch((error) => {\n          console.error(\"Vimeo pause error:\", error)\n        })\n    }\n  }, [isVimeoVideo, shouldPlayVimeo, setVideoLoading, setVideoPlaying])\n\n  if (isVimeoVideo) {\n    return <div ref={containerRef} className=\"absolute inset-0\" />\n  }\n\n  return (\n    <video\n      ref={videoRef}\n      className={cn(\"absolute inset-0 h-full w-full object-cover\")}\n      style={{\n        top: `-${cropTop}%`,\n        bottom: `-${cropBottom}%`,\n        height: `calc(100% + ${cropTop + cropBottom}%)`,\n      }}\n      muted={muted}\n      loop={loop}\n      preload={preload}\n      playsInline\n      crossOrigin=\"anonymous\"\n      aria-label=\"Video player\"\n    />\n  )\n}\n\n/**\n * Thumbnail component shown when video is not playing\n */\nconst HoverVideoPlayerThumbnail: React.FC<{ src: string }> = ({ src }) => {\n  const { cropTop, cropBottom, isLoading, isPlaying } = useHoverVideoPlayer()\n  const shouldReduceMotion = useReducedMotion()\n  const shouldShowThumbnail = !isPlaying || isLoading\n\n  return (\n    <motion.div\n      className={cn(\n        \"pointer-events-none absolute inset-0 z-1 h-full w-full transform-gpu\"\n      )}\n      style={{\n        top: `-${cropTop}%`,\n        bottom: `-${cropBottom}%`,\n        height: `calc(100% + ${cropTop + cropBottom}%)`,\n      }}\n      animate={{\n        opacity: shouldShowThumbnail ? 1 : 0,\n        scale: shouldShowThumbnail ? 1 : 1.01,\n      }}\n      initial={false}\n      transition={shouldReduceMotion ? { duration: 0 } : hoverTransition}\n    >\n      <Image\n        src={src}\n        alt=\"Video thumbnail\"\n        fill\n        sizes=\"(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw\"\n        className=\"object-cover\"\n        priority\n      />\n    </motion.div>\n  )\n}\n\n/**\n * Controls overlay with play/pause, volume, and progress bar\n */\nconst HoverVideoPlayerControls: React.FC<{ children: React.ReactNode }> = ({\n  children,\n}) => {\n  const { isHovering, isMobile, controlsVisible } = useHoverVideoPlayer()\n  const shouldReduceMotion = useReducedMotion()\n\n  const shouldShowControls = isMobile ? controlsVisible : isHovering\n\n  return (\n    <AnimatePresence initial={false}>\n      {shouldShowControls && (\n        <motion.div\n          className=\"absolute right-0 bottom-0 left-0 z-40 bg-linear-to-t from-black to-transparent p-4\"\n          initial={\n            shouldReduceMotion ? { opacity: 1, y: 0 } : { opacity: 0, y: 12 }\n          }\n          animate={{ opacity: 1, y: 0 }}\n          exit={\n            shouldReduceMotion ? { opacity: 0, y: 0 } : { opacity: 0, y: 8 }\n          }\n          transition={shouldReduceMotion ? { duration: 0 } : hoverSpring}\n        >\n          {children}\n        </motion.div>\n      )}\n    </AnimatePresence>\n  )\n}\n\n/**\n * Overlay shown when hovering over the video\n */\nconst HoverVideoPlayerHoverOverlay: React.FC<{ children: React.ReactNode }> = ({\n  children,\n}) => {\n  const { isHovering, isLoading } = useHoverVideoPlayer()\n  const shouldReduceMotion = useReducedMotion()\n\n  return (\n    <AnimatePresence initial={false}>\n      {isHovering && !isLoading && (\n        <motion.div\n          className=\"absolute inset-0 z-30 flex items-center justify-center\"\n          initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0 }}\n          animate={{ opacity: 1 }}\n          exit={{ opacity: 0 }}\n          transition={shouldReduceMotion ? { duration: 0 } : hoverTransition}\n        >\n          {children}\n        </motion.div>\n      )}\n    </AnimatePresence>\n  )\n}\n\n/**\n * Overlay shown when video is paused\n */\nconst HoverVideoPlayerPausedOverlay: React.FC<{\n  children: React.ReactNode\n}> = ({ children }) => {\n  const { isPlaying, isLoading, isHovering } = useHoverVideoPlayer()\n  const shouldReduceMotion = useReducedMotion()\n\n  return (\n    <AnimatePresence initial={false}>\n      {!isPlaying && !isLoading && !isHovering && (\n        <motion.div\n          className=\"bg-opacity-50 absolute inset-0 z-10 flex items-center justify-center bg-black\"\n          initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0 }}\n          animate={{ opacity: 1 }}\n          exit={{ opacity: 0 }}\n          transition={shouldReduceMotion ? { duration: 0 } : hoverTransition}\n        >\n          {children}\n        </motion.div>\n      )}\n    </AnimatePresence>\n  )\n}\n\n/**\n * Overlay shown while video is loading\n */\nconst HoverVideoPlayerLoadingOverlay: React.FC<{\n  children: React.ReactNode\n}> = ({ children }) => {\n  const { isLoading } = useHoverVideoPlayer()\n  const shouldReduceMotion = useReducedMotion()\n\n  return (\n    <AnimatePresence initial={false} mode=\"wait\">\n      {isLoading && (\n        <motion.div\n          className=\"bg-opacity-50 absolute inset-0 z-20 flex items-center justify-center bg-black\"\n          initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0 }}\n          animate={{ opacity: 1 }}\n          exit={{ opacity: 0 }}\n          transition={shouldReduceMotion ? { duration: 0 } : hoverTransition}\n        >\n          {children}\n        </motion.div>\n      )}\n    </AnimatePresence>\n  )\n}\n\n/**\n * Play/Pause button component\n */\nconst HoverVideoPlayerPlayPauseButton: React.FC = () => {\n  const { isPlaying, togglePlay } = useHoverVideoPlayer()\n\n  return (\n    <Button\n      size=\"icon\"\n      variant=\"ghost\"\n      onClick={togglePlay}\n      className=\"hover:text-primary-foreground text-white\"\n      aria-label={isPlaying ? \"Pause video\" : \"Play video\"}\n    >\n      {isPlaying ? <Pause className=\"h-6 w-6\" /> : <Play className=\"h-6 w-6\" />}\n    </Button>\n  )\n}\n\n/**\n * Volume control component with mute toggle and volume slider\n */\nconst HoverVideoPlayerVolumeControl: React.FC = () => {\n  const { muted, toggleMute, volume, setVolume } = useHoverVideoPlayer()\n\n  return (\n    <div className=\"flex items-center space-x-2\">\n      <Button\n        size=\"icon\"\n        variant=\"ghost\"\n        onClick={toggleMute}\n        className=\"hover:text-primary-foreground text-white\"\n        aria-label={muted ? \"Unmute\" : \"Mute\"}\n      >\n        {muted ? (\n          <VolumeX className=\"h-6 w-6\" />\n        ) : (\n          <Volume2 className=\"h-6 w-6\" />\n        )}\n      </Button>\n      <Slider\n        className=\"w-24\"\n        value={[muted ? 0 : volume * 100]}\n        min={0}\n        max={100}\n        step={1}\n        onValueChange={(value) => setVolume(value[0] / 100)}\n        aria-label=\"Volume\"\n      />\n    </div>\n  )\n}\n\n/**\n * Progress bar component for video timeline\n */\nconst HoverVideoPlayerProgressBar: React.FC = () => {\n  const { progress, seekProgress } = useHoverVideoPlayer()\n\n  return (\n    <Slider\n      className=\"mt-2\"\n      value={[progress]}\n      min={0}\n      max={100}\n      step={0.1}\n      onValueChange={(value) => {\n        seekProgress(value[0])\n      }}\n      aria-label=\"Video progress\"\n    />\n  )\n}\n\n/**\n * Picture-in-Picture toggle button\n */\nconst HoverVideoPlayerPiPButton: React.FC = () => {\n  const { isPiP, togglePiP } = useHoverVideoPlayer()\n\n  return (\n    <Button\n      size=\"icon\"\n      variant=\"ghost\"\n      onClick={togglePiP}\n      className=\"hover:text-primary-foreground text-white\"\n      aria-label={\n        isPiP ? \"Exit picture in picture\" : \"Enter picture in picture\"\n      }\n    >\n      {isPiP ? (\n        <Minimize className=\"h-6 w-6\" />\n      ) : (\n        <Maximize className=\"h-6 w-6\" />\n      )}\n    </Button>\n  )\n}\n\n/**\n * Wrapper component for maintaining aspect ratio with padding\n */\nconst HoverVideoPaddingWrapper: React.FC<{\n  width: number\n  height: number\n  className?: string\n  children?: ReactNode\n}> = ({ width, height, className, children }) => {\n  return (\n    <div\n      style={{ \"--width\": width, \"--height\": height } as React.CSSProperties}\n      className={cn(\n        className,\n        \"relative aspect-[var(--width)/var(--height)] [--radius:theme(borderRadius.xl)]\"\n      )}\n    >\n      <div className=\"absolute -inset-[var(--padding)] rounded-[calc(var(--radius)+var(--padding))] shadow-sm ring-1 ring-black/5 [--padding:theme(spacing.2)]\" />\n      {children}\n    </div>\n  )\n}\n\n// Add display names for better debugging\nHoverVideoPlayerControls.displayName = \"HoverVideoPlayerControls\"\nHoverVideoPaddingWrapper.displayName = \"HoverVideoPaddingWrapper\"\nHoverVideoPlayerHoverOverlay.displayName = \"HoverVideoPlayerHoverOverlay\"\nHoverVideoPlayerPausedOverlay.displayName = \"HoverVideoPlayerPausedOverlay\"\nHoverVideoPlayerLoadingOverlay.displayName = \"HoverVideoPlayerLoadingOverlay\"\nHoverVideoPlayerPlayPauseButton.displayName = \"HoverVideoPlayerPlayPauseButton\"\nHoverVideoPlayerVolumeControl.displayName = \"HoverVideoPlayerVolumeControl\"\nHoverVideoPlayerProgressBar.displayName = \"HoverVideoPlayerProgressBar\"\nHoverVideoPlayerPiPButton.displayName = \"HoverVideoPlayerPiPButton\"\n\nexport {\n  HoverVideoPlayer,\n  HoverVideoPlayerControls,\n  HoverVideoPlayerHoverOverlay,\n  HoverVideoPlayerPausedOverlay,\n  HoverVideoPlayerLoadingOverlay,\n  HoverVideoPlayerPlayPauseButton,\n  HoverVideoPlayerVolumeControl,\n  HoverVideoPlayerProgressBar,\n  HoverVideoPlayerPiPButton,\n  HoverVideoPaddingWrapper,\n}\n\nexport default HoverVideoPlayer\n",
      "type": "registry:ui"
    }
  ]
}