{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"marketing-gallery-immersive-gallery","type":"registry:block","title":"Immersive Gallery","files":[{"path":"components/blocks/marketing/gallery/immersive-gallery.tsx","content":"import * as React from 'react';\nimport { Button } from '@/components/ui/button';\nimport {\n  ArrowLeft,\n  ArrowRight,\n  Camera,\n  Calendar,\n  MapPin,\n  Info,\n  X,\n  ChevronUp,\n} from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from '@/components/ui/tooltip';\nimport { Badge } from '@/components/ui/badge';\n\ninterface SplitGalleryImage {\n  src: string;\n  alt: string;\n  width: number;\n  height: number;\n  title: string;\n  description: string;\n  photographer: string;\n  date: string;\n  location: string;\n  tags: string[];\n}\n\nexport default function SplitGallery() {\n  const [currentIndex, setCurrentIndex] = React.useState(0);\n  const [infoExpanded, setInfoExpanded] = React.useState(false);\n  const [fullscreen, setFullscreen] = React.useState(false);\n\n  // Static images array with detailed metadata\n  const images = React.useMemo<SplitGalleryImage[]>(\n    () => [\n      {\n        src: 'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?q=80&w=1474&auto=format&fit=crop',\n        alt: 'Mountain lake with snow-capped peaks in background',\n        width: 1474,\n        height: 983,\n        title: 'Alpine Serenity',\n        description:\n          'A pristine alpine lake reflects the majestic snow-capped mountains that surround it. The stillness of the water creates a perfect mirror image of the landscape, while the early morning light bathes everything in a golden glow.',\n        photographer: 'Thomas Raynor',\n        date: 'August 15, 2023',\n        location: 'Banff National Park, Canada',\n        tags: ['Nature', 'Landscape', 'Mountains', 'Water'],\n      },\n      {\n        src: 'https://images.unsplash.com/photo-1470770903676-69b98201ea1c?q=80&w=1470&auto=format&fit=crop',\n        alt: 'Sunset over mountain range with dramatic clouds',\n        width: 1470,\n        height: 980,\n        title: 'Twilight Mountains',\n        description:\n          'As the day comes to a close, the setting sun casts dramatic rays through the clouds, illuminating the mountain range with a spectrum of warm colors. The interplay of light and shadow emphasizes the rugged texture of the peaks.',\n        photographer: 'Emma Walters',\n        date: 'October 3, 2023',\n        location: 'Dolomites, Italy',\n        tags: ['Sunset', 'Mountains', 'Clouds', 'Dramatic'],\n      },\n      {\n        src: 'https://images.unsplash.com/photo-1542224566-6e85f2e6772f?q=80&w=1528&auto=format&fit=crop',\n        alt: 'Desert landscape with rock formations at sunrise',\n        width: 1528,\n        height: 1019,\n        title: 'Desert Dawn',\n        description:\n          'The first light of day breaks over ancient rock formations in the desert, creating long shadows and revealing the rich textures and colors of the arid landscape. The silence and vastness create a profound sense of solitude and timelessness.',\n        photographer: 'Hassan Khan',\n        date: 'March 21, 2023',\n        location: 'Wadi Rum, Jordan',\n        tags: ['Desert', 'Sunrise', 'Rock Formations', 'Landscape'],\n      },\n      {\n        src: 'https://images.unsplash.com/photo-1505765050516-f72dcac9c60e?q=80&w=1470&auto=format&fit=crop',\n        alt: 'Foggy forest with sunlight streaming through trees',\n        width: 1470,\n        height: 980,\n        title: 'Mystic Forest',\n        description:\n          'Morning fog weaves between ancient trees as rays of sunlight pierce through the canopy. The ethereal atmosphere transforms the forest into an enchanted realm where time seems to stand still and every sound is muffled by the mist.',\n        photographer: 'Lucia Fernandez',\n        date: 'November 12, 2023',\n        location: 'Olympic National Park, USA',\n        tags: ['Forest', 'Fog', 'Sunlight', 'Trees', 'Mystical'],\n      },\n      {\n        src: 'https://images.unsplash.com/photo-1534447677768-be436bb09401?q=80&w=1494&auto=format&fit=crop',\n        alt: 'Aerial view of turquoise ocean water meeting white sand beach',\n        width: 1494,\n        height: 996,\n        title: 'Coastal Paradise',\n        description:\n          'A birds-eye view reveals the stunning meeting point of crystal-clear turquoise waters and pristine white sand beaches. The natural patterns formed by ocean currents create a mesmerizing tapestry of colors and textures along the shoreline.',\n        photographer: 'Michael Chen',\n        date: 'January 8, 2023',\n        location: 'Whitsunday Islands, Australia',\n        tags: ['Aerial', 'Ocean', 'Beach', 'Coastal', 'Turquoise'],\n      },\n    ],\n    []\n  );\n\n  const nextImage = () => {\n    setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));\n    if (infoExpanded) setInfoExpanded(false);\n  };\n\n  const prevImage = () => {\n    setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1));\n    if (infoExpanded) setInfoExpanded(false);\n  };\n\n  // Keyboard navigation\n  React.useEffect(() => {\n    const handleKeyDown = (e: KeyboardEvent) => {\n      if (e.key === 'ArrowRight') {\n        nextImage();\n      } else if (e.key === 'ArrowLeft') {\n        prevImage();\n      } else if (e.key === 'Escape' && fullscreen) {\n        setFullscreen(false);\n      } else if (e.key === 'i') {\n        setInfoExpanded((prev) => !prev);\n      } else if (e.key === 'f') {\n        setFullscreen((prev) => !prev);\n      }\n    };\n\n    window.addEventListener('keydown', handleKeyDown);\n    return () => window.removeEventListener('keydown', handleKeyDown);\n  }, [fullscreen, infoExpanded]);\n\n  const currentImage = images[currentIndex]!;\n\n  return (\n    <div\n      className={cn(\n        'relative w-full overflow-hidden bg-black text-white',\n        fullscreen ? 'fixed inset-0 z-50' : 'h-screen max-h-[800px]'\n      )}\n    >\n      {/* Main gallery container */}\n      <div className=\"relative h-full w-full\">\n        {/* Image container */}\n        <div className=\"absolute inset-0 transition-opacity duration-500\">\n          <img\n            src={currentImage.src}\n            alt={currentImage.alt}\n            sizes=\"100vw\"\n            className=\"object-cover\"\n          />\n          {/* Gradient overlay for better text readability */}\n          <div className=\"absolute inset-0 bg-gradient-to-t from-black/70 via-transparent to-black/30\" />\n        </div>\n\n        {/* Header with title and navigation */}\n        <div className=\"absolute top-0 right-0 left-0 z-10 flex items-center justify-between p-4 md:p-6\">\n          <h2 className=\"animate-fade-in text-xl font-bold text-white md:text-3xl\">\n            {currentImage.title}\n          </h2>\n\n          <div className=\"flex items-center gap-2\">\n            <TooltipProvider>\n              <Tooltip>\n                <TooltipTrigger asChild>\n                  <Button\n                    variant=\"outline\"\n                    size=\"icon\"\n                    className=\"h-8 w-8 rounded-full border-white/20 bg-black/30 text-white backdrop-blur-sm hover:bg-black/50 md:h-10 md:w-10\"\n                    onClick={() => setInfoExpanded(!infoExpanded)}\n                  >\n                    <Info className=\"h-4 w-4 md:h-5 md:w-5\" />\n                  </Button>\n                </TooltipTrigger>\n                <TooltipContent side=\"bottom\">\n                  <p>Image details</p>\n                </TooltipContent>\n              </Tooltip>\n            </TooltipProvider>\n\n            {fullscreen && (\n              <TooltipProvider>\n                <Tooltip>\n                  <TooltipTrigger asChild>\n                    <Button\n                      variant=\"outline\"\n                      size=\"icon\"\n                      className=\"h-8 w-8 rounded-full border-white/20 bg-black/30 text-white backdrop-blur-sm hover:bg-black/50 md:h-10 md:w-10\"\n                      onClick={() => setFullscreen(false)}\n                    >\n                      <X className=\"h-4 w-4 md:h-5 md:w-5\" />\n                    </Button>\n                  </TooltipTrigger>\n                  <TooltipContent side=\"bottom\">\n                    <p>Exit fullscreen</p>\n                  </TooltipContent>\n                </Tooltip>\n              </TooltipProvider>\n            )}\n          </div>\n        </div>\n\n        {/* Navigation arrows */}\n        <div className=\"absolute top-1/2 right-0 left-0 z-10 flex -translate-y-1/2 justify-between px-4 md:px-6\">\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-10 w-10 rounded-full border-white/20 bg-black/30 text-white backdrop-blur-sm hover:bg-black/50 md:h-12 md:w-12\"\n            onClick={prevImage}\n            aria-label=\"Previous image\"\n          >\n            <ArrowLeft className=\"h-5 w-5 md:h-6 md:w-6\" />\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-10 w-10 rounded-full border-white/20 bg-black/30 text-white backdrop-blur-sm hover:bg-black/50 md:h-12 md:w-12\"\n            onClick={nextImage}\n            aria-label=\"Next image\"\n          >\n            <ArrowRight className=\"h-5 w-5 md:h-6 md:w-6\" />\n          </Button>\n        </div>\n\n        {/* Bottom info bar */}\n        <div\n          className={cn(\n            'absolute right-0 bottom-0 left-0 z-20 transform transition-transform duration-500',\n            infoExpanded ? 'translate-y-0' : 'translate-y-[calc(100%-60px)]'\n          )}\n        >\n          {/* Info toggle button */}\n          <button\n            onClick={() => setInfoExpanded(!infoExpanded)}\n            className=\"mx-auto flex h-10 w-full items-center justify-center bg-black/60 backdrop-blur-md\"\n          >\n            <ChevronUp\n              className={cn(\n                'h-5 w-5 transition-transform duration-300',\n                infoExpanded && 'rotate-180'\n              )}\n            />\n          </button>\n\n          {/* Expanded info panel */}\n          <div className=\"bg-black/60 p-5 backdrop-blur-md md:p-6\">\n            <div className=\"mb-4 grid gap-5 md:grid-cols-2\">\n              <div className=\"space-y-4\">\n                <div className=\"animate-fade-in text-sm md:text-base\">\n                  {currentImage.description}\n                </div>\n\n                <div className=\"flex flex-wrap gap-2\">\n                  {currentImage.tags.map((tag) => (\n                    <Badge\n                      key={tag}\n                      variant=\"secondary\"\n                      className=\"bg-white/10 hover:bg-white/20\"\n                    >\n                      {tag}\n                    </Badge>\n                  ))}\n                </div>\n              </div>\n\n              <div className=\"space-y-4\">\n                <div className=\"grid grid-cols-[repeat(auto-fit,minmax(150px,1fr))] gap-4\">\n                  <div className=\"space-y-1\">\n                    <div className=\"flex items-center gap-2\">\n                      <Camera className=\"h-4 w-4 text-gray-400\" />\n                      <span className=\"text-xs font-medium text-gray-400 uppercase\">\n                        Photographer\n                      </span>\n                    </div>\n                    <p className=\"text-sm\">{currentImage.photographer}</p>\n                  </div>\n\n                  <div className=\"space-y-1\">\n                    <div className=\"flex items-center gap-2\">\n                      <Calendar className=\"h-4 w-4 text-gray-400\" />\n                      <span className=\"text-xs font-medium text-gray-400 uppercase\">\n                        Date\n                      </span>\n                    </div>\n                    <p className=\"text-sm\">{currentImage.date}</p>\n                  </div>\n\n                  <div className=\"space-y-1\">\n                    <div className=\"flex items-center gap-2\">\n                      <MapPin className=\"h-4 w-4 text-gray-400\" />\n                      <span className=\"text-xs font-medium text-gray-400 uppercase\">\n                        Location\n                      </span>\n                    </div>\n                    <p className=\"text-sm\">{currentImage.location}</p>\n                  </div>\n                </div>\n\n                <div className=\"pt-2\">\n                  <Button\n                    onClick={() => setFullscreen(!fullscreen)}\n                    className=\"w-full sm:w-auto\"\n                    variant={fullscreen ? 'destructive' : 'default'}\n                  >\n                    {fullscreen ? 'Exit Fullscreen' : 'View Fullscreen'}\n                  </Button>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        {/* Bottom indicator bar */}\n        <div className=\"absolute bottom-[60px] left-0 z-10 flex w-full justify-center gap-1 p-4\">\n          {images.map((_, index) => (\n            <button\n              key={index}\n              onClick={() => {\n                setCurrentIndex(index);\n                if (infoExpanded) setInfoExpanded(false);\n              }}\n              className={cn(\n                'h-1 transition-all',\n                index === currentIndex\n                  ? 'w-6 bg-white'\n                  : 'w-3 bg-white/40 hover:bg-white/60'\n              )}\n              aria-label={`Go to image ${index + 1}`}\n            />\n          ))}\n        </div>\n      </div>\n\n      {/* Add animation classes */}\n      <style jsx global>{`\n        @keyframes fadeIn {\n          from {\n            opacity: 0;\n          }\n          to {\n            opacity: 1;\n          }\n        }\n\n        .animate-fade-in {\n          animation: fadeIn 0.5s ease-in-out;\n        }\n      `}</style>\n    </div>\n  );\n}\n","type":"registry:component","target":"components/blocks/marketing/gallery/immersive-gallery.tsx"},{"path":"components/ui/button.tsx","content":"import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  \"group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/80\",\n        outline:\n          \"border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\n        ghost:\n          \"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50\",\n        destructive:\n          \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default:\n          \"h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n        xs: \"h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\n        sm: \"h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5\",\n        lg: \"h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n        icon: \"size-9\",\n        \"icon-xs\":\n          \"size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3\",\n        \"icon-sm\":\n          \"size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md\",\n        \"icon-lg\": \"size-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nfunction Button({\n  className,\n  variant = \"default\",\n  size = \"default\",\n  asChild = false,\n  ...props\n}: React.ComponentProps<\"button\"> &\n  VariantProps<typeof buttonVariants> & {\n    asChild?: boolean\n  }) {\n  const Comp = asChild ? Slot.Root : \"button\"\n\n  return (\n    <Comp\n      data-slot=\"button\"\n      data-variant={variant}\n      data-size={size}\n      className={cn(buttonVariants({ variant, size, className }))}\n      {...props}\n    />\n  )\n}\n\nexport { Button, buttonVariants }\n","type":"registry:ui","target":"components/ui/button.tsx"},{"path":"components/ui/tooltip.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction TooltipProvider({\n  delayDuration = 0,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n  return (\n    <TooltipPrimitive.Provider\n      data-slot=\"tooltip-provider\"\n      delayDuration={delayDuration}\n      {...props}\n    />\n  )\n}\n\nfunction Tooltip({\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n  return <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n}\n\nfunction TooltipTrigger({\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n  return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />\n}\n\nfunction TooltipContent({\n  className,\n  sideOffset = 0,\n  children,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\n  return (\n    <TooltipPrimitive.Portal>\n      <TooltipPrimitive.Content\n        data-slot=\"tooltip-content\"\n        sideOffset={sideOffset}\n        className={cn(\n          \"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n        <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground\" />\n      </TooltipPrimitive.Content>\n    </TooltipPrimitive.Portal>\n  )\n}\n\nexport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }\n","type":"registry:ui","target":"components/ui/tooltip.tsx"},{"path":"components/ui/badge.tsx","content":"import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst badgeVariants = cva(\n  \"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground [a]:hover:bg-primary/80\",\n        secondary:\n          \"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80\",\n        destructive:\n          \"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20\",\n        outline:\n          \"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground\",\n        ghost:\n          \"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nfunction Badge({\n  className,\n  variant = \"default\",\n  asChild = false,\n  ...props\n}: React.ComponentProps<\"span\"> &\n  VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n  const Comp = asChild ? Slot.Root : \"span\"\n\n  return (\n    <Comp\n      data-slot=\"badge\"\n      data-variant={variant}\n      className={cn(badgeVariants({ variant }), className)}\n      {...props}\n    />\n  )\n}\n\nexport { Badge, badgeVariants }\n","type":"registry:ui","target":"components/ui/badge.tsx"}],"description":"An edge to edge gallery with large visuals and smooth motion. Use it to surround visitors in your photos or brand imagery on a campaign page.","dependencies":["class-variance-authority","lucide-react","radix-ui"]}