{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "carousel",
  "title": "Carousel",
  "description": "Slideshow with bar indicators, keyboard navigation and an accessible pause control.",
  "dependencies": ["class-variance-authority", "lucide-react"],
  "registryDependencies": [
    "https://indiacn.in/r/theme.json",
    "https://indiacn.in/r/typography.json"
  ],
  "files": [
    {
      "path": "components/ui/carousel.tsx",
      "content": "'use client';\n\nimport { cva } from 'class-variance-authority';\nimport { ChevronLeft, ChevronRight, Pause, Play } from 'lucide-react';\nimport {\n  ComponentProps,\n  ReactNode,\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useMemo,\n  useState,\n} from 'react';\n\nimport { Body2, Label1, Label2 } from '@/components/ui/typography';\nimport { cn } from '@/lib/utils';\n\n/*\n * UX4G carousel: slides fill the frame, chevrons sit inside the left and right\n * edges, and the indicators are 30x3px bars — current colour at 50% when\n * inactive, full when current. `variant` follows the kit's Dark property and\n * describes the slide behind the controls, not the page theme.\n */\ntype TCarouselVariant = 'light' | 'dark';\n\ninterface ICarouselContext {\n  index: number;\n  count: number;\n  variant: TCarouselVariant;\n  baseId: string;\n  playing: boolean;\n  canAutoPlay: boolean;\n  goTo: (i: number) => void;\n  next: () => void;\n  previous: () => void;\n  togglePlaying: () => void;\n  register: (n: number) => void;\n}\n\nconst CAROUSEL_CONTEXT = createContext<ICarouselContext | null>(null);\n\n/** Reads the nearest Carousel context, or fails loudly rather than silently. */\nfunction useCarousel() {\n  const context = useContext(CAROUSEL_CONTEXT);\n  if (!context) throw new Error('Carousel parts must be rendered inside <Carousel>');\n  return context;\n}\n\nconst CONTROL_VARIANTS = cva(\n  'focus-visible:shadow-focus-primary absolute top-1/2 z-10 flex -translate-y-1/2 cursor-pointer items-center justify-center rounded-md p-2 transition-opacity focus-visible:outline-none disabled:pointer-events-none disabled:opacity-30',\n  {\n    variants: {\n      variant: {\n        light: 'text-neutral-600 hover:text-neutral',\n        dark: 'text-neutral-0/70 hover:text-neutral-0',\n      },\n    },\n    defaultVariants: { variant: 'light' },\n  },\n);\n\ninterface ICarouselProps extends Omit<ComponentProps<'section'>, 'onChange'> {\n  variant?: TCarouselVariant;\n  /** Milliseconds between slides. Omit for a manual carousel. */\n  autoPlayInterval?: number;\n  label?: string;\n}\n\n/**\n * Slideshow region.\n *\n * Auto-advance stops on hover, on focus, and whenever the visitor asks for\n * reduced motion, and CarouselPlayPause exposes the manual control WCAG 2.2.2\n * requires for anything that moves on its own.\n */\nfunction Carousel({\n  className,\n  children,\n  variant = 'light',\n  autoPlayInterval,\n  label = 'Carousel',\n  ...props\n}: ICarouselProps) {\n  const baseId = useId();\n  const [index, setIndex] = useState(0);\n  const [count, setCount] = useState(0);\n  const [playing, setPlaying] = useState(Boolean(autoPlayInterval));\n  const [paused, setPaused] = useState(false);\n  const [reduceMotion, setReduceMotion] = useState(false);\n\n  useEffect(() => {\n    const query = window.matchMedia('(prefers-reduced-motion: reduce)');\n    const sync = () => setReduceMotion(query.matches);\n    sync();\n    query.addEventListener('change', sync);\n    return () => query.removeEventListener('change', sync);\n  }, []);\n\n  const goTo = useCallback((i: number) => setIndex(i), []);\n  const next = useCallback(() => setIndex(i => (count ? (i + 1) % count : 0)), [count]);\n  const previous = useCallback(\n    () => setIndex(i => (count ? (i - 1 + count) % count : 0)),\n    [count],\n  );\n  const togglePlaying = useCallback(() => setPlaying(p => !p), []);\n  const register = useCallback((n: number) => setCount(n), []);\n\n  const canAutoPlay = Boolean(autoPlayInterval) && !reduceMotion;\n  const running = canAutoPlay && playing && !paused && count > 1;\n\n  useEffect(() => {\n    if (!running) return;\n    const id = setInterval(next, autoPlayInterval);\n    return () => clearInterval(id);\n  }, [running, next, autoPlayInterval]);\n\n  const value = useMemo(\n    () => ({\n      index,\n      count,\n      variant,\n      baseId,\n      playing,\n      canAutoPlay,\n      goTo,\n      next,\n      previous,\n      togglePlaying,\n      register,\n    }),\n    [index, count, variant, baseId, playing, canAutoPlay, goTo, next, previous, togglePlaying, register],\n  );\n\n  const handleKeyDown = useCallback(\n    (e: React.KeyboardEvent<HTMLElement>) => {\n      if (e.key === 'ArrowLeft') {\n        e.preventDefault();\n        previous();\n      } else if (e.key === 'ArrowRight') {\n        e.preventDefault();\n        next();\n      }\n    },\n    [next, previous],\n  );\n\n  const pause = useCallback(() => setPaused(true), []);\n  const resume = useCallback(() => setPaused(false), []);\n\n  return (\n    <CAROUSEL_CONTEXT.Provider value={value}>\n      <section\n        aria-roledescription='carousel'\n        aria-label={label}\n        className={cn('relative overflow-hidden rounded-md', className)}\n        onMouseEnter={pause}\n        onMouseLeave={resume}\n        onFocusCapture={pause}\n        onBlurCapture={resume}\n        onKeyDown={handleKeyDown}\n        {...props}\n      >\n        {children}\n      </section>\n    </CAROUSEL_CONTEXT.Provider>\n  );\n}\n\n/** Track holding the slides. Counts its children so the parts stay in step. */\nfunction CarouselContent({ className, children, ...props }: ComponentProps<'div'>) {\n  const { index, register } = useCarousel();\n  const slides = Array.isArray(children) ? children : [children];\n\n  useEffect(() => {\n    register(slides.length);\n  }, [register, slides.length]);\n\n  return (\n    <div\n      aria-live='polite'\n      className={cn('flex transition-transform duration-500 ease-in-out', className)}\n      style={{ transform: `translateX(-${index * 100}%)` }}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\n/** A single slide. */\nfunction CarouselItem({ className, ...props }: ComponentProps<'div'>) {\n  return (\n    <div\n      role='group'\n      aria-roledescription='slide'\n      className={cn('relative w-full shrink-0 grow-0 basis-full', className)}\n      {...props}\n    />\n  );\n}\n\n/** Caption block sitting over the lower part of a slide. */\nfunction CarouselCaption({\n  className,\n  title,\n  children,\n  ...props\n}: ComponentProps<'div'> & { title?: ReactNode }) {\n  const { variant } = useCarousel();\n\n  return (\n    <div\n      className={cn(\n        'absolute inset-x-0 bottom-12 px-12 text-center',\n        variant === 'dark' ? 'text-neutral-0' : 'text-neutral',\n        className,\n      )}\n      {...props}\n    >\n      {title && <Label1 className='block font-semibold'>{title}</Label1>}\n      {children && <Body2 className='mt-1'>{children}</Body2>}\n    </div>\n  );\n}\n\n/** Previous-slide control. */\nfunction CarouselPrevious({ className, ...props }: ComponentProps<'button'>) {\n  const { previous, variant, count } = useCarousel();\n\n  return (\n    <button\n      type='button'\n      onClick={previous}\n      disabled={count < 2}\n      aria-label='Previous slide'\n      className={cn(CONTROL_VARIANTS({ variant }), 'left-2', className)}\n      {...props}\n    >\n      <ChevronLeft className='size-8' aria-hidden />\n    </button>\n  );\n}\n\n/** Next-slide control. */\nfunction CarouselNext({ className, ...props }: ComponentProps<'button'>) {\n  const { next, variant, count } = useCarousel();\n\n  return (\n    <button\n      type='button'\n      onClick={next}\n      disabled={count < 2}\n      aria-label='Next slide'\n      className={cn(CONTROL_VARIANTS({ variant }), 'right-2', className)}\n      {...props}\n    >\n      <ChevronRight className='size-8' aria-hidden />\n    </button>\n  );\n}\n\n/** One indicator bar. Split out so its handler is not rebuilt on every render. */\nfunction CarouselIndicator({ slide }: { slide: number }) {\n  const { index, goTo, variant } = useCarousel();\n  const handleClick = useCallback(() => goTo(slide), [goTo, slide]);\n  const current = slide === index;\n\n  return (\n    <button\n      type='button'\n      onClick={handleClick}\n      aria-label={`Go to slide ${slide + 1}`}\n      aria-current={current || undefined}\n      className={cn(\n        'focus-visible:shadow-focus-primary h-3 w-[30px] cursor-pointer transition-opacity focus-visible:outline-none',\n        'before:block before:h-[3px] before:w-full before:translate-y-[4.5px] before:bg-current before:content-[\"\"]',\n        variant === 'dark' ? 'text-neutral-0' : 'text-neutral',\n        current ? 'opacity-100' : 'opacity-50 hover:opacity-75',\n      )}\n    />\n  );\n}\n\n/** Bar indicators. 30x3px, half opacity until current. */\nfunction CarouselIndicators({ className, ...props }: ComponentProps<'div'>) {\n  const { count } = useCarousel();\n\n  return (\n    <div\n      className={cn('absolute inset-x-0 bottom-4 z-10 flex justify-center gap-1.5', className)}\n      {...props}\n    >\n      {Array.from({ length: count }, (_, i) => (\n        <CarouselIndicator key={i} slide={i} />\n      ))}\n    </div>\n  );\n}\n\n/** Pause and resume control for an auto-advancing carousel. */\nfunction CarouselPlayPause({ className, ...props }: ComponentProps<'button'>) {\n  const { playing, togglePlaying, variant, canAutoPlay } = useCarousel();\n  if (!canAutoPlay) return null;\n\n  const Icon = playing ? Pause : Play;\n\n  return (\n    <button\n      type='button'\n      onClick={togglePlaying}\n      aria-label={playing ? 'Pause carousel' : 'Play carousel'}\n      className={cn(\n        'focus-visible:shadow-focus-primary absolute top-3 right-3 z-10 inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2.5 py-1.5 transition-opacity focus-visible:outline-none',\n        variant === 'dark'\n          ? 'text-neutral-0/70 hover:text-neutral-0 bg-neutral/40'\n          : 'text-neutral-600 hover:text-neutral bg-neutral-0/70',\n        className,\n      )}\n      {...props}\n    >\n      <Icon className='size-3.5' aria-hidden />\n      <Label2>{playing ? 'Pause' : 'Play'}</Label2>\n    </button>\n  );\n}\n\nexport type { TCarouselVariant };\n\nexport {\n  Carousel,\n  CarouselContent,\n  CarouselItem,\n  CarouselCaption,\n  CarouselPrevious,\n  CarouselNext,\n  CarouselIndicators,\n  CarouselPlayPause,\n  CONTROL_VARIANTS,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
