{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "event-list",
  "version": "7.1.1",
  "category": "events",
  "meta": {
    "preview": "https://ui.manifest.build/previews/event-list.png",
    "version": "7.1.1",
    "changelog": {
      "1.0.0": "Initial release with grid, list and carousel layouts. Includes pagination support.",
      "2.0.0": "BREAKING: Updated to use simplified Event interface with display-formatted dateTime",
      "3.0.0": "Updated grid variant to show 3 cards with images and View More button. Added onViewMore action.",
      "4.0.0": "BREAKING: Added fullwidth variant with split-screen map layout. Updated to 15 events. Added coordinates to Event type.",
      "5.0.0": "BREAKING: Fullwidth is no longer a variant - use fullscreenComponent instead. Map now uses real Leaflet.",
      "5.1.0": "Added animated filter panel with Category, Date, Neighborhood, Price, and Format filters. Filters apply to both list and map markers.",
      "5.2.0": "Added expand button next to title in list and carousel variants with onExpand action",
      "5.2.1": "Fixed event-card dependency resolution for shadcn CLI installation",
      "6.0.0": "BREAKING: Removed id from Event interface. Use array index for selection and key.",
      "6.0.1": "Added aria-labels to navigation buttons, carousel dots, filter close, and expand buttons for accessibility",
      "6.0.2": "Moved demo data to separate file for cleaner component code",
      "6.0.3": "Added comprehensive JSDoc documentation",
      "6.0.4": "Removed Next.js dependency - now uses React-only lazy loading for map components",
      "6.0.5": "Updated Props interface with decorative header and sub-parameter JSDoc documentation",
      "6.0.6": "Fixed defensive property access to handle empty objects and null values",
      "6.0.7": "Added types.ts to registry for proper installation",
      "6.0.8": "Added demo/data.ts to registry for proper installation via shadcn CLI",
      "7.0.0": "BREAKING: Removed onPageChange, onViewMore, onExpand, onFilterClick, onFiltersApply actions. Filters and expand are now internal.",
      "7.0.1": "Removed default content data - component only renders explicitly provided data",
      "7.0.2": "Extracted shared map utilities to shared.tsx and fixed Leaflet CSS deduplication",
      "7.1.0": "Added demo data defaults - component renders demo content when no data prop is provided",
      "7.1.1": "Fixed react-leaflet Invalid hook call errors by using React.lazy instead of useEffect dynamic imports"
    }
  },
  "changelog": {
    "1.0.0": "Initial release with grid, list and carousel layouts. Includes pagination support.",
    "2.0.0": "BREAKING: Updated to use simplified Event interface with display-formatted dateTime",
    "3.0.0": "Updated grid variant to show 3 cards with images and View More button. Added onViewMore action.",
    "4.0.0": "BREAKING: Added fullwidth variant with split-screen map layout. Updated to 15 events. Added coordinates to Event type.",
    "5.0.0": "BREAKING: Fullwidth is no longer a variant - use fullscreenComponent instead. Map now uses real Leaflet.",
    "5.1.0": "Added animated filter panel with Category, Date, Neighborhood, Price, and Format filters. Filters apply to both list and map markers.",
    "5.2.0": "Added expand button next to title in list and carousel variants with onExpand action",
    "5.2.1": "Fixed event-card dependency resolution for shadcn CLI installation",
    "6.0.0": "BREAKING: Removed id from Event interface. Use array index for selection and key.",
    "6.0.1": "Added aria-labels to navigation buttons, carousel dots, filter close, and expand buttons for accessibility",
    "6.0.2": "Moved demo data to separate file for cleaner component code",
    "6.0.3": "Added comprehensive JSDoc documentation",
    "6.0.4": "Removed Next.js dependency - now uses React-only lazy loading for map components",
    "6.0.5": "Updated Props interface with decorative header and sub-parameter JSDoc documentation",
    "6.0.6": "Fixed defensive property access to handle empty objects and null values",
    "6.0.7": "Added types.ts to registry for proper installation",
    "6.0.8": "Added demo/data.ts to registry for proper installation via shadcn CLI",
    "7.0.0": "BREAKING: Removed onPageChange, onViewMore, onExpand, onFilterClick, onFiltersApply actions. Filters and expand are now internal.",
    "7.0.1": "Removed default content data - component only renders explicitly provided data",
    "7.0.2": "Extracted shared map utilities to shared.tsx and fixed Leaflet CSS deduplication",
    "7.1.0": "Added demo data defaults - component renders demo content when no data prop is provided",
    "7.1.1": "Fixed react-leaflet Invalid hook call errors by using React.lazy instead of useEffect dynamic imports"
  },
  "title": "Event List",
  "author": "MNFST, Inc",
  "description": "Display events in grid, list, or carousel layouts. Fullscreen mode shows interactive split-screen map with Leaflet and animated filter panel.",
  "dependencies": [
    "lucide-react",
    "react-leaflet",
    "leaflet"
  ],
  "devDependencies": [
    "@types/leaflet"
  ],
  "registryDependencies": [
    "button",
    "checkbox",
    "https://ui.manifest.build/r/event-card.json",
    "https://ui.manifest.build/r/manifest-types.json",
    "https://ui.manifest.build/r/event-shared.json"
  ],
  "files": [
    {
      "path": "registry/events/event-list.tsx",
      "content": "'use client'\n\nimport { Button } from '@/components/ui/button'\nimport { Checkbox } from '@/components/ui/checkbox'\nimport { cn } from '@/lib/utils'\nimport { ChevronDown, ChevronLeft, ChevronRight, SlidersHorizontal, X } from 'lucide-react'\nimport { Suspense, useCallback, useRef, useState } from 'react'\nimport type { Event } from './types'\nimport { EventCard } from './event-card'\nimport { demoEvents } from './demo/events'\nimport { LazyLeafletMap, MapPlaceholder } from './shared'\n\n// SVG pin marker - teardrop shape like Google Maps\nfunction createPinSvg(isSelected: boolean) {\n  const color = '#374151' // slate-700\n  const ringColor = isSelected ? '#9ca3af' : 'transparent' // gray-400 ring when selected\n  return `\n    <svg width=\"32\" height=\"42\" viewBox=\"0 0 32 42\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n      ${isSelected ? `<circle cx=\"16\" cy=\"16\" r=\"14\" fill=\"none\" stroke=\"${ringColor}\" stroke-width=\"3\"/>` : ''}\n      <path d=\"M16 0C7.163 0 0 7.163 0 16c0 12 16 26 16 26s16-14 16-26c0-8.837-7.163-16-16-16z\" fill=\"${color}\"/>\n      <circle cx=\"16\" cy=\"16\" r=\"6\" fill=\"white\"/>\n    </svg>\n  `\n}\n\n// Filter options\nconst categoryOptions = ['Music', 'Comedy', 'Classes', 'Sports', 'Food & Drink', 'Arts', 'Film', 'Nightlife', 'Wellness', 'Networking']\nconst dateOptions = ['Today', 'Tomorrow', 'This weekend', 'This week', 'Next week', 'This month', 'Custom range']\nconst neighborhoodOptions = ['Downtown', 'Hollywood', 'Santa Monica', 'Venice', 'Echo Park', 'Silver Lake', 'Los Feliz', 'DTLA', 'Arts District', 'Brentwood', 'Miracle Mile', 'Hollywood Hills', 'Elysian Park']\nconst priceOptions = ['Free', 'Under $25', '$25 - $50', '$50 - $100', '$100+']\nconst formatOptions = ['In-person', 'Online', 'Hybrid']\n\ninterface FilterState {\n  categories: string[]\n  dates: string[]\n  neighborhoods: string[]\n  prices: string[]\n  formats: string[]\n}\n\n/**\n * Default empty filter state.\n * @constant\n */\nconst defaultFilters: FilterState = {\n  categories: [],\n  dates: [],\n  neighborhoods: [],\n  prices: [],\n  formats: []\n}\n\n/**\n * Filter section component with expandable checkbox list.\n * @component\n */\nfunction FilterSection({\n  title,\n  options,\n  selected,\n  onChange,\n  defaultExpanded = true,\n  showLimit = 5\n}: {\n  title: string\n  options: string[]\n  selected: string[]\n  onChange: (values: string[]) => void\n  defaultExpanded?: boolean\n  showLimit?: number\n}) {\n  const [expanded, setExpanded] = useState(defaultExpanded)\n  const [showAll, setShowAll] = useState(false)\n\n  const visibleOptions = showAll ? options : options.slice(0, showLimit)\n  const hasMore = options.length > showLimit\n\n  const toggleOption = (option: string) => {\n    if (selected.includes(option)) {\n      onChange(selected.filter(s => s !== option))\n    } else {\n      onChange([...selected, option])\n    }\n  }\n\n  return (\n    <div className=\"border-b border-border/50\">\n      <button\n        onClick={() => setExpanded(!expanded)}\n        className=\"flex w-full items-center justify-between py-4 text-sm font-medium hover:text-foreground/80 transition-colors\"\n      >\n        <span>{title}</span>\n        <ChevronDown className={cn(\n          \"h-4 w-4 text-muted-foreground transition-transform duration-200\",\n          expanded && \"rotate-180\"\n        )} />\n      </button>\n      <div className={cn(\n        \"grid transition-all duration-200 ease-out\",\n        expanded ? \"grid-rows-[1fr] opacity-100\" : \"grid-rows-[0fr] opacity-0\"\n      )}>\n        <div className=\"overflow-hidden\">\n          <div className=\"space-y-2 pb-4\">\n            {visibleOptions.map(option => (\n              <label\n                key={option}\n                className=\"flex items-center gap-3 cursor-pointer group\"\n              >\n                <Checkbox\n                  checked={selected.includes(option)}\n                  onCheckedChange={() => toggleOption(option)}\n                  className=\"h-4 w-4\"\n                />\n                <span className=\"text-sm text-muted-foreground group-hover:text-foreground transition-colors\">\n                  {option}\n                </span>\n              </label>\n            ))}\n            {hasMore && (\n              <button\n                onClick={() => setShowAll(!showAll)}\n                className=\"mt-1 text-xs text-primary hover:text-primary/80 transition-colors\"\n              >\n                {showAll ? 'Show less' : `View ${options.length - showLimit} more`}\n              </button>\n            )}\n          </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n\n// Filter panel that slides over the event list\nfunction FilterPanel({\n  isOpen,\n  onClose,\n  filters,\n  onFiltersChange,\n  onApply,\n  onReset,\n  resultCount\n}: {\n  isOpen: boolean\n  onClose: () => void\n  filters: FilterState\n  onFiltersChange: (filters: FilterState) => void\n  onApply: () => void\n  onReset: () => void\n  resultCount: number\n}) {\n  const activeFiltersCount = Object.values(filters).flat().length\n\n  return (\n    <>\n      {/* Backdrop */}\n      <div\n        className={cn(\n          \"absolute inset-0 bg-background/60 backdrop-blur-[2px] transition-opacity duration-300 z-10\",\n          isOpen ? \"opacity-100\" : \"opacity-0 pointer-events-none\"\n        )}\n        onClick={onClose}\n      />\n\n      {/* Panel */}\n      <div\n        className={cn(\n          \"absolute inset-0 bg-background z-20 flex flex-col transition-all duration-300 ease-out\",\n          isOpen\n            ? \"opacity-100 translate-x-0\"\n            : \"opacity-0 -translate-x-4 pointer-events-none\"\n        )}\n      >\n        {/* Header */}\n        <div className=\"flex items-center justify-between border-b px-4 py-3\">\n          <div className=\"flex items-center gap-2\">\n            <span className=\"font-semibold\">Filters</span>\n            {activeFiltersCount > 0 && (\n              <span className=\"bg-primary text-primary-foreground text-xs px-2 py-0.5 rounded-full\">\n                {activeFiltersCount}\n              </span>\n            )}\n          </div>\n          <Button\n            variant=\"ghost\"\n            size=\"icon\"\n            className=\"h-8 w-8\"\n            onClick={onClose}\n            aria-label=\"Close filters\"\n          >\n            <X className=\"h-4 w-4\" />\n          </Button>\n        </div>\n\n        {/* Filter sections */}\n        <div className=\"flex-1 overflow-y-auto px-4 py-3 space-y-1\">\n          <FilterSection\n            title=\"Category\"\n            options={categoryOptions}\n            selected={filters.categories}\n            onChange={(categories) => onFiltersChange({ ...filters, categories })}\n          />\n          <FilterSection\n            title=\"Date\"\n            options={dateOptions}\n            selected={filters.dates}\n            onChange={(dates) => onFiltersChange({ ...filters, dates })}\n          />\n          <FilterSection\n            title=\"Neighborhood\"\n            options={neighborhoodOptions}\n            selected={filters.neighborhoods}\n            onChange={(neighborhoods) => onFiltersChange({ ...filters, neighborhoods })}\n          />\n          <FilterSection\n            title=\"Price\"\n            options={priceOptions}\n            selected={filters.prices}\n            onChange={(prices) => onFiltersChange({ ...filters, prices })}\n          />\n          <FilterSection\n            title=\"Format\"\n            options={formatOptions}\n            selected={filters.formats}\n            onChange={(formats) => onFiltersChange({ ...filters, formats })}\n            showLimit={3}\n          />\n        </div>\n\n        {/* Footer with actions */}\n        <div className=\"border-t px-4 py-3 space-y-2\">\n          <Button\n            className=\"w-full\"\n            onClick={onApply}\n          >\n            Show {resultCount} events\n          </Button>\n          {activeFiltersCount > 0 && (\n            <Button\n              variant=\"ghost\"\n              className=\"w-full text-muted-foreground hover:text-foreground\"\n              onClick={onReset}\n            >\n              Reset all filters\n            </Button>\n          )}\n        </div>\n      </div>\n    </>\n  )\n}\n\n/**\n * ═══════════════════════════════════════════════════════════════════════════\n * EventListProps\n * ═══════════════════════════════════════════════════════════════════════════\n *\n * Props for the EventList component. Supports list, grid, carousel, and\n * fullwidth (split-screen with map) layout variants with filtering capabilities.\n */\nexport interface EventListProps {\n  data?: {\n    /** Array of events to display. */\n    events?: Event[]\n    /** Optional title displayed above the list. */\n    title?: string\n  }\n  actions?: {\n    /** Called when an event card is clicked. */\n    onEventSelect?: (event: Event) => void\n  }\n  appearance?: {\n    /**\n     * Layout variant for the event list.\n     * @default \"list\"\n     */\n    variant?: 'list' | 'grid' | 'carousel' | 'fullwidth'\n    /** Number of columns for grid layout. */\n    columns?: 2 | 3 | 4\n  }\n}\n\n/**\n * An event list component with multiple layout variants.\n * Supports list, grid, carousel, and fullwidth (split-screen with map) layouts.\n *\n * Features:\n * - Four layout variants (list, grid, carousel, fullwidth)\n * - Interactive map with event markers (fullwidth)\n * - Filter panel with categories, dates, neighborhoods, prices, formats\n * - Responsive carousel with navigation\n * - Event hover/selection sync between list and map\n * - Pagination support\n *\n * @component\n * @example\n * ```tsx\n * <EventList\n *   data={{\n *     events: [...],\n *     title: \"Events near you\"\n *   }}\n *   actions={{\n *     onEventSelect: (event) => console.log(\"Selected:\", event.title),\n *     onExpand: () => console.log(\"Expand to fullscreen\"),\n *     onFiltersApply: (filters) => console.log(\"Filters:\", filters)\n *   }}\n *   appearance={{\n *     variant: \"grid\",\n *     columns: 3,\n *     eventsPerPage: 10\n *   }}\n * />\n * ```\n */\nexport function EventList({ data, actions, appearance }: EventListProps) {\n  const resolved: NonNullable<EventListProps['data']> = data ?? { events: demoEvents }\n  const events = resolved.events ?? []\n  const title = resolved.title\n  const onEventSelect = actions?.onEventSelect\n  const variant = appearance?.variant ?? 'list'\n  const [currentIndex, setCurrentIndex] = useState(0)\n  const [selectedEventIndex, setSelectedEventIndex] = useState<number | null>(null)\n\n  // Filter state for fullwidth variant\n  const [showFilters, setShowFilters] = useState(false)\n  const [filters, setFilters] = useState<FilterState>(defaultFilters)\n  const [appliedFilters, setAppliedFilters] = useState<FilterState>(defaultFilters)\n\n  // Refs for fullwidth variant scroll functionality\n  const listContainerRef = useRef<HTMLDivElement>(null)\n  const eventItemRefs = useRef<Map<number, HTMLDivElement>>(new Map())\n\n  // Scroll to event in list when selected from map\n  const scrollToEvent = useCallback((eventIndex: number) => {\n    const eventElement = eventItemRefs.current.get(eventIndex)\n    if (eventElement && listContainerRef.current) {\n      const container = listContainerRef.current\n      const elementTop = eventElement.offsetTop\n      const elementHeight = eventElement.offsetHeight\n      const containerHeight = container.offsetHeight\n      const scrollTo = elementTop - containerHeight / 2 + elementHeight / 2\n\n      container.scrollTo({\n        top: scrollTo,\n        behavior: 'smooth'\n      })\n    }\n  }, [])\n\n  // Filter events based on applied filters\n  const filterEvents = useCallback((eventsToFilter: Event[], filtersToApply: FilterState): Event[] => {\n    return eventsToFilter.filter(event => {\n      // Category filter\n      if (filtersToApply.categories.length > 0) {\n        if (!event.category || !filtersToApply.categories.includes(event.category)) return false\n      }\n\n      // Date filter - parse dateTime string for keywords\n      if (filtersToApply.dates.length > 0) {\n        const dateTimeLower = event.dateTime.toLowerCase()\n        const dateMatch = filtersToApply.dates.some(dateOption => {\n          if (dateOption === 'Today') {\n            return dateTimeLower.includes('today') || dateTimeLower.includes('tonight')\n          }\n          if (dateOption === 'Tomorrow') {\n            return dateTimeLower.includes('tomorrow')\n          }\n          if (dateOption === 'This weekend') {\n            return dateTimeLower.includes('saturday') || dateTimeLower.includes('sunday')\n          }\n          if (dateOption === 'This week') {\n            // Match any day name or \"In X days\" where X <= 7\n            const dayNames = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']\n            if (dayNames.some(day => dateTimeLower.includes(day))) return true\n            if (dateTimeLower.includes('today') || dateTimeLower.includes('tonight') || dateTimeLower.includes('tomorrow')) return true\n            const inDaysMatch = dateTimeLower.match(/in (\\d+) day/)\n            if (inDaysMatch && parseInt(inDaysMatch[1]) <= 7) return true\n            return false\n          }\n          if (dateOption === 'Next week') {\n            const inDaysMatch = dateTimeLower.match(/in (\\d+) day/)\n            if (inDaysMatch) {\n              const days = parseInt(inDaysMatch[1])\n              return days > 7 && days <= 14\n            }\n            return false\n          }\n          if (dateOption === 'This month') {\n            // Accept all events for \"this month\" as a broad filter\n            return true\n          }\n          // Custom range - accept all for now\n          return true\n        })\n        if (!dateMatch) return false\n      }\n\n      // Neighborhood filter\n      if (filtersToApply.neighborhoods.length > 0) {\n        const eventNeighborhood = event.neighborhood || ''\n        if (!filtersToApply.neighborhoods.some(n =>\n          eventNeighborhood.toLowerCase().includes(n.toLowerCase())\n        )) return false\n      }\n\n      // Price filter\n      if (filtersToApply.prices.length > 0) {\n        const priceMatch = filtersToApply.prices.some(priceRange => {\n          const eventPriceRange = event.priceRange ?? ''\n          if (priceRange === 'Free') {\n            return eventPriceRange.toLowerCase().includes('free')\n          }\n          // Extract numeric price from event\n          const priceNum = parseInt(eventPriceRange.replace(/[^0-9]/g, '')) || 0\n          if (priceRange === 'Under $25') return priceNum < 25\n          if (priceRange === '$25 - $50') return priceNum >= 25 && priceNum <= 50\n          if (priceRange === '$50 - $100') return priceNum >= 50 && priceNum <= 100\n          if (priceRange === '$100+') return priceNum >= 100\n          return true\n        })\n        if (!priceMatch) return false\n      }\n\n      // Format filter - check if event is in-person, online, or hybrid\n      if (filtersToApply.formats.length > 0) {\n        const formatMatch = filtersToApply.formats.some(format => {\n          // All demo events have venues, so they're all in-person\n          // In a real app, you'd check for onlineUrl or locationType\n          if (format === 'In-person') {\n            return event.venue && event.city\n          }\n          if (format === 'Online') {\n            // Would check for onlineUrl field\n            return false\n          }\n          if (format === 'Hybrid') {\n            // Would check for both venue and onlineUrl\n            return false\n          }\n          return true\n        })\n        if (!formatMatch) return false\n      }\n\n      return true\n    })\n  }, [])\n\n  // List variant\n  if (variant === 'list') {\n    return (\n      <div className=\"space-y-3\">\n        {title && (\n          <div className=\"mb-4\">\n            <h2 className=\"text-lg font-semibold\">{title}</h2>\n          </div>\n        )}\n        {events.slice(0, 3).map((event, index) => (\n          <EventCard\n            key={index}\n            data={{ event }}\n            appearance={{ variant: 'horizontal' }}\n            actions={{ onClick: onEventSelect }}\n          />\n        ))}\n      </div>\n    )\n  }\n\n  // Grid variant (inline mode - show 3 events with images)\n  if (variant === 'grid') {\n    return (\n      <div className=\"space-y-4\">\n        {title && (\n          <div>\n            <h2 className=\"text-lg font-semibold\">{title}</h2>\n          </div>\n        )}\n        <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n          {events.slice(0, 3).map((event, index) => (\n            <EventCard\n              key={index}\n              data={{ event }}\n              appearance={{ variant: 'default' }}\n              actions={{ onClick: onEventSelect }}\n            />\n          ))}\n        </div>\n      </div>\n    )\n  }\n\n  // Fullwidth variant with split-screen layout (list on left, map on right)\n  if (variant === 'fullwidth') {\n    const handleEventHover = (eventIndex: number | null) => {\n      setSelectedEventIndex(eventIndex)\n    }\n\n    const handleEventClick = (event: Event, index: number) => {\n      setSelectedEventIndex(index)\n      onEventSelect?.(event)\n    }\n\n    const handleMapMarkerClick = (event: Event, index: number) => {\n      setSelectedEventIndex(index)\n      scrollToEvent(index)\n      onEventSelect?.(event)\n    }\n\n    const handleFilterButtonClick = () => {\n      setFilters(appliedFilters)\n      setShowFilters(true)\n    }\n\n    const handleApplyFilters = () => {\n      setAppliedFilters(filters)\n      setShowFilters(false)\n    }\n\n    const handleResetFilters = () => {\n      setFilters(defaultFilters)\n      setAppliedFilters(defaultFilters)\n    }\n\n    // Get filtered events\n    const filteredEvents = filterEvents(events, appliedFilters)\n    // Get preview count for filter panel (shows what would be selected)\n    const previewFilteredCount = filterEvents(events, filters).length\n    // Count of active filters\n    const activeFiltersCount = Object.values(appliedFilters).flat().length\n\n    return (\n      <div className=\"flex h-full min-h-[600px] bg-background\">\n        {/* Left Panel - Event List */}\n        <div className=\"w-full md:w-[50%] lg:w-[45%] xl:w-[40%] xl:max-w-[720px] flex-shrink-0 border-r flex flex-col relative\">\n          {/* Header */}\n          <div className=\"flex items-center justify-between gap-3 border-b px-4 py-3\">\n            <div className=\"flex items-center gap-2 min-w-0\">\n              {title && <span className=\"font-semibold truncate\">{title}</span>}\n              <span className=\"text-muted-foreground text-xs whitespace-nowrap\">| {filteredEvents.length}</span>\n            </div>\n            <Button\n              variant=\"outline\"\n              size=\"sm\"\n              className=\"gap-2 flex-shrink-0\"\n              onClick={handleFilterButtonClick}\n            >\n              <SlidersHorizontal className=\"h-4 w-4\" />\n              <span className=\"hidden sm:inline\">Filters</span>\n              {activeFiltersCount > 0 && (\n                <span className=\"bg-primary text-primary-foreground text-xs px-1.5 py-0.5 rounded-full\">\n                  {activeFiltersCount}\n                </span>\n              )}\n            </Button>\n          </div>\n\n          {/* Scrollable Event List */}\n          <div ref={listContainerRef} className=\"flex-1 overflow-y-auto\">\n            {filteredEvents.length === 0 ? (\n              <div className=\"flex flex-col items-center justify-center h-full p-8 text-center\">\n                <p className=\"text-muted-foreground\">No events match your filters</p>\n                <Button\n                  variant=\"link\"\n                  className=\"mt-2\"\n                  onClick={handleResetFilters}\n                >\n                  Reset filters\n                </Button>\n              </div>\n            ) : (\n              filteredEvents.map((event, index) => (\n                <div\n                  key={index}\n                  ref={(el) => {\n                    if (el) eventItemRefs.current.set(index, el)\n                  }}\n                  className={cn(\n                    'border-b transition-colors cursor-pointer',\n                    selectedEventIndex === index && 'bg-accent'\n                  )}\n                  onMouseEnter={() => handleEventHover(index)}\n                  onMouseLeave={() => handleEventHover(null)}\n                  onClick={() => handleEventClick(event, index)}\n                >\n                  <div className=\"flex gap-3 p-3\">\n                    {/* Thumbnail */}\n                    {event.image && (\n                      <div className=\"h-20 w-20 flex-shrink-0 overflow-hidden rounded-md bg-muted\">\n                        <img\n                          src={event.image}\n                          alt={event.title || 'Event image'}\n                          className=\"h-full w-full object-cover\"\n                        />\n                      </div>\n                    )}\n                    {/* Event Info */}\n                    <div className=\"flex-1 min-w-0\">\n                      {event.priceRange && (\n                        <p className=\"font-semibold text-sm\">{event.priceRange}</p>\n                      )}\n                      {(event.dateTime || event.category) && (\n                        <p className=\"text-xs text-muted-foreground mt-0.5 line-clamp-1\">\n                          {[event.dateTime, event.category].filter(Boolean).join(' · ')}\n                        </p>\n                      )}\n                      {(event.venue || event.city) && (\n                        <p className=\"text-xs text-muted-foreground mt-0.5 line-clamp-1\">\n                          {[event.venue, event.city].filter(Boolean).join(', ')}\n                        </p>\n                      )}\n                      {event.title && (\n                        <p className=\"text-sm font-medium mt-1 line-clamp-1\">{event.title}</p>\n                      )}\n                    </div>\n                  </div>\n                </div>\n              ))\n            )}\n          </div>\n\n          {/* Filter Panel Overlay */}\n          <FilterPanel\n            isOpen={showFilters}\n            onClose={() => setShowFilters(false)}\n            filters={filters}\n            onFiltersChange={setFilters}\n            onApply={handleApplyFilters}\n            onReset={handleResetFilters}\n            resultCount={previewFilteredCount}\n          />\n        </div>\n\n        {/* Right Panel - Map */}\n        <div className=\"hidden md:flex flex-1 relative\">\n          <Suspense fallback={<MapPlaceholder />}>\n            <LazyLeafletMap\n              center={[34.0522, -118.2437]}\n              zoom={12}\n              renderMarkers={({ Marker, L }) => (\n                <>\n                  {filteredEvents.map((event, index) => {\n                    if (!event.coordinates) return null\n                    const isSelected = selectedEventIndex === index\n                    const icon = L.divIcon({\n                      className: '',\n                      html: `<div style=\"\n                        position: absolute;\n                        left: 50%;\n                        top: 100%;\n                        transform: translate(-50%, -100%);\n                        z-index: ${isSelected ? '1000' : '1'};\n                      \">${createPinSvg(isSelected)}</div>`,\n                      iconSize: [32, 42],\n                      iconAnchor: [16, 42]\n                    })\n                    return (\n                      <Marker\n                        key={index}\n                        position={[event.coordinates.lat, event.coordinates.lng]}\n                        icon={icon}\n                        zIndexOffset={isSelected ? 1000 : 0}\n                        eventHandlers={{\n                          click: () => handleMapMarkerClick(event, index)\n                        }}\n                      />\n                    )\n                  })}\n                </>\n              )}\n            />\n          </Suspense>\n        </div>\n      </div>\n    )\n  }\n\n  // Carousel variant\n  const maxIndexMobile = events.length - 1\n  const maxIndexTablet = Math.max(0, events.length - 2)\n  const maxIndexDesktop = Math.max(0, events.length - 3)\n\n  const prev = () => {\n    setCurrentIndex((i) => Math.max(0, i - 1))\n  }\n\n  const next = () => {\n    setCurrentIndex((i) => i + 1)\n  }\n\n  const isAtStart = currentIndex === 0\n  const isAtEndMobile = currentIndex >= maxIndexMobile\n  const isAtEndTablet = currentIndex >= maxIndexTablet\n  const isAtEndDesktop = currentIndex >= maxIndexDesktop\n\n  return (\n    <div className=\"relative\">\n      {title && (\n        <div className=\"mb-4\">\n          <h2 className=\"text-lg font-semibold\">{title}</h2>\n        </div>\n      )}\n      <div className=\"overflow-hidden rounded-lg\">\n        {/* Mobile: 1 card, slides by 100% */}\n        <div\n          className=\"flex transition-transform duration-300 ease-out md:hidden\"\n          style={{ transform: `translateX(-${currentIndex * 100}%)` }}\n        >\n          {events.map((event, index) => (\n            <div key={index} className=\"w-full shrink-0 px-0.5\">\n              <EventCard\n                data={{ event }}\n                appearance={{ variant: 'compact' }}\n                actions={{ onClick: onEventSelect }}\n              />\n            </div>\n          ))}\n        </div>\n\n        {/* Tablet: 2 cards visible, slides by 50% */}\n        <div\n          className=\"hidden md:flex lg:hidden transition-transform duration-300 ease-out\"\n          style={{ transform: `translateX(-${currentIndex * 50}%)` }}\n        >\n          {events.map((event, index) => (\n            <div key={index} className=\"w-1/2 shrink-0 px-1.5\">\n              <EventCard\n                data={{ event }}\n                appearance={{ variant: 'compact' }}\n                actions={{ onClick: onEventSelect }}\n              />\n            </div>\n          ))}\n        </div>\n\n        {/* Desktop: 3 cards visible, slides by 33.333% */}\n        <div\n          className=\"hidden lg:flex transition-transform duration-300 ease-out\"\n          style={{ transform: `translateX(-${currentIndex * (100 / 3)}%)` }}\n        >\n          {events.map((event, index) => (\n            <div key={index} className=\"w-1/3 shrink-0 px-1.5\">\n              <EventCard\n                data={{ event }}\n                appearance={{ variant: 'compact' }}\n                actions={{ onClick: onEventSelect }}\n              />\n            </div>\n          ))}\n        </div>\n      </div>\n      <div className=\"mt-3 flex items-center justify-between px-2\">\n        <div className=\"flex gap-1\">\n          {events.map((_, i) => (\n            <button\n              key={i}\n              onClick={() => setCurrentIndex(i)}\n              aria-label={`Go to slide ${i + 1}`}\n              className={cn(\n                'h-1.5 rounded-full transition-all cursor-pointer',\n                i === currentIndex\n                  ? 'w-4 bg-foreground'\n                  : 'w-1.5 bg-muted-foreground/30'\n              )}\n            />\n          ))}\n        </div>\n        {/* Mobile navigation */}\n        <div className=\"flex gap-1 md:hidden\">\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-8 w-8\"\n            onClick={prev}\n            disabled={isAtStart}\n            aria-label=\"Previous event\"\n          >\n            <ChevronLeft className=\"h-4 w-4\" />\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-8 w-8\"\n            onClick={next}\n            disabled={isAtEndMobile}\n            aria-label=\"Next event\"\n          >\n            <ChevronRight className=\"h-4 w-4\" />\n          </Button>\n        </div>\n        {/* Tablet navigation */}\n        <div className=\"hidden md:flex lg:hidden gap-1\">\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-8 w-8\"\n            onClick={prev}\n            disabled={isAtStart}\n            aria-label=\"Previous event\"\n          >\n            <ChevronLeft className=\"h-4 w-4\" />\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-8 w-8\"\n            onClick={next}\n            disabled={isAtEndTablet}\n            aria-label=\"Next event\"\n          >\n            <ChevronRight className=\"h-4 w-4\" />\n          </Button>\n        </div>\n        {/* Desktop navigation */}\n        <div className=\"hidden lg:flex gap-1\">\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-8 w-8\"\n            onClick={prev}\n            disabled={isAtStart}\n            aria-label=\"Previous event\"\n          >\n            <ChevronLeft className=\"h-4 w-4\" />\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            className=\"h-8 w-8\"\n            onClick={next}\n            disabled={isAtEndDesktop}\n            aria-label=\"Next event\"\n          >\n            <ChevronRight className=\"h-4 w-4\" />\n          </Button>\n        </div>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:block",
      "target": "components/ui/event-list.tsx"
    },
    {
      "path": "registry/events/demo/events.ts",
      "content": "// Demo data for Events category components\n// This file contains sample data used for component previews and documentation\n\nimport type { Event, EventDetails } from '../types'\n\n// Helper to generate dates relative to today\nfunction getDateAt(daysFromNow: number, hour: number): string {\n  const date = new Date()\n  date.setDate(date.getDate() + daysFromNow)\n  date.setHours(hour, 0, 0, 0)\n  return date.toISOString()\n}\n\n// Single event for EventCard default\nexport const demoEvent: Event = {\n  title: 'NEON Vol. 9',\n  category: 'Music',\n  venue: 'Echoplex',\n  neighborhood: 'Echo Park',\n  city: 'Los Angeles',\n  dateTime: 'Tonight 9:00 PM - 3:00 AM',\n  priceRange: '$45 - $150',\n  image: 'https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=800',\n  vibeTags: ['High energy', 'Late night', 'Dressy'],\n  vibeDescription:\n    'Immersive electronic experience with world-class DJs and stunning visuals.',\n  aiSummary:\n    \"Immersive electronic night with world-class DJs and stunning visuals at LA's top-rated venue.\",\n  lineup: ['DJ Shadow', 'Bonobo', 'Four Tet', 'Caribou'],\n  ticketTiers: [\n    'General Admission $45',\n    'VIP Access $120',\n    'Backstage Pass $150'\n  ],\n  eventSignal: 'going-fast',\n  organizerRating: 4.8,\n  reviewCount: 12453,\n  venueRating: 4.8,\n  ageRestriction: '21+',\n  hasMultipleDates: true\n}\n\n// 15 events for EventList default\nexport const demoEvents: Event[] = [\n  {\n    title: 'NEON Vol. 9',\n    category: 'Music',\n    venue: 'Echoplex',\n    neighborhood: 'Echo Park',\n    city: 'Los Angeles',\n    dateTime: 'Tonight 9:00 PM - 3:00 AM',\n    priceRange: '$45 - $150',\n    image: 'https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=800',\n    coordinates: { lat: 34.0781, lng: -118.2606 },\n    vibeTags: ['High energy', 'Late night'],\n    eventSignal: 'going-fast',\n    organizerRating: 4.8,\n    reviewCount: 12453,\n    ageRestriction: '21+'\n  },\n  {\n    title: 'The Midnight Show',\n    category: 'Comedy',\n    venue: 'The Comedy Underground',\n    neighborhood: 'Santa Monica',\n    city: 'Los Angeles',\n    dateTime: 'Tonight 10:00 PM - 12:00 AM',\n    priceRange: '$15 - $35',\n    image: 'https://images.unsplash.com/photo-1585699324551-f6c309eedeca?w=800',\n    coordinates: { lat: 34.0195, lng: -118.4912 },\n    vibeTags: ['Social', 'Late night'],\n    eventSignal: 'popular',\n    organizerRating: 4.7,\n    reviewCount: 3241,\n    discount: 'TONIGHT ONLY - 40% OFF'\n  },\n  {\n    title: 'Salsa Sundays @ Echo Park',\n    category: 'Classes',\n    venue: 'Echo Park Lake',\n    neighborhood: 'Echo Park',\n    city: 'Los Angeles',\n    dateTime: 'Saturday 6:00 PM - 10:00 PM',\n    priceRange: 'Free',\n    image: 'https://images.unsplash.com/photo-1504609813442-a8924e83f76e?w=800',\n    coordinates: { lat: 34.0731, lng: -118.2608 },\n    vibeTags: ['High energy', 'Social'],\n    eventSignal: 'just-added',\n    organizerRating: 4.9,\n    reviewCount: 8764\n  },\n  {\n    title: 'Dawn Flow: Griffith Park',\n    category: 'Classes',\n    venue: 'Griffith Park',\n    neighborhood: 'Los Feliz',\n    city: 'Los Angeles',\n    dateTime: 'Tomorrow 6:00 AM - 8:00 AM',\n    priceRange: 'Free',\n    image: 'https://images.unsplash.com/photo-1545205597-3d9d02c29597?w=800',\n    coordinates: { lat: 34.1365, lng: -118.2943 },\n    vibeTags: ['Chill', 'Wellness', 'Outdoor'],\n    organizerRating: 4.9,\n    reviewCount: 8764,\n    discount: 'FREE - First 50 Only'\n  },\n  {\n    title: 'Lakers vs Celtics',\n    category: 'Sports',\n    venue: 'Crypto.com Arena',\n    neighborhood: 'Downtown',\n    city: 'Los Angeles',\n    dateTime: 'Friday 7:30 PM - 10:30 PM',\n    priceRange: '$125 - $850',\n    image: 'https://images.unsplash.com/photo-1546519638-68e109498ffc?w=800',\n    coordinates: { lat: 34.0430, lng: -118.2673 },\n    vibeTags: ['High energy', 'Social', 'Premium'],\n    eventSignal: 'sales-end-soon',\n    organizerRating: 4.5,\n    reviewCount: 2341\n  },\n  {\n    title: 'Smorgasburg LA: Sunday Market',\n    category: 'Food & Drink',\n    venue: 'ROW DTLA',\n    neighborhood: 'Arts District',\n    city: 'Los Angeles',\n    dateTime: 'Sunday 10:00 AM - 4:00 PM',\n    priceRange: 'Free',\n    image: 'https://images.unsplash.com/photo-1555939594-58d7cb561ad1?w=800',\n    coordinates: { lat: 34.0341, lng: -118.2324 },\n    vibeTags: ['Family-friendly', 'Outdoor', 'Social'],\n    organizerRating: 4.8,\n    reviewCount: 5632\n  },\n  {\n    title: 'LACMA After Hours',\n    category: 'Arts',\n    venue: 'LACMA',\n    neighborhood: 'Miracle Mile',\n    city: 'Los Angeles',\n    dateTime: 'Friday 7:00 PM - 11:00 PM',\n    priceRange: '$35 - $75',\n    image: 'https://images.unsplash.com/photo-1531243269054-5ebf6f34081e?w=800',\n    coordinates: { lat: 34.0639, lng: -118.3592 },\n    vibeTags: ['Chill', 'Date night', 'Sophisticated'],\n    organizerRating: 4.7,\n    reviewCount: 1234,\n    ageRestriction: '21+',\n    discount: 'MEMBER PRICE'\n  },\n  {\n    title: 'Blue Note Under Stars',\n    category: 'Music',\n    venue: 'Hollywood Bowl',\n    neighborhood: 'Hollywood Hills',\n    city: 'Los Angeles',\n    dateTime: 'Saturday 8:00 PM - 11:00 PM',\n    priceRange: '$45 - $200',\n    image: 'https://images.unsplash.com/photo-1514320291840-2e0a9bf2a9ae?w=800',\n    coordinates: { lat: 34.1122, lng: -118.3391 },\n    vibeTags: ['Chill', 'Date night', 'Outdoor'],\n    lineup: ['Kamasi Washington', 'Thundercat', 'Terrace Martin'],\n    organizerRating: 4.8,\n    reviewCount: 12453\n  },\n  {\n    title: 'Meraki: Seth Troxler',\n    category: 'Nightlife',\n    venue: 'Sound Nightclub',\n    neighborhood: 'Hollywood',\n    city: 'Los Angeles',\n    dateTime: 'Saturday 10:00 PM - 4:00 AM',\n    priceRange: '$35 - $65',\n    image: 'https://images.unsplash.com/photo-1571266028243-e4733b0f0bb0?w=800',\n    coordinates: { lat: 34.0928, lng: -118.3287 },\n    vibeTags: ['High energy', 'Late night', 'Underground'],\n    lineup: ['Amelie Lens', 'I Hate Models', 'FJAAK'],\n    organizerRating: 4.6,\n    reviewCount: 1876,\n    ageRestriction: '21+'\n  },\n  {\n    title: 'Whitney Cummings + Friends',\n    category: 'Comedy',\n    venue: 'The Laugh Factory',\n    neighborhood: 'Hollywood',\n    city: 'Los Angeles',\n    dateTime: 'In 2 days 8:00 PM - 11:00 PM',\n    priceRange: '$25 - $55',\n    image: 'https://images.unsplash.com/photo-1527224538127-2104bb71c51b?w=800',\n    coordinates: { lat: 34.0901, lng: -118.3615 },\n    vibeTags: ['Chill', 'Social', 'Date night'],\n    organizerRating: 4.7,\n    reviewCount: 3241,\n    ageRestriction: '18+'\n  },\n  {\n    title: 'Venice Beach Drum Circle',\n    category: 'Music',\n    venue: 'Venice Beach Boardwalk',\n    neighborhood: 'Venice',\n    city: 'Los Angeles',\n    dateTime: 'Sunday 4:00 PM - 8:00 PM',\n    priceRange: 'Free',\n    image: 'https://images.unsplash.com/photo-1506157786151-b8491531f063?w=800',\n    coordinates: { lat: 33.9850, lng: -118.4695 },\n    vibeTags: ['Outdoor', 'Social', 'Chill'],\n    eventSignal: 'popular',\n    organizerRating: 4.6,\n    reviewCount: 2145\n  },\n  {\n    title: 'Rooftop Cinema: Blade Runner',\n    category: 'Film',\n    venue: 'Rooftop Cinema Club',\n    neighborhood: 'DTLA',\n    city: 'Los Angeles',\n    dateTime: 'Friday 8:30 PM - 11:00 PM',\n    priceRange: '$25 - $45',\n    image: 'https://images.unsplash.com/photo-1489599849927-2ee91cede3ba?w=800',\n    coordinates: { lat: 34.0407, lng: -118.2468 },\n    vibeTags: ['Date night', 'Views', 'Chill'],\n    organizerRating: 4.8,\n    reviewCount: 892\n  },\n  {\n    title: 'Dodgers vs Giants',\n    category: 'Sports',\n    venue: 'Dodger Stadium',\n    neighborhood: 'Elysian Park',\n    city: 'Los Angeles',\n    dateTime: 'Saturday 1:10 PM - 4:30 PM',\n    priceRange: '$35 - $350',\n    image: 'https://images.unsplash.com/photo-1566577739112-5180d4bf9390?w=800',\n    coordinates: { lat: 34.0739, lng: -118.2400 },\n    vibeTags: ['Family-friendly', 'Social', 'High energy'],\n    eventSignal: 'few-tickets-left',\n    organizerRating: 4.7,\n    reviewCount: 15678\n  },\n  {\n    title: 'Natural Wine Fair',\n    category: 'Food & Drink',\n    venue: 'Grand Central Market',\n    neighborhood: 'Downtown',\n    city: 'Los Angeles',\n    dateTime: 'Sunday 12:00 PM - 6:00 PM',\n    priceRange: '$45 - $85',\n    image: 'https://images.unsplash.com/photo-1510812431401-41d2bd2722f3?w=800',\n    coordinates: { lat: 34.0508, lng: -118.2490 },\n    vibeTags: ['Tasting', 'Social', 'Sophisticated'],\n    eventSignal: 'just-added',\n    organizerRating: 4.5,\n    reviewCount: 567,\n    ageRestriction: '21+'\n  },\n  {\n    title: 'Meditation in the Gardens',\n    category: 'Wellness',\n    venue: 'The Getty Center',\n    neighborhood: 'Brentwood',\n    city: 'Los Angeles',\n    dateTime: 'Sunday 7:00 AM - 9:00 AM',\n    priceRange: 'Free',\n    image: 'https://images.unsplash.com/photo-1506126613408-eca07ce68773?w=800',\n    coordinates: { lat: 34.0780, lng: -118.4741 },\n    vibeTags: ['Wellness', 'Outdoor', 'Chill'],\n    organizerRating: 4.9,\n    reviewCount: 1234\n  }\n]\n\n// Detailed event for EventDetail default\nexport const demoEventDetails: EventDetails = {\n  title: 'Sunglasses at Night: Underground Techno',\n  category: 'Nightlife',\n  venue: 'The White Rabbit',\n  neighborhood: 'The Woodlands',\n  city: 'Houston, TX',\n  startDateTime: getDateAt(2, 22),\n  endDateTime: getDateAt(3, 4),\n  priceRange: '$15 - $30',\n  images: [\n    'https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=800',\n    'https://images.unsplash.com/photo-1514525253161-7a46d19cd819?w=800',\n    'https://images.unsplash.com/photo-1533174072545-7a4b6ad7a6c3?w=800'\n  ],\n  vibeTags: ['High energy', 'Late night', 'Underground'],\n  eventSignal: 'going-fast',\n  aiSummary: 'Raw, unfiltered techno in an authentic warehouse setting.',\n  description:\n    'Experience the raw energy of underground techno. Industrial beats, immersive visuals, and a crowd that lives for the music.',\n  lineup: ['Amelie Lens', 'I Hate Models', 'FJAAK'],\n  attendeesCount: 537,\n  friendsGoing: [\n    { name: 'Alex', avatar: 'https://i.pravatar.cc/40?u=alex' },\n    { name: 'Sam', avatar: 'https://i.pravatar.cc/40?u=sam' }\n  ],\n  organizer: {\n    name: 'Midnight Lovers',\n    image: 'https://i.pravatar.cc/80?u=midnight',\n    rating: 4.5,\n    reviewCount: 1067,\n    verified: true,\n    followers: 1200,\n    eventsCount: 154,\n    hostingYears: 8,\n    trackRecord: 'great',\n    responseRate: 'very responsive'\n  },\n  venue_details: {\n    name: 'The White Rabbit',\n    address: '8827 Nasher Ave',\n    city: 'Houston TX',\n    coordinates: { lat: 29.7604, lng: -95.3698 }\n  },\n  tiers: [\n    { name: 'General Admission', price: 15, available: 50 },\n    {\n      name: 'VIP Access',\n      price: 30,\n      available: 20,\n      benefits: ['Skip the line', 'Exclusive lounge']\n    }\n  ],\n  goodToKnow: {\n    duration: '2 hours',\n    doorsOpen: '7:00 PM',\n    showtime: '7:30 PM',\n    ageRestriction: '21+',\n    dressCode: 'Casual',\n    parking: 'Limited, leave early to avoid long queues'\n  },\n  policies: {\n    refund: 'No refunds. Tickets are transferable.',\n    entry: 'Open 2 hours before event',\n    idRequired: true,\n    securityOnSite: true\n  },\n  faq: [\n    {\n      question: 'What is the refund policy?',\n      answer: 'No refunds. Tickets are transferable.'\n    },\n    {\n      question: 'When do doors open?',\n      answer: 'Open 2 hours before event.'\n    },\n    {\n      question: 'Is there parking?',\n      answer: 'Limited, leave early to avoid long queues.'\n    }\n  ],\n  relatedTags: ['Houston Events', 'Texas Nightlife', 'Techno Parties']\n}\n\n// Ticket tiers for TicketTierSelect\nexport const demoTicketTiers = [\n  {\n    id: '1',\n    name: 'General Admission',\n    price: 45,\n    fee: 5,\n    available: 100,\n    maxPerOrder: 10,\n  },\n  {\n    id: '2',\n    name: 'VIP',\n    price: 150,\n    fee: 15,\n    available: 20,\n    maxPerOrder: 4,\n    description: 'Includes backstage access',\n  },\n]\n\n// Event confirmation data\nexport const demoEventConfirmation = {\n  orderNumber: 'EVT-12345',\n  eventTitle: 'Summer Music Festival',\n  ticketCount: 2,\n  recipientEmail: 'customer@example.com',\n  eventDate: 'Jan 20, 2024',\n  eventLocation: 'Central Park, New York',\n  organizer: {\n    name: 'Live Nation',\n  },\n}\n",
      "type": "registry:lib",
      "target": "components/ui/demo/events.ts"
    }
  ],
  "categories": [
    "events"
  ],
  "type": "registry:block"
}