{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "fleet-map",
  "title": "Fleet Map",
  "author": "AlgorizeTech",
  "description": "Ask the user to pick one of 3 fleet tracker variants: https://avaraui.com/view/fleet-tracker. This item holds 3 of them as looks of one component (default, minimal, modern). Skip the question if the user already named one. A live fleet tracking view pairing a Leaflet map with a vehicle list and status filters. Use it for logistics and delivery dashboards.",
  "dependencies": [
    "leaflet",
    "lucide-react",
    "react-leaflet"
  ],
  "devDependencies": [
    "@types/leaflet"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/Fleet-map.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport type * as LeafletNS from \"leaflet\";\nimport \"leaflet/dist/leaflet.css\";\nimport {\n  Map as MapIcon,\n  MapPin,\n  Menu,\n  Minus,\n  Navigation2,\n  Plus,\n  Search,\n  Truck,\n} from \"lucide-react\";\nimport { useEffect, useMemo, useState, useSyncExternalStore } from \"react\";\nimport { useMap } from \"react-leaflet\";\n\n// `useMap` is a hook and is safe to import statically -- react-leaflet's module body does\n// not touch the DOM. Leaflet itself does, so the map components are pulled in at runtime\n// (see the effect below) rather than at module scope.\n//\n// They are loaded as one namespace held in state, NOT as four separate React.lazy\n// components: lazy children suspend *after* MapContainer has mounted, which unmounts and\n// remounts the leaflet instance mid-flight and throws \"Map container is being reused by\n// another instance\". One gate, one mount.\ntype ReactLeaflet = typeof import(\"react-leaflet\");\n\n// ==========================================\n// TYPES & INTERFACES\n// ==========================================\nexport type FleetVariant = \"default\" | \"minimal\" | \"modern\";\nexport type FleetColor = \"indigo\" | \"rose\" | \"emerald\" | \"zinc\" | (string & {});\n\nexport interface Vehicle {\n  id: string;\n  location: string;\n  lat: number;\n  lng: number;\n  status?: \"active\" | \"idle\" | \"maintenance\" | (string & {});\n  speed?: string;\n}\n\nexport interface FleetTrackerProps {\n  fleetData: Vehicle[];\n  variant?: FleetVariant;\n  color?: FleetColor;\n  zoom?: number;\n  tileApiKey?: string;\n  tileUrl?: string;\n  darkTileUrl?: string;\n  attribution?: string;\n}\n\n// ==========================================\n// VARIANT & COLOR CONFIGURATION\n// ==========================================\nconst fleetVariants: Record<FleetVariant, Record<string, string>> = {\n  default: {\n    wrapper:\n      \"flex flex-col md:flex-row bg-card border border-border/80 shadow-xl rounded-2xl overflow-hidden relative [&_.leaflet-control-attribution]:hidden\",\n    sidebar:\n      \"w-full md:w-[320px] lg:w-[380px] bg-muted border-r border-border/80 shrink-0 flex flex-col h-full z-20\",\n    mapContainer: \"flex-1 w-full h-full relative z-10 min-w-0 overflow-hidden\",\n    itemActive: \"bg-card border-l-[3px] shadow-sm\",\n    itemInactive:\n      \"hover:bg-muted border-l-[3px] border-transparent\",\n    header: \"border-border\",\n    search: \"shadow-sm bg-card border border-border\",\n  },\n  minimal: {\n    wrapper:\n      \"flex flex-col md:flex-row relative rounded-2xl overflow-hidden shadow-lg border border-border/80 [&_.leaflet-control-attribution]:hidden\",\n    // Mobile: a plain full-width panel in normal flow (same as `default`).\n    // md+: the floating glass panel this variant is known for.\n    sidebar:\n      \"w-full h-full rounded-none border-0 shadow-none md:absolute md:top-4 md:left-4 md:bottom-4 md:h-auto md:w-[300px] lg:w-[340px] md:rounded-xl md:border md:shadow-2xl bg-background/95 backdrop-blur-md border-border/80 z-20 shrink-0 flex flex-col overflow-hidden\",\n    mapContainer: \"flex-1 w-full h-full relative z-0 min-w-0 overflow-hidden rounded-2xl\",\n    itemActive: \"bg-muted rounded-lg mx-3 my-1\",\n    itemInactive:\n      \"hover:bg-muted rounded-lg mx-3 my-1 border border-transparent\",\n    header: \"border-border\",\n    search: \"shadow-sm bg-card border border-border\",\n  },\n  // The same frosted recipe as GlassPanel in ProfilePageBlock: translucent\n  // card surface, hairline border, inset top highlight, over an aurora the\n  // wrapper draws. Kept inline so this block installs on its own.\n  modern: {\n    wrapper:\n      \"flex flex-col md:flex-row relative isolate overflow-hidden rounded-[28px] p-3 gap-3 bg-muted border border-foreground/10 shadow-xl [&_.leaflet-control-attribution]:hidden\",\n    sidebar:\n      \"w-full md:w-[320px] lg:w-[360px] shrink-0 flex flex-col h-full z-20 overflow-hidden rounded-[24px] border border-foreground/10 bg-card/55 backdrop-blur-2xl shadow-[inset_0_1px_0_rgba(255,255,255,0.75),0_24px_48px_-28px_rgba(15,23,42,0.35)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.08),0_24px_48px_-28px_rgba(0,0,0,0.9)]\",\n    mapContainer:\n      \"flex-1 w-full h-full relative z-10 min-w-0 rounded-[24px] overflow-hidden bg-muted border border-foreground/10 shadow-[0_24px_48px_-28px_rgba(15,23,42,0.35)] dark:shadow-[0_24px_48px_-28px_rgba(0,0,0,0.9)]\",\n    itemActive: \"bg-card/80 rounded-2xl mx-3 my-1 shadow-sm\",\n    itemInactive:\n      \"hover:bg-card/50 rounded-2xl mx-3 my-1 border border-transparent transition-colors\",\n    header: \"border-foreground/10\",\n    search: \"border border-foreground/10 bg-card/50\",\n  },\n};\n\n/** Colours washed behind the modern variant's glass, top-left / top-right / bottom. */\nconst AURORA = [\"#a78bfa\", \"#7dd3fc\", \"#fdba74\"];\n\nconst colorThemes: Record<string, Record<string, string>> = {\n  primary: {\n    text: \"text-primary\",\n    bg: \"bg-primary hover:bg-primary/90 text-primary-foreground\",\n    border: \"border-primary/30\",\n    lightBg: \"bg-primary/10\",\n    focusRing:\n      \"focus-within:ring-primary/20 focus-within:border-primary/50\",\n    marker: \"violet\",\n    glow: \"color-mix(in srgb, var(--primary) 75%, transparent)\",\n  },\n  indigo: {\n    text: \"text-indigo-600 dark:text-indigo-400\",\n    bg: \"bg-indigo-500 hover:bg-indigo-600 text-white\",\n    border: \"border-indigo-500\",\n    lightBg: \"bg-indigo-50 dark:bg-indigo-500/10\",\n    focusRing:\n      \"focus-within:ring-indigo-500/20 focus-within:border-indigo-500/50\",\n    marker: \"violet\",\n    glow: \"rgba(129, 140, 248, 0.75)\",\n  },\n  rose: {\n    text: \"text-rose-600 dark:text-rose-400\",\n    bg: \"bg-rose-500 hover:bg-rose-600 text-white\",\n    border: \"border-rose-500\",\n    lightBg: \"bg-rose-50 dark:bg-rose-500/10\",\n    focusRing: \"focus-within:ring-rose-500/20 focus-within:border-rose-500/50\",\n    marker: \"red\",\n    glow: \"rgba(244, 63, 94, 0.75)\",\n  },\n  emerald: {\n    text: \"text-emerald-600 dark:text-emerald-400\",\n    bg: \"bg-emerald-500 hover:bg-emerald-600 text-white\",\n    border: \"border-emerald-500\",\n    lightBg: \"bg-emerald-50 dark:bg-emerald-500/10\",\n    focusRing:\n      \"focus-within:ring-emerald-500/20 focus-within:border-emerald-500/50\",\n    marker: \"green\",\n    glow: \"rgba(16, 185, 129, 0.75)\",\n  },\n  zinc: {\n    text: \"text-zinc-900 dark:text-zinc-100\",\n    bg: \"bg-zinc-900 hover:bg-zinc-800 dark:bg-white dark:hover:bg-zinc-200 text-white dark:text-zinc-900\",\n    border: \"border-zinc-900 dark:border-white\",\n    lightBg: \"bg-zinc-100 dark:bg-zinc-800/50\",\n    focusRing: \"focus-within:ring-zinc-500/20 focus-within:border-zinc-500/50\",\n    marker: \"black\",\n    glow: \"rgba(250, 250, 250, 0.85)\",\n  },\n};\n\n// ==========================================\n// TILE SOURCES\n// ==========================================\n// Uses OpenStreetMap tiles as the reliable, completely free default (no API key required).\n// Users can optionally provide `tileApiKey` (for CARTO basemaps) or custom `tileUrl` / `darkTileUrl`.\nconst TILES = {\n  light:\n    \"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\n  dark:\n    \"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\n} as const;\n\nconst TILE_ATTRIBUTION =\n  '&copy; <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\" rel=\"noopener noreferrer\">OpenStreetMap</a> contributors';\n\n// ==========================================\n// DARK MAP GRADING\n// ==========================================\n// When running in dark mode with the default tile provider, a CSS color-inversion\n// and hue-rotation filter transforms standard light tiles into a dark-themed map\n// without requiring specialized dark-tile accounts or paid APIs.\n// Marker layers and popups sit above this tile layer and preserve their true colors.\nconst MAP_STYLES = `\n.fleet-map-root .leaflet-container {\n  background: #e8eaed;\n  font-family: inherit;\n}\n.fleet-map-root[data-fleet-theme=\"dark\"] .leaflet-container {\n  background: #12151b;\n}\n\n.fleet-map-root[data-fleet-theme=\"dark\"] .fleet-tiles-osm-dark {\n  filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%);\n}\n\n/* The pin art bakes in a black drop shadow that vanishes on a dark ground.\n   Fade the shadow layer and give every pin a light rim instead. */\n.fleet-map-root[data-fleet-theme=\"dark\"] .leaflet-shadow-pane {\n  opacity: 0.35;\n}\n.fleet-map-root .leaflet-marker-icon {\n  filter: drop-shadow(0 2px 3px rgba(9, 9, 11, 0.3));\n}\n.fleet-map-root[data-fleet-theme=\"dark\"] .leaflet-marker-icon {\n  filter: drop-shadow(0 0 1px rgba(228, 228, 231, 0.6))\n    drop-shadow(0 3px 5px rgba(0, 0, 0, 0.7));\n}\n\n/* Only filter and opacity are safe to animate on a marker -- leaflet owns the\n   element's transform and positions every pin through it. */\n.fleet-map-root .leaflet-marker-icon.fleet-pin-active,\n.fleet-map-root[data-fleet-theme=\"dark\"] .leaflet-marker-icon.fleet-pin-active {\n  animation: fleet-pin-pulse 2.6s ease-in-out infinite;\n}\n@keyframes fleet-pin-pulse {\n  0%,\n  100% {\n    filter: drop-shadow(0 0 1px rgba(228, 228, 231, 0.7))\n      drop-shadow(0 0 5px var(--fleet-pin-glow))\n      drop-shadow(0 3px 5px rgba(0, 0, 0, 0.55));\n  }\n  50% {\n    filter: drop-shadow(0 0 1px rgba(228, 228, 231, 0.9))\n      drop-shadow(0 0 14px var(--fleet-pin-glow))\n      drop-shadow(0 3px 5px rgba(0, 0, 0, 0.55));\n  }\n}\n@media (prefers-reduced-motion: reduce) {\n  .fleet-map-root .leaflet-marker-icon.fleet-pin-active {\n    animation: none;\n    filter: drop-shadow(0 0 1px rgba(228, 228, 231, 0.8))\n      drop-shadow(0 0 10px var(--fleet-pin-glow))\n      drop-shadow(0 3px 5px rgba(0, 0, 0, 0.55));\n  }\n}\n\n/* Popups are hard-coded white, down to the tip; repaint them from the theme. */\n.fleet-map-root .leaflet-popup-content-wrapper {\n  background: var(--popover);\n  color: var(--popover-foreground);\n  border: 1px solid var(--border);\n}\n.fleet-map-root[data-fleet-theme=\"dark\"] .leaflet-popup-content-wrapper {\n  box-shadow: 0 12px 32px -10px rgba(0, 0, 0, 0.85);\n}\n.fleet-map-root .leaflet-popup-tip {\n  background: var(--popover);\n  border: 1px solid var(--border);\n}\n.fleet-map-root[data-fleet-theme=\"dark\"] .leaflet-popup-tip {\n  box-shadow: none;\n}\n.fleet-map-root .leaflet-popup-close-button,\n.fleet-map-root .leaflet-popup-close-button span {\n  color: var(--muted-foreground);\n}\n.fleet-map-root .leaflet-popup-close-button:hover {\n  color: var(--foreground);\n}\n`;\n\n// --- LEAFLET DEFAULT ICON PATCH ---\n// This module is client-only: it imports leaflet's stylesheet at the top level,\n// which only a bundler can resolve. Load it with React.lazy (see `fleet-tracker`)\n// rather than importing it into a server-rendered tree.\n//\n// leaflet is imported statically rather than with require(): `require` does not\n// exist in an ESM bundle, so a Vite or Rollup consumer hit\n// \"ReferenceError: require is not defined\" the moment this component mounted.\nlet iconDefaultsPatched = false;\n\n/**\n * Suppress leaflet's default icon URL resolution, which assumes the images sit next to\n * the stylesheet -- bundlers rewrite that path. Runs once, after leaflet has loaded in\n * the browser, so nothing touches `window` during server prerender.\n */\nfunction patchIconDefaults(L: typeof LeafletNS) {\n  if (iconDefaultsPatched) return;\n  iconDefaultsPatched = true;\n  delete (L.Icon.Default.prototype as unknown as { _getIconUrl: unknown })._getIconUrl;\n  L.Icon.Default.mergeOptions({\n    iconRetinaUrl:\n      \"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png\",\n    iconUrl:\n      \"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png\",\n    shadowUrl:\n      \"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png\",\n  });\n}\n\nconst getIcon = (\n  L: typeof LeafletNS,\n  colorName: string,\n  className: string,\n): LeafletNS.Icon => {\n  return new L.Icon({\n    iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-${colorName}.png`,\n    shadowUrl:\n      \"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png\",\n    iconSize: [25, 41],\n    iconAnchor: [12, 41],\n    popupAnchor: [1, -34],\n    shadowSize: [41, 41],\n    className,\n  });\n};\n\nfunction MapController({\n  activeVehicle,\n  zoom = 15,\n}: {\n  activeVehicle: Vehicle | null;\n  zoom?: number;\n}) {\n  const map = useMap();\n  useEffect(() => {\n    if (\n      !activeVehicle ||\n      typeof activeVehicle.lat !== \"number\" ||\n      typeof activeVehicle.lng !== \"number\"\n    )\n      return;\n\n    const target: [number, number] = [activeVehicle.lat, activeVehicle.lng];\n\n    // On mobile the map pane is revealed in the same render that selects a\n    // vehicle, so Leaflet still holds the zero size it measured while hidden.\n    // flyTo divides by that size and ends up with NaN coordinates, so\n    // re-measure first and skip the animation until the pane has a real box.\n    map.invalidateSize({ animate: false, pan: false });\n    const size = map.getSize();\n    if (!size.x || !size.y) {\n      map.setView(target, zoom, { animate: false });\n      return;\n    }\n\n    map.flyTo(target, zoom, { duration: 1.5 });\n  }, [activeVehicle, map, zoom]);\n  return null;\n}\n\nfunction ZoomControls() {\n  const map = useMap();\n  return (\n    <div className=\"leaflet-bottom leaflet-right z-[1000] m-4 flex flex-col gap-1.5 pointer-events-auto\">\n      <button\n        type=\"button\"\n        title=\"Zoom In\"\n        aria-label=\"Zoom in\"\n        onClick={(e) => {\n          e.stopPropagation();\n          map.zoomIn();\n        }}\n        onDoubleClick={(e) => e.stopPropagation()}\n        className=\"w-8 h-8 rounded-lg bg-card/90 hover:bg-card text-foreground border border-border/80 backdrop-blur-md flex items-center justify-center transition-colors shadow-md cursor-pointer\"\n      >\n        <Plus className=\"w-4 h-4\" />\n      </button>\n      <button\n        type=\"button\"\n        title=\"Zoom Out\"\n        aria-label=\"Zoom out\"\n        onClick={(e) => {\n          e.stopPropagation();\n          map.zoomOut();\n        }}\n        onDoubleClick={(e) => e.stopPropagation()}\n        className=\"w-8 h-8 rounded-lg bg-card/90 hover:bg-card text-foreground border border-border/80 backdrop-blur-md flex items-center justify-center transition-colors shadow-md cursor-pointer\"\n      >\n        <Minus className=\"w-4 h-4\" />\n      </button>\n    </div>\n  );\n}\n\n// Leaflet only measures its container once; refit whenever the container\n// resizes (docs device preview, responsive layouts). The ResizeObserver must\n// come from the container's own window — a parent-realm observer never fires\n// for elements inside an iframe.\nfunction MapResizeHandler() {\n  const map = useMap();\n  useEffect(() => {\n    const container = map.getContainer();\n    const win = container.ownerDocument.defaultView ?? window;\n    const FrameResizeObserver =\n      (win as Window & typeof globalThis).ResizeObserver ?? ResizeObserver;\n    const observer = new FrameResizeObserver(() => map.invalidateSize());\n    observer.observe(container);\n    return () => observer.disconnect();\n  }, [map]);\n  return null;\n}\n\n// --- MAIN COMPONENT ---\n/**\n * Dark mode is an external system (a class on <html>), so it is read through\n * useSyncExternalStore rather than mirrored into state from an effect. The\n * server snapshot doubles as the \"not mounted yet\" signal.\n */\nconst subscribeToTheme = (onChange: () => void) => {\n  const observer = new MutationObserver(onChange);\n  observer.observe(document.documentElement, {\n    attributes: true,\n    attributeFilter: [\"class\"],\n  });\n  return () => observer.disconnect();\n};\nconst getIsDark = () => document.documentElement.classList.contains(\"dark\");\nconst getIsDarkOnServer = () => null;\n\nconst subscribeToNothing = () => () => {};\nconst useIsMounted = () =>\n  useSyncExternalStore(\n    subscribeToNothing,\n    () => true,\n    () => false,\n  );\n\nexport default function FleetTracker({\n  fleetData = [],\n  variant = \"default\",\n  color = \"primary\",\n  zoom = 15,\n  tileApiKey,\n  tileUrl,\n  darkTileUrl,\n  attribution,\n}: FleetTrackerProps) {\n  const isCustomDark = Boolean(darkTileUrl || tileApiKey);\n  const lightTileSource =\n    tileUrl ||\n    (tileApiKey\n      ? `https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png?key=${tileApiKey}`\n      : TILES.light);\n  const darkTileSource =\n    darkTileUrl ||\n    (tileApiKey\n      ? `https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png?key=${tileApiKey}`\n      : TILES.dark);\n  const tileAttribution =\n    attribution ||\n    (tileApiKey\n      ? '&copy; <a href=\"https://carto.com/\">CartoDB</a>'\n      : TILE_ATTRIBUTION);\n  // Custom hex injection logic\n  const isCustomHex = color?.startsWith(\"#\");\n  const customVars = isCustomHex\n    ? ({\n        \"--fleet-custom\": color,\n        \"--fleet-custom-10\": `${color}1a`,\n        \"--fleet-custom-20\": `${color}33`,\n        \"--fleet-custom-50\": `${color}80`,\n      } as React.CSSProperties)\n    : {};\n\n  const theme = isCustomHex\n    ? {\n        text: \"text-[var(--fleet-custom)]\",\n        bg: \"bg-[var(--fleet-custom)] hover:brightness-110 text-white\",\n        border: \"border-[var(--fleet-custom)]\",\n        lightBg: \"bg-[var(--fleet-custom-10)]\",\n        focusRing:\n          \"focus-within:ring-[var(--fleet-custom-20)] focus-within:border-[var(--fleet-custom-50)]\",\n        marker: \"black\", // Fallback marker for arbitrary colors\n      }\n    : colorThemes[color as string] || colorThemes[\"primary\"];\n\n  const styles = fleetVariants[variant];\n\n  // The pin halo is the only thing separating a marker from a dark basemap, so\n  // it tracks the active colour rather than sitting on a fixed neutral.\n  const rootVars = {\n    ...customVars,\n    \"--fleet-pin-glow\": isCustomHex ? `${color}bf` : theme.glow,\n  } as React.CSSProperties;\n\n  const mounted = useIsMounted();\n  const isDarkMode =\n    useSyncExternalStore(subscribeToTheme, getIsDark, getIsDarkOnServer) ??\n    false;\n  const [searchQuery, setSearchQuery] = useState(\"\");\n  const [activeVehicle, setActiveVehicle] = useState<Vehicle | null>(\n    fleetData[0] || null,\n  );\n  // Below md the list and the map share the same box, so only one is shown at a\n  // time. The list is the landing view; the header/floating buttons swap them.\n  const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(true);\n\n  // leaflet reaches for `window` as soon as its module body runs, so it is imported\n  // here rather than at the top of the file. On the server this stays null and the\n  // map subtree is skipped, which is what keeps `next build` prerender working.\n  const [leaflet, setLeaflet] = useState<typeof LeafletNS | null>(null);\n  const [rl, setRl] = useState<ReactLeaflet | null>(null);\n  useEffect(() => {\n    let cancelled = false;\n    Promise.all([import(\"leaflet\"), import(\"react-leaflet\")]).then(([L, RL]) => {\n      if (cancelled) return;\n      patchIconDefaults(L);\n      setLeaflet(L);\n      setRl(RL);\n    });\n    return () => {\n      cancelled = true;\n    };\n  }, []);\n\n  // Rebuilding an Icon swaps the underlying <img>, which restarts the active\n  // pin's animation -- so they are built once per colour, not once per render.\n  const icons = useMemo(() => {\n    if (!leaflet) return null;\n    return {\n      active: getIcon(leaflet, theme.marker, \"fleet-pin fleet-pin-active\"),\n      inactive: getIcon(leaflet, \"grey\", \"fleet-pin\"),\n    };\n  }, [leaflet, theme.marker]);\n\n  const filteredVehicles = fleetData.filter(\n    (v) =>\n      v.id.toLowerCase().includes(searchQuery.toLowerCase()) ||\n      v.location.toLowerCase().includes(searchQuery.toLowerCase()),\n  );\n\n  const getStatusStyle = (status?: string) => {\n    switch (status) {\n      case \"active\":\n        return \"text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10\";\n      case \"idle\":\n        return \"text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-500/10\";\n      case \"maintenance\":\n        return \"text-rose-600 dark:text-rose-400 bg-rose-50 dark:bg-rose-500/10\";\n      default:\n        return \"text-muted-foreground bg-muted\";\n    }\n  };\n\n  if (!mounted)\n    return (\n      <div className=\"w-full h-[600px] md:h-[700px] bg-muted rounded-2xl animate-pulse border border-border\" />\n    );\n\n  return (\n    <div\n      style={rootVars}\n      data-fleet-theme={isDarkMode ? \"dark\" : \"light\"}\n      className={cn(\n        \"fleet-map-root w-full h-[600px] md:h-[700px] font-sans transition-all duration-300\",\n        styles.wrapper,\n      )}\n    >\n      <style>{MAP_STYLES}</style>\n\n      {variant === \"modern\" && (\n        <div aria-hidden className=\"pointer-events-none absolute inset-0 -z-10\">\n          <span\n            style={{ background: AURORA[0] }}\n            className=\"absolute -left-32 -top-32 size-[32rem] rounded-full opacity-80 blur-3xl dark:opacity-35\"\n          />\n          <span\n            style={{ background: AURORA[1] }}\n            className=\"absolute -right-24 top-16 size-[28rem] rounded-full opacity-80 blur-3xl dark:opacity-35\"\n          />\n          <span\n            style={{ background: AURORA[2] }}\n            className=\"absolute -bottom-40 left-1/3 size-[32rem] rounded-full opacity-70 blur-3xl dark:opacity-30\"\n          />\n        </div>\n      )}\n\n      {/* Mobile: jump back to the vehicle list while the map is showing */}\n      {!isMobileSidebarOpen && (\n        <button\n          type=\"button\"\n          aria-label=\"Show vehicle list\"\n          onClick={() => setIsMobileSidebarOpen(true)}\n          className={cn(\n            \"md:hidden absolute top-4 left-4 z-[40] p-3 rounded-xl shadow-lg border border-border\",\n            theme.bg,\n          )}\n        >\n          <Menu className=\"w-5 h-5\" />\n        </button>\n      )}\n\n      {/* --- SIDEBAR --- */}\n      <aside\n        className={cn(\n          styles.sidebar,\n          isMobileSidebarOpen ? \"flex\" : \"hidden md:flex\",\n        )}\n      >\n        <div className={cn(\"p-5 shrink-0 border-b\", styles.header)}>\n          <div className=\"flex items-center justify-between mb-5\">\n            <div className=\"flex items-center gap-3\">\n              <div\n                className={cn(\n                  \"w-10 h-10 rounded-xl flex items-center justify-center shadow-sm\",\n                  theme.bg,\n                )}\n              >\n                <Navigation2 className={cn(\"w-5 h-5\", theme.bg.includes(\"text-\") ? theme.bg.split(\" \").filter(c => c.startsWith(\"text-\") || c.startsWith(\"dark:text-\")).join(\" \") : \"text-primary-foreground\")} />\n              </div>\n              <div>\n                <h2 className=\"font-semibold text-lg text-foreground leading-tight\">\n                  Fleet Command\n                </h2>\n                <p className=\"text-xs font-medium text-muted-foreground\">\n                  {filteredVehicles.length} Vehicles Active\n                </p>\n              </div>\n            </div>\n            {/* Mobile: swap the list out for the map */}\n            <button\n              type=\"button\"\n              aria-label=\"Show map\"\n              className=\"md:hidden text-muted-foreground hover:text-foreground\"\n              onClick={() => setIsMobileSidebarOpen(false)}\n            >\n              <MapIcon className=\"w-5 h-5\" />\n            </button>\n          </div>\n\n          <div\n            className={cn(\n              \"relative w-full rounded-xl flex items-center px-3 py-2.5 transition-all\",\n              styles.search,\n              theme.focusRing,\n            )}\n          >\n            <Search className=\"w-4 h-4 text-muted-foreground shrink-0\" />\n            <input\n              type=\"text\"\n              placeholder=\"Search vehicles...\"\n              value={searchQuery}\n              onChange={(e) => setSearchQuery(e.target.value)}\n              className=\"flex-1 bg-transparent border-none focus:outline-none text-sm ml-2 text-foreground placeholder:text-muted-foreground\"\n            />\n          </div>\n        </div>\n\n        <div className=\"flex-1 overflow-y-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none] py-3\">\n          {filteredVehicles.map((vehicle) => {\n            const isActive = activeVehicle?.id === vehicle.id;\n            return (\n              <div\n                key={vehicle.id}\n                onClick={() => {\n                  setActiveVehicle(vehicle);\n                  // On mobile, reveal the map so the fly-to is actually visible.\n                  if (window.innerWidth < 768) setIsMobileSidebarOpen(false);\n                }}\n                className={cn(\n                  \"px-5 py-4 cursor-pointer transition-all flex items-start gap-4\",\n                  isActive\n                    ? cn(\n                        styles.itemActive,\n                        variant === \"default\" ? theme.border : \"\",\n                        variant === \"default\" ? theme.lightBg : \"\",\n                      )\n                    : styles.itemInactive,\n                )}\n              >\n                <div\n                  className={cn(\n                    \"p-2.5 rounded-lg shrink-0 transition-colors shadow-sm\",\n                    isActive\n                      ? theme.bg\n                      : variant === \"modern\"\n                        ? \"bg-card/60 border border-foreground/10\"\n                        : \"bg-card border border-border\",\n                  )}\n                >\n                  <Truck\n                    className={cn(\n                      \"w-5 h-5\",\n                      isActive\n                        ? theme.bg.includes(\"text-\") ? theme.bg.split(\" \").filter(c => c.startsWith(\"text-\") || c.startsWith(\"dark:text-\")).join(\" \") : \"text-primary-foreground\"\n                        : \"text-muted-foreground\",\n                    )}\n                  />\n                </div>\n                <div className=\"flex-1 min-w-0\">\n                  <div className=\"flex justify-between items-start mb-1\">\n                    <h4\n                      className={cn(\n                        \"font-semibold text-sm truncate\",\n                        isActive\n                          ? variant === \"default\"\n                            ? theme.text\n                            : \"text-foreground\"\n                          : \"text-foreground\",\n                      )}\n                    >\n                      {vehicle.id}\n                    </h4>\n                    <span\n                      className={cn(\n                        \"text-[10px] font-semibold px-2 py-0.5 rounded-md uppercase tracking-wider\",\n                        getStatusStyle(vehicle.status),\n                      )}\n                    >\n                      {vehicle.status || \"Active\"}\n                    </span>\n                  </div>\n                  <div className=\"flex items-center gap-1.5 text-muted-foreground mb-1.5\">\n                    <MapPin className=\"w-3 h-3 shrink-0\" />\n                    <p className=\"text-xs truncate\">{vehicle.location}</p>\n                  </div>\n                  {vehicle.speed && (\n                    <p className=\"text-[11px] font-medium text-muted-foreground\">\n                      Speed: {vehicle.speed}\n                    </p>\n                  )}\n                </div>\n              </div>\n            );\n          })}\n        </div>\n      </aside>\n\n      {/* --- LEAFLET MAP AREA --- */}\n      <main\n        className={cn(\n          styles.mapContainer,\n          isMobileSidebarOpen ? \"hidden md:block\" : \"block\",\n        )}\n      >\n        {leaflet && rl && icons && (\n          <rl.MapContainer\n            center={\n              activeVehicle\n                ? [activeVehicle.lat, activeVehicle.lng]\n                : fleetData[0]\n                  ? [fleetData[0].lat, fleetData[0].lng]\n                  : [40.7465, -74.0014]\n            }\n            zoom={zoom}\n            className=\"w-full h-full z-0\"\n            zoomControl={false}\n            attributionControl={false}\n          >\n            {isDarkMode ? (\n              <rl.TileLayer\n                key=\"dark\"\n                className={cn(\n                  \"fleet-tiles-base\",\n                  !isCustomDark && \"fleet-tiles-osm-dark\",\n                )}\n                attribution={tileAttribution}\n                url={darkTileSource}\n                zIndex={1}\n              />\n            ) : (\n              <rl.TileLayer\n                key=\"light\"\n                className=\"fleet-tiles-base\"\n                attribution={tileAttribution}\n                url={lightTileSource}\n                zIndex={1}\n              />\n            )}\n            <MapController activeVehicle={activeVehicle} zoom={zoom} />\n            <MapResizeHandler />\n            <ZoomControls />\n            {fleetData.map((vehicle) => {\n              const isActive = activeVehicle?.id === vehicle.id;\n              return (\n                <rl.Marker\n                  key={vehicle.id}\n                  position={[vehicle.lat, vehicle.lng]}\n                  icon={isActive ? icons.active : icons.inactive}\n                  // Lift the selected pin so its halo is not clipped by a\n                  // neighbouring marker drawn after it.\n                  zIndexOffset={isActive ? 1000 : 0}\n                  eventHandlers={{ click: () => setActiveVehicle(vehicle) }}\n                >\n                  <rl.Popup className=\"font-sans\">\n                    <div className=\"text-center\">\n                      <h3 className=\"font-semibold text-sm text-foreground m-0 leading-tight\">\n                        {vehicle.id}\n                      </h3>\n                      <p className=\"text-xs text-muted-foreground mt-1 m-0\">\n                        {vehicle.location}\n                      </p>\n                    </div>\n                  </rl.Popup>\n                </rl.Marker>\n              );\n            })}\n          </rl.MapContainer>\n        )}\n      </main>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/custom/blocks/Fleet-map.tsx"
    }
  ],
  "docs": "Named exports: FleetTracker, Vehicle. `fleetData` take { id, location, lat, lng, status, speed? } with status active | idle | maintenance. **Import Leaflet's CSS yourself** — `import \"leaflet/dist/leaflet.css\"` — or the map renders as broken tiles. Leaflet touches `window`, so load it client-side only.",
  "categories": [
    "maps",
    "data-display"
  ],
  "type": "registry:component"
}
