{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "leaflet-map",
  "title": "Leaflet Map",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/leaflet-map/leaflet-map.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport type * as L from 'leaflet'\nimport { useTheme } from 'next-themes'\nimport { cn } from '@/lib/utils'\nimport {\n  leafletMapVariants,\n  LEAFLET_TILES,\n  LEAFLET_THEME_TILES,\n  type LeafletMapVariant,\n  type LeafletMapVariants,\n  type LeafletTilePreset,\n} from './leaflet-map.variants'\nimport 'leaflet/dist/leaflet.css'\nimport './leaflet-map.css'\n\ntype LeafletModule = typeof import('leaflet')\nlet leafletPromise: Promise<LeafletModule> | null = null\n\n/** Leaflet touches `window`/`document` at import time — load it lazily so SSR\n *  renders never evaluate the module. */\nfunction loadLeaflet(): Promise<LeafletModule> {\n  if (!leafletPromise) leafletPromise = import('leaflet')\n  return leafletPromise\n}\n\n/** [lng, lat] (Mapbox order, matching the `map` component) -> Leaflet [lat, lng]. */\nfunction toLatLng(c: [number, number]): L.LatLngExpression {\n  return [c[1], c[0]]\n}\nfunction toLatLngs(path: [number, number][] | [number, number][][]): L.LatLngExpression[] | L.LatLngExpression[][] {\n  if (!path.length) return []\n  return Array.isArray(path[0][0])\n    ? (path as [number, number][][]).map((ring) => ring.map(toLatLng))\n    : (path as [number, number][]).map(toLatLng)\n}\nfunction toLatLngBounds(bounds: [[number, number], [number, number]]): L.LatLngBoundsExpression {\n  return [toLatLng(bounds[0]), toLatLng(bounds[1])] as L.LatLngBoundsExpression\n}\ntype LeafletPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n\n/** Leaflet merges options by assignment, so an explicit `undefined` clobbers\n *  its defaults (e.g. `subdomains: 'abc'` -> crash, `icon: undefined` kills the\n *  default pin). Strip undefined keys before handing options to Leaflet. */\nfunction defined<T extends object>(o: T): T {\n  return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined)) as T\n}\n\nconst LEAFLET_ICON_BASE = 'https://unpkg.com/leaflet@1.9.4/dist/images'\nlet defaultIconFixed = false\n/** Leaflet's default pin references image paths bundlers can't resolve. */\nfunction fixDefaultLeafletIcon(L: LeafletModule) {\n  if (defaultIconFixed) return\n  defaultIconFixed = true\n  L.Icon.Default.mergeOptions({\n    iconRetinaUrl: `${LEAFLET_ICON_BASE}/marker-icon-2x.png`,\n    iconUrl: `${LEAFLET_ICON_BASE}/marker-icon.png`,\n    shadowUrl: `${LEAFLET_ICON_BASE}/marker-shadow.png`,\n  })\n}\n\nconst LeafletMapContext = React.createContext<L.Map | null>(null)\nconst LeafletLayerContext = React.createContext<L.Layer | null>(null)\n\n/** The L.Map instance from the enclosing <LeafletMap> — null until created. */\nexport function useLeafletMap() {\n  return React.useContext(LeafletMapContext)\n}\n\nexport interface LeafletFlyToOptions {\n  /** [lng, lat] — Mapbox order. */\n  center?: [number, number]\n  zoom?: number\n  /** Milliseconds (converted to Leaflet's seconds). */\n  duration?: number\n}\nexport interface LeafletViewOptions {\n  center?: [number, number]\n  zoom?: number\n}\n\n/** Imperative handle published by <LeafletMap> — mirrors the `map`\n *  component's MapRef surface: camera helpers plus the raw L.Map. */\nexport interface LeafletMapRef {\n  readonly map: L.Map | null\n  getMap(): L.Map | null\n  flyTo(options?: LeafletFlyToOptions): void\n  setView(options?: LeafletViewOptions): void\n  jumpTo(options?: LeafletViewOptions): void\n  fitBounds(bounds: [[number, number], [number, number]] | L.LatLngBoundsExpression, options?: L.FitBoundsOptions): void\n  panTo(center: [number, number]): void\n  zoomIn(): void\n  zoomOut(): void\n  resize(): void\n}\n\nexport interface LeafletMapProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Named raster basemap preset ('streets' | 'outdoors' | 'satellite' | 'satellite-streets' | 'light' | 'dark' | 'navigation-day' | 'navigation-night' | 'standard' | 'muted' | 'default'). */\n  variant?: LeafletMapVariant\n  /** Height preset. Omit to size via `className` (blocks typically pass `size-full`). */\n  size?: LeafletMapVariants['size']\n  /** Custom raster tile URL template — overrides `variant`. */\n  tileUrl?: string\n  /** Attribution HTML for a custom `tileUrl`. Defaults to the OpenStreetMap credit. */\n  tileAttribution?: string\n  /** Tile subdomains for a custom `tileUrl` ('abcd' or ['a','b']). */\n  tileSubdomains?: string | string[]\n  /** Initial [lng, lat] — Mapbox order, matching the `map` component. */\n  center?: [number, number]\n  zoom?: number\n  minZoom?: number\n  /** Caps the map's max zoom. Defaults to the tile provider's own maxZoom. */\n  maxZoom?: number\n  /** Show the zoom control. */\n  navigation?: boolean\n  /** Placement of the zoom control ('top-left' | 'top-right' | 'bottom-left' | 'bottom-right'). */\n  navigationPosition?: LeafletPosition\n  /** Show the HTML5 fullscreen toggle button. */\n  fullscreen?: boolean\n  /** Placement of the fullscreen button. Defaults to 'top-right'. */\n  fullscreenPosition?: LeafletPosition\n  /** Show tile credits behind a ⓘ button (reveals on hover/tap). Keep on — OSM/Esri tiles require attribution. */\n  attribution?: boolean\n  /** Wheel zoom. Set false for maps embedded in scrollable pages. */\n  scrollWheelZoom?: boolean\n  /** Desaturate the tile pane to a quiet canvas (markers stay coloured). */\n  muted?: boolean\n  /** Hands you the raw Leaflet Map once it is created. */\n  onCreated?: (map: L.Map) => void\n}\n\nconst LeafletMapComponent = React.forwardRef<LeafletMapRef, LeafletMapProps>(\n  (\n    {\n      className,\n      variant = 'default',\n      size,\n      tileUrl,\n      tileAttribution,\n      tileSubdomains,\n      center = [0, 20],\n      zoom = 2,\n      minZoom,\n      maxZoom,\n      navigation = true,\n      navigationPosition = 'bottom-right',\n      fullscreen = false,\n      fullscreenPosition = 'top-right',\n      attribution = true,\n      scrollWheelZoom = true,\n      muted = false,\n      onCreated,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const { resolvedTheme } = useTheme()\n    const [htmlDark, setHtmlDark] = React.useState(false)\n    const isDark = resolvedTheme === 'dark' || htmlDark\n    const isMuted = muted || variant === 'muted'\n\n    const resolvedTiles = React.useMemo<LeafletTilePreset>(() => {\n      if (tileUrl) {\n        return {\n          url: tileUrl,\n          attribution:\n            tileAttribution ??\n            '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors',\n          subdomains: tileSubdomains,\n          maxZoom,\n        }\n      }\n      if (\n        variant &&\n        variant !== 'default' &&\n        variant !== 'muted' &&\n        LEAFLET_TILES[variant as keyof typeof LEAFLET_TILES]\n      ) {\n        return LEAFLET_TILES[variant as keyof typeof LEAFLET_TILES]\n      }\n      return isDark ? LEAFLET_THEME_TILES.dark : LEAFLET_THEME_TILES.light\n    }, [tileUrl, tileAttribution, tileSubdomains, maxZoom, variant, isDark])\n\n    const [mounted, setMounted] = React.useState(false)\n    const [inView, setInView] = React.useState(false)\n    const [isFullscreen, setIsFullscreen] = React.useState(false)\n    const [canZoomIn, setCanZoomIn] = React.useState(true)\n    const [canZoomOut, setCanZoomOut] = React.useState(true)\n    const containerRef = React.useRef<HTMLDivElement>(null)\n    const mapElRef = React.useRef<HTMLDivElement>(null)\n    const mapRef = React.useRef<L.Map | null>(null)\n    const leafletRef = React.useRef<LeafletModule | null>(null)\n    const baseLayerRef = React.useRef<L.TileLayer | null>(null)\n    const overlayLayerRef = React.useRef<L.TileLayer | null>(null)\n    const [mapReady, setMapReady] = React.useState(false)\n    const [attributions, setAttributions] = React.useState<string[]>([])\n    const [showAttribution, setShowAttribution] = React.useState(false)\n\n    const syncZoomBounds = React.useCallback(() => {\n      const m = mapRef.current\n      if (!m) return\n      setCanZoomIn(m.getZoom() < m.getMaxZoom())\n      setCanZoomOut(m.getZoom() > m.getMinZoom())\n    }, [])\n\n    // Collect attribution strings from every layer (base tiles, overlays,\n    // custom LeafletTileLayers) — deduped, rendered by the ⓘ popover.\n    const collectAttributions = React.useCallback(() => {\n      const m = mapRef.current\n      if (!m) return\n      const seen = new Set<string>()\n      m.eachLayer((layer) => {\n        const a = (layer as L.TileLayer).options?.attribution\n        if (typeof a === 'string' && a) seen.add(a)\n      })\n      setAttributions([...seen])\n    }, [])\n\n    const applyTiles = React.useCallback((tiles: LeafletTilePreset) => {\n      const m = mapRef.current\n      const L = leafletRef.current\n      if (!m || !L) return\n      baseLayerRef.current?.remove()\n      baseLayerRef.current = null\n      overlayLayerRef.current?.remove()\n      overlayLayerRef.current = null\n      baseLayerRef.current = L.tileLayer(\n        tiles.url,\n        defined({\n          attribution: tiles.attribution,\n          subdomains: tiles.subdomains,\n          maxZoom: tiles.maxZoom ?? 19,\n        }),\n      )\n      baseLayerRef.current.addTo(m)\n      if (tiles.overlayUrl) {\n        overlayLayerRef.current = L.tileLayer(tiles.overlayUrl, { maxZoom: tiles.maxZoom ?? 19 })\n        overlayLayerRef.current.addTo(m)\n      }\n    }, [])\n\n    React.useEffect(() => {\n      const root = document.documentElement\n      const sync = () => setHtmlDark(root.classList.contains('dark'))\n      sync()\n      setMounted(true)\n      const obs = new MutationObserver(sync)\n      obs.observe(root, { attributes: true, attributeFilter: ['class'] })\n      const onFs = () => setIsFullscreen(Boolean(document.fullscreenElement))\n      document.addEventListener('fullscreenchange', onFs)\n      return () => {\n        obs.disconnect()\n        document.removeEventListener('fullscreenchange', onFs)\n      }\n    }, [])\n\n    React.useEffect(() => {\n      const el = containerRef.current\n      if (!el || typeof IntersectionObserver === 'undefined') {\n        setInView(true)\n        return\n      }\n      const io = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), {\n        rootMargin: '160px',\n        threshold: 0.01,\n      })\n      io.observe(el)\n      return () => io.disconnect()\n    }, [])\n\n    React.useEffect(() => {\n      const el = containerRef.current\n      if (!el || typeof ResizeObserver === 'undefined') return\n      const ro = new ResizeObserver(() => mapRef.current?.invalidateSize())\n      ro.observe(el)\n      return () => ro.disconnect()\n    }, [mounted])\n\n    React.useEffect(() => {\n      if (!mounted || !inView || mapRef.current) return\n      let cancelled = false\n      loadLeaflet().then((L) => {\n        if (cancelled || mapRef.current || !mapElRef.current) return\n        leafletRef.current = L\n        fixDefaultLeafletIcon(L)\n        const tiles = resolvedTiles\n        const m = L.map(\n          mapElRef.current,\n          defined({\n            center: toLatLng(center),\n            zoom,\n            minZoom,\n            maxZoom: maxZoom ?? tiles.maxZoom,\n            zoomControl: false,\n            attributionControl: false,\n            scrollWheelZoom,\n          }),\n        )\n        mapRef.current = m\n        applyTiles(tiles)\n        m.on('zoomend', syncZoomBounds)\n        syncZoomBounds()\n        m.on('layeradd layerremove', collectAttributions)\n        collectAttributions()\n        setMapReady(true)\n        onCreated?.(m)\n      })\n      return () => {\n        cancelled = true\n      }\n      // Map creation is mount-once; later prop changes flow through the\n      // watchers below.\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [mounted, inView])\n\n    React.useEffect(() => {\n      if (mapRef.current) applyTiles(resolvedTiles)\n    }, [resolvedTiles, applyTiles])\n\n    React.useEffect(() => {\n      const m = mapRef.current\n      if (m && center) m.setView(toLatLng(center), zoom)\n    }, [center, zoom])\n\n    React.useEffect(() => {\n      if (!attribution) setShowAttribution(false)\n    }, [attribution])\n\n    React.useEffect(() => {\n      const m = mapRef.current\n      if (!m) return\n      if (scrollWheelZoom) m.scrollWheelZoom.enable()\n      else m.scrollWheelZoom.disable()\n    }, [scrollWheelZoom])\n\n    React.useEffect(\n      () => () => {\n        mapRef.current?.remove()\n        mapRef.current = null\n      },\n      [],\n    )\n\n    React.useImperativeHandle(\n      ref,\n      () => ({\n        get map() {\n          return mapRef.current\n        },\n        getMap: () => mapRef.current,\n        flyTo: (options = {}) => {\n          const m = mapRef.current\n          if (!m) return\n          m.flyTo(options.center ? toLatLng(options.center) : m.getCenter(), options.zoom ?? m.getZoom(), {\n            duration: (options.duration ?? 800) / 1000,\n          })\n        },\n        setView: (options = {}) => {\n          const m = mapRef.current\n          if (!m) return\n          m.setView(options.center ? toLatLng(options.center) : m.getCenter(), options.zoom ?? m.getZoom())\n        },\n        jumpTo: (options = {}) => {\n          const m = mapRef.current\n          if (!m) return\n          m.setView(options.center ? toLatLng(options.center) : m.getCenter(), options.zoom ?? m.getZoom(), {\n            animate: false,\n          })\n        },\n        fitBounds: (bounds, options) =>\n          mapRef.current?.fitBounds(toLatLngBounds(bounds as [[number, number], [number, number]]), options),\n        panTo: (c) => mapRef.current?.panTo(toLatLng(c)),\n        zoomIn: () => mapRef.current?.zoomIn(),\n        zoomOut: () => mapRef.current?.zoomOut(),\n        resize: () => mapRef.current?.invalidateSize(),\n      }),\n      [mapReady],\n    )\n\n    // Zoom/fullscreen chrome is plain HTML overlaid on the map (like Mapbox's\n    // controls) — a corner stack per occupied corner, zoom group above fullscreen.\n    const cornerClasses: Record<LeafletPosition, string> = {\n      'top-left': 'left-3 top-3',\n      'top-right': 'right-3 top-3',\n      // above the ⓘ button (bottom-left) when credits are shown\n      'bottom-left': attribution && attributions.length ? 'bottom-9 left-3' : 'bottom-3 left-3',\n      'bottom-right': 'bottom-3 right-3',\n    }\n    const cornerOrder: LeafletPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right']\n    const navPosition = navigationPosition ?? 'bottom-right'\n    const fsPosition = fullscreenPosition ?? 'top-right'\n\n    return (\n      <div\n        ref={containerRef}\n        data-uipkge=\"\"\n        data-slot=\"leaflet-map\"\n        data-variant={variant}\n        data-muted={isMuted}\n        className={cn(leafletMapVariants({ variant, ...(size ? { size } : {}) }), className)}\n        {...props}\n      >\n        {/* className must stay constant — React rewrites the attribute when\n            the value changes and would wipe the classes Leaflet adds\n            (leaflet-container, leaflet-touch, …). The empty pre-mount div is\n            invisible regardless. */}\n        <div ref={mapElRef} className=\"size-full\" />\n        {cornerOrder.map((corner) => {\n          const showZoom = mapReady && navigation && navPosition === corner\n          const showFullscreen = mapReady && fullscreen && fsPosition === corner\n          if (!showZoom && !showFullscreen) return null\n          return (\n            <div key={corner} className={cn('absolute z-[1000] flex flex-col gap-2.5', cornerClasses[corner])}>\n              {showZoom && (\n                <div className=\"border-border bg-card divide-border flex flex-col divide-y overflow-hidden rounded-lg border shadow-sm\">\n                  <button\n                    type=\"button\"\n                    className=\"text-muted-foreground hover:bg-muted flex size-8 items-center justify-center transition-colors disabled:pointer-events-none disabled:opacity-40\"\n                    aria-label=\"Zoom in\"\n                    disabled={!canZoomIn}\n                    onClick={() => mapRef.current?.zoomIn()}\n                  >\n                    <svg className=\"size-full\" viewBox=\"0 0 29 29\" fill=\"currentColor\" aria-hidden=\"true\">\n                      <path d=\"M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z\" />\n                    </svg>\n                  </button>\n                  <button\n                    type=\"button\"\n                    className=\"text-muted-foreground hover:bg-muted flex size-8 items-center justify-center transition-colors disabled:pointer-events-none disabled:opacity-40\"\n                    aria-label=\"Zoom out\"\n                    disabled={!canZoomOut}\n                    onClick={() => mapRef.current?.zoomOut()}\n                  >\n                    <svg className=\"size-full\" viewBox=\"0 0 29 29\" fill=\"currentColor\" aria-hidden=\"true\">\n                      <path d=\"M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z\" />\n                    </svg>\n                  </button>\n                </div>\n              )}\n              {showFullscreen && (\n                <button\n                  type=\"button\"\n                  className=\"border-border bg-card text-muted-foreground hover:bg-muted flex size-8 items-center justify-center rounded-lg border shadow-sm transition-colors\"\n                  aria-label=\"Toggle fullscreen\"\n                  onClick={() => {\n                    const el = containerRef.current\n                    if (!el) return\n                    if (document.fullscreenElement) document.exitFullscreen()\n                    else el.requestFullscreen?.()\n                  }}\n                >\n                  <svg className=\"size-full\" viewBox=\"0 0 29 29\" fill=\"currentColor\" aria-hidden=\"true\">\n                    {isFullscreen ? (\n                      <path d=\"M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z\" />\n                    ) : (\n                      <path d=\"M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z\" />\n                    )}\n                  </svg>\n                </button>\n              )}\n            </div>\n          )\n        })}\n        {/* Tile credits behind a Mapbox-style ⓘ button: hover reveals on\n            desktop, tap toggles on touch. Keep visible — OSM/Esri tiles\n            require credit. */}\n        {mapReady && attribution && attributions.length > 0 && (\n          <div className=\"group absolute bottom-3 left-3 z-[1000] flex flex-col items-start gap-1.5\">\n            <div\n              role=\"note\"\n              className={cn(\n                'border-border bg-popover text-popover-foreground max-w-64 rounded-md border px-2.5 py-1.5 text-[11px] leading-relaxed shadow-md transition-opacity [&_a]:underline',\n                showAttribution\n                  ? 'visible opacity-100'\n                  : 'invisible opacity-0 group-hover:visible group-hover:opacity-100',\n              )}\n              dangerouslySetInnerHTML={{ __html: attributions.join(' | ') }}\n            />\n            <button\n              type=\"button\"\n              className=\"border-border bg-card text-muted-foreground hover:bg-muted hover:text-foreground flex size-4 items-center justify-center rounded-full border shadow-xs transition-colors\"\n              aria-label=\"Map data attribution\"\n              aria-expanded={showAttribution}\n              onClick={() => setShowAttribution((v) => !v)}\n            >\n              <svg\n                className=\"size-3\"\n                viewBox=\"0 0 24 24\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                strokeWidth=\"2\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                aria-hidden=\"true\"\n              >\n                <circle cx=\"12\" cy=\"12\" r=\"10\" />\n                <path d=\"M12 16v-4M12 8h.01\" />\n              </svg>\n            </button>\n          </div>\n        )}\n        {mapReady && <LeafletMapContext.Provider value={mapRef.current}>{children}</LeafletMapContext.Provider>}\n      </div>\n    )\n  },\n)\nLeafletMapComponent.displayName = 'LeafletMap'\n\n/** Shared layer lifecycle: build once the enclosing map exists, add to it,\n *  remove on unmount, and republish through LeafletLayerContext so nested\n *  popups/tooltips can bind. */\nfunction useLeafletLayer<T extends L.Layer>(build: (L: LeafletModule) => T): T | null {\n  const map = useLeafletMap()\n  const [layer, setLayer] = React.useState<T | null>(null)\n  const buildRef = React.useRef(build)\n  buildRef.current = build\n  React.useEffect(() => {\n    if (!map) return\n    let cancelled = false\n    let instance: T | null = null\n    loadLeaflet().then((L) => {\n      if (cancelled) return\n      instance = buildRef.current(L)\n      instance.addTo(map)\n      setLayer(instance)\n    })\n    return () => {\n      cancelled = true\n      instance?.remove()\n      setLayer(null)\n    }\n  }, [map])\n  return layer\n}\n\nfunction useLatest<T>(value: T) {\n  const r = React.useRef(value)\n  r.current = value\n  return r\n}\n\ntype MarkerAnchor =\n  | 'center'\n  | 'top'\n  | 'bottom'\n  | 'left'\n  | 'right'\n  | 'top-left'\n  | 'top-right'\n  | 'bottom-left'\n  | 'bottom-right'\n\nexport interface LeafletMarkerProps {\n  /** [lng, lat] — Mapbox order, matching the `map` component's MapMarker. */\n  lngLat: [number, number]\n  /** Which edge/corner of the marker content sits on the coordinate. */\n  anchor?: MarkerAnchor\n  draggable?: boolean\n  opacity?: number\n  zIndexOffset?: number\n  title?: string\n  alt?: string\n  onClick?: (e: L.LeafletMouseEvent) => void\n  children?: React.ReactNode\n}\n\n/** Marker with real-DOM custom content: children portal into a div icon, so\n *  event handlers and state keep working. LeafletPopup/LeafletTooltip children\n *  bind to the marker instead of becoming icon content. */\nconst LeafletMarkerComponent = ({\n  lngLat,\n  anchor = 'center',\n  draggable,\n  opacity,\n  zIndexOffset,\n  title,\n  alt,\n  onClick,\n  children,\n}: LeafletMarkerProps) => {\n  const map = useLeafletMap()\n  const [iconEl, setIconEl] = React.useState<HTMLElement | null>(null)\n  const [marker, setMarker] = React.useState<L.Marker | null>(null)\n  const onClickRef = useLatest(onClick)\n\n  const kids = React.Children.toArray(children)\n  const overlays = kids.filter(\n    (k) => React.isValidElement(k) && (k.type === LeafletPopupComponent || k.type === LeafletTooltipComponent),\n  )\n  const htmlKids = kids.filter((k) => !overlays.includes(k))\n  const hasHtml = htmlKids.length > 0\n  const hasHtmlRef = useLatest(hasHtml)\n\n  React.useEffect(() => {\n    if (!map) return\n    let cancelled = false\n    let m: L.Marker | null = null\n    loadLeaflet().then((L) => {\n      if (cancelled) return\n      let el: HTMLElement | undefined\n      if (hasHtmlRef.current) {\n        el = document.createElement('div')\n        el.className = 'uipkge-leaflet-anchor'\n        el.dataset.anchor = anchor\n      }\n      m = L.marker(\n        toLatLng(lngLat),\n        defined({\n          icon: el ? L.divIcon({ className: 'uipkge-leaflet-div-icon', html: el }) : undefined,\n          interactive: true,\n          draggable,\n          opacity,\n          zIndexOffset,\n          title,\n          alt,\n        }),\n      )\n      m.on('click', (ev) => onClickRef.current?.(ev))\n      m.addTo(map)\n      setMarker(m)\n      if (el) setIconEl(el)\n    })\n    return () => {\n      cancelled = true\n      m?.remove()\n      setMarker(null)\n      setIconEl(null)\n    }\n    // Marker builds once per map; prop changes flow through the effects below.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [map])\n\n  React.useEffect(() => {\n    marker?.setLatLng(toLatLng(lngLat))\n  }, [marker, lngLat?.[0], lngLat?.[1]])\n  React.useEffect(() => {\n    if (opacity !== undefined) marker?.setOpacity(opacity)\n  }, [marker, opacity])\n  React.useEffect(() => {\n    if (zIndexOffset !== undefined) marker?.setZIndexOffset(zIndexOffset)\n  }, [marker, zIndexOffset])\n\n  return (\n    <LeafletLayerContext.Provider value={marker}>\n      {overlays}\n      {iconEl ? createPortal(htmlKids, iconEl) : null}\n    </LeafletLayerContext.Provider>\n  )\n}\nLeafletMarkerComponent.displayName = 'LeafletMarker'\n\nexport interface LeafletPopupProps {\n  /** [lng, lat] — standalone popup on the map. Omit inside a layer to bind to it. */\n  lngLat?: [number, number]\n  maxWidth?: number\n  /** Minimum popup width. Defaults to 200 — keeps card-style content from collapsing narrow. */\n  minWidth?: number\n  offset?: [number, number]\n  className?: string\n  autoClose?: boolean\n  closeOnClick?: boolean\n  closeButton?: boolean\n  keepInView?: boolean\n  children?: React.ReactNode\n}\n\n/** Popup bound to the nearest ancestor layer, or opened standalone at `lngLat`.\n *  Children portal into the popup's real content node. */\nconst LeafletPopupComponent = ({ lngLat, minWidth = 200, children, ...options }: LeafletPopupProps) => {\n  const map = useLeafletMap()\n  const parent = React.useContext(LeafletLayerContext)\n  const [contentEl, setContentEl] = React.useState<HTMLElement | null>(null)\n  const optionsRef = useLatest(defined({ ...options, minWidth }))\n\n  React.useEffect(() => {\n    if (!map) return\n    const el = document.createElement('div')\n    el.className = 'uipkge-leaflet-popup-src'\n    setContentEl(el)\n    let cancelled = false\n    let popup: L.Popup | null = null\n    let boundTo: L.Layer | null = null\n    loadLeaflet().then((L) => {\n      if (cancelled) return\n      if (parent) {\n        boundTo = parent\n        parent.bindPopup(el, optionsRef.current as L.PopupOptions)\n      } else if (lngLat) {\n        popup = L.popup(optionsRef.current as L.PopupOptions)\n          .setLatLng(toLatLng(lngLat))\n          .setContent(el)\n        popup.openOn(map)\n      }\n    })\n    return () => {\n      cancelled = true\n      try {\n        boundTo?.unbindPopup()\n        popup?.remove()\n      } catch {\n        /* map already destroyed */\n      }\n      setContentEl(null)\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [map, parent])\n\n  return contentEl ? createPortal(children, contentEl) : null\n}\nLeafletPopupComponent.displayName = 'LeafletPopup'\n\nexport interface LeafletTooltipProps {\n  /** [lng, lat] — standalone tooltip on the map. Omit inside a layer to bind to it. */\n  lngLat?: [number, number]\n  offset?: [number, number]\n  direction?: 'top' | 'bottom' | 'left' | 'right' | 'center' | 'auto'\n  permanent?: boolean\n  sticky?: boolean\n  opacity?: number\n  className?: string\n  interactive?: boolean\n  children?: React.ReactNode\n}\n\n/** Tooltip bound to the nearest ancestor layer, or standalone at `lngLat`. */\nconst LeafletTooltipComponent = ({ lngLat, children, ...options }: LeafletTooltipProps) => {\n  const map = useLeafletMap()\n  const parent = React.useContext(LeafletLayerContext)\n  const [contentEl, setContentEl] = React.useState<HTMLElement | null>(null)\n  const optionsRef = useLatest(options)\n\n  React.useEffect(() => {\n    if (!map) return\n    const el = document.createElement('div')\n    el.className = 'uipkge-leaflet-tooltip-src'\n    setContentEl(el)\n    let cancelled = false\n    let tooltip: L.Tooltip | null = null\n    let boundTo: L.Layer | null = null\n    loadLeaflet().then((L) => {\n      if (cancelled) return\n      if (parent) {\n        boundTo = parent\n        parent.bindTooltip(el, optionsRef.current as L.TooltipOptions)\n      } else if (lngLat) {\n        tooltip = L.tooltip(optionsRef.current as L.TooltipOptions)\n          .setLatLng(toLatLng(lngLat))\n          .setContent(el)\n        tooltip.addTo(map)\n      }\n    })\n    return () => {\n      cancelled = true\n      try {\n        boundTo?.unbindTooltip()\n        tooltip?.remove()\n      } catch {\n        /* map already destroyed */\n      }\n      setContentEl(null)\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [map, parent])\n\n  return contentEl ? createPortal(children, contentEl) : null\n}\nLeafletTooltipComponent.displayName = 'LeafletTooltip'\n\nexport interface LeafletPathProps {\n  color?: string\n  weight?: number\n  opacity?: number\n  lineCap?: 'butt' | 'round' | 'square'\n  lineJoin?: 'miter' | 'round' | 'bevel'\n  dashArray?: string | number[]\n  dashOffset?: string\n  fill?: boolean\n  fillColor?: string\n  fillOpacity?: number\n  className?: string\n  onClick?: (e: L.LeafletMouseEvent) => void\n  children?: React.ReactNode\n}\n\nexport interface LeafletPolylineProps extends LeafletPathProps {\n  /** Path points as [lng, lat][] — or [lng, lat][][] for multi-part lines. */\n  lngLatPath: [number, number][] | [number, number][][]\n  smoothFactor?: number\n  noClip?: boolean\n}\n\nconst LeafletPolylineComponent = ({ lngLatPath, onClick, children, ...opts }: LeafletPolylineProps) => {\n  const onClickRef = useLatest(onClick)\n  const optsRef = useLatest(opts)\n  const layer = useLeafletLayer<L.Polyline>((L) => {\n    const l = L.polyline(toLatLngs(lngLatPath) as L.LatLngExpression[], { ...optsRef.current, interactive: true })\n    l.on('click', (ev) => onClickRef.current?.(ev))\n    return l\n  })\n  React.useEffect(() => {\n    layer?.setLatLngs(toLatLngs(lngLatPath) as L.LatLngExpression[])\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, lngLatPath])\n  React.useEffect(() => {\n    layer?.setStyle({ ...opts, interactive: true })\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, opts.color, opts.weight, opts.opacity, opts.dashArray, opts.dashOffset, opts.lineCap, opts.lineJoin])\n  return <LeafletLayerContext.Provider value={layer}>{children}</LeafletLayerContext.Provider>\n}\nLeafletPolylineComponent.displayName = 'LeafletPolyline'\n\nexport interface LeafletPolygonProps extends LeafletPathProps {\n  /** Ring points as [lng, lat][] — or [lng, lat][][] for holes/multi-polygons. */\n  lngLatPath: [number, number][] | [number, number][][]\n}\n\nconst LeafletPolygonComponent = ({ lngLatPath, onClick, children, ...opts }: LeafletPolygonProps) => {\n  const onClickRef = useLatest(onClick)\n  const optsRef = useLatest(opts)\n  const layer = useLeafletLayer<L.Polygon>((L) => {\n    const l = L.polygon(toLatLngs(lngLatPath) as L.LatLngExpression[], { ...optsRef.current, interactive: true })\n    l.on('click', (ev) => onClickRef.current?.(ev))\n    return l\n  })\n  React.useEffect(() => {\n    layer?.setLatLngs(toLatLngs(lngLatPath) as L.LatLngExpression[])\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, lngLatPath])\n  React.useEffect(() => {\n    layer?.setStyle({ ...opts, interactive: true })\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, opts.color, opts.weight, opts.opacity, opts.fill, opts.fillColor, opts.fillOpacity, opts.dashArray])\n  return <LeafletLayerContext.Provider value={layer}>{children}</LeafletLayerContext.Provider>\n}\nLeafletPolygonComponent.displayName = 'LeafletPolygon'\n\nexport interface LeafletCircleProps extends LeafletPathProps {\n  /** [lng, lat] — Mapbox order. */\n  center: [number, number]\n  /** Radius in meters. */\n  radius?: number\n}\n\nconst LeafletCircleComponent = ({ center, radius, onClick, children, ...opts }: LeafletCircleProps) => {\n  const onClickRef = useLatest(onClick)\n  const optsRef = useLatest(opts)\n  const layer = useLeafletLayer<L.Circle>((L) => {\n    const l = L.circle(toLatLng(center), { ...optsRef.current, interactive: true, radius })\n    l.on('click', (ev) => onClickRef.current?.(ev))\n    return l\n  })\n  React.useEffect(() => {\n    layer?.setLatLng(toLatLng(center))\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, center?.[0], center?.[1]])\n  React.useEffect(() => {\n    if (radius !== undefined) layer?.setRadius(radius)\n  }, [layer, radius])\n  React.useEffect(() => {\n    layer?.setStyle({ ...opts, interactive: true })\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, opts.color, opts.weight, opts.opacity, opts.fill, opts.fillColor, opts.fillOpacity, opts.dashArray])\n  return <LeafletLayerContext.Provider value={layer}>{children}</LeafletLayerContext.Provider>\n}\nLeafletCircleComponent.displayName = 'LeafletCircle'\n\nexport interface LeafletCircleMarkerProps extends LeafletPathProps {\n  /** [lng, lat] — Mapbox order. */\n  center: [number, number]\n  /** Radius in pixels. */\n  radius?: number\n}\n\nconst LeafletCircleMarkerComponent = ({ center, radius, onClick, children, ...opts }: LeafletCircleMarkerProps) => {\n  const onClickRef = useLatest(onClick)\n  const optsRef = useLatest(opts)\n  const layer = useLeafletLayer<L.CircleMarker>((L) => {\n    const l = L.circleMarker(toLatLng(center), { ...optsRef.current, interactive: true, radius })\n    l.on('click', (ev) => onClickRef.current?.(ev))\n    return l\n  })\n  React.useEffect(() => {\n    layer?.setLatLng(toLatLng(center))\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, center?.[0], center?.[1]])\n  React.useEffect(() => {\n    if (radius !== undefined) layer?.setRadius(radius)\n  }, [layer, radius])\n  React.useEffect(() => {\n    layer?.setStyle({ ...opts, interactive: true })\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [layer, opts.color, opts.weight, opts.opacity, opts.fill, opts.fillColor, opts.fillOpacity, opts.dashArray])\n  return <LeafletLayerContext.Provider value={layer}>{children}</LeafletLayerContext.Provider>\n}\nLeafletCircleMarkerComponent.displayName = 'LeafletCircleMarker'\n\nexport interface LeafletGeoJsonProps {\n  /** GeoJSON FeatureCollection / Feature / geometry. */\n  geojson: GeoJSON.GeoJSON\n  /** Leaflet GeoJSON options: `style`, `pointToLayer`, `onEachFeature`, `filter`, `coordsToLatLng`. */\n  options?: L.GeoJSONOptions\n  onClick?: (e: L.LeafletMouseEvent) => void\n  children?: React.ReactNode\n}\n\nconst LeafletGeoJsonComponent = ({ geojson, options, onClick, children }: LeafletGeoJsonProps) => {\n  const onClickRef = useLatest(onClick)\n  const optionsRef = useLatest(options)\n  const layer = useLeafletLayer<L.GeoJSON>((L) => {\n    const l = L.geoJSON(geojson as any, optionsRef.current)\n    l.on('click', (ev) => onClickRef.current?.(ev))\n    return l\n  })\n  React.useEffect(() => {\n    if (!layer || !geojson) return\n    layer.clearLayers()\n    layer.addData(geojson as any)\n  }, [layer, geojson])\n  return <LeafletLayerContext.Provider value={layer}>{children}</LeafletLayerContext.Provider>\n}\nLeafletGeoJsonComponent.displayName = 'LeafletGeoJson'\n\nexport interface LeafletTileLayerProps {\n  /** Raster tile URL template ({z}/{x}/{y}, optional {s} subdomains + {r} retina). */\n  url: string\n  attribution?: string\n  subdomains?: string | string[]\n  minZoom?: number\n  maxZoom?: number\n  opacity?: number\n  zIndex?: number\n  tms?: boolean\n}\n\nconst LeafletTileLayerComponent = ({\n  url,\n  attribution,\n  subdomains,\n  minZoom,\n  maxZoom,\n  opacity,\n  zIndex,\n  tms,\n}: LeafletTileLayerProps) => {\n  const optsRef = useLatest({ attribution, subdomains, minZoom, maxZoom, opacity, zIndex, tms })\n  const layer = useLeafletLayer<L.TileLayer>((L) => L.tileLayer(url, defined(optsRef.current)))\n  React.useEffect(() => {\n    if (url) layer?.setUrl(url)\n  }, [layer, url])\n  React.useEffect(() => {\n    if (opacity !== undefined) layer?.setOpacity(opacity)\n  }, [layer, opacity])\n  React.useEffect(() => {\n    if (zIndex !== undefined) layer?.setZIndex(zIndex)\n  }, [layer, zIndex])\n  return null\n}\nLeafletTileLayerComponent.displayName = 'LeafletTileLayer'\n\nexport {\n  LeafletMapComponent as LeafletMap,\n  LeafletMarkerComponent as LeafletMarker,\n  LeafletPopupComponent as LeafletPopup,\n  LeafletTooltipComponent as LeafletTooltip,\n  LeafletPolylineComponent as LeafletPolyline,\n  LeafletPolygonComponent as LeafletPolygon,\n  LeafletCircleComponent as LeafletCircle,\n  LeafletCircleMarkerComponent as LeafletCircleMarker,\n  LeafletGeoJsonComponent as LeafletGeoJson,\n  LeafletTileLayerComponent as LeafletTileLayer,\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/leaflet-map/leaflet-map.tsx"
    },
    {
      "path": "packages/registry-react/components/leaflet-map/leaflet-map.variants.ts",
      "content": "import { cva, type VariantProps } from 'class-variance-authority'\n\nexport type LeafletMapVariant =\n  | 'default'\n  | 'muted'\n  | 'streets'\n  | 'outdoors'\n  | 'light'\n  | 'dark'\n  | 'satellite'\n  | 'satellite-streets'\n  | 'navigation-day'\n  | 'navigation-night'\n  | 'standard'\n\nexport interface LeafletTilePreset {\n  /** Raster tile URL template ({z}/{x}/{y}, optional {s} subdomains + {r} retina). */\n  url: string\n  /** Required provider attribution HTML. */\n  attribution: string\n  subdomains?: string | string[]\n  maxZoom?: number\n  /** Optional label/boundary overlay composited above the base tiles. */\n  overlayUrl?: string\n}\n\nconst OSM_ATTR = '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\nconst TOPO_ATTR = `${OSM_ATTR} | map style: &copy; <a href=\"https://opentopomap.org\">OpenTopoMap</a> (<a href=\"https://creativecommons.org/licenses/by-sa/3.0/\">CC-BY-SA</a>)`\nconst ESRI_ATTR = 'Tiles &copy; Esri &mdash; Source: Esri, Maxar, Earthstar Geographics, and the GIS User Community'\n\nconst OSM_STANDARD: LeafletTilePreset = {\n  url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',\n  attribution: OSM_ATTR,\n  maxZoom: 19,\n}\n// CARTO basemaps moved behind an API key — Esri Canvas/Street services stay key-free.\nconst ESRI_LIGHT: LeafletTilePreset = {\n  url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}',\n  attribution: ESRI_ATTR,\n  maxZoom: 16,\n  overlayUrl:\n    'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Reference/MapServer/tile/{z}/{y}/{x}',\n}\nconst ESRI_DARK: LeafletTilePreset = {\n  url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}',\n  attribution: ESRI_ATTR,\n  maxZoom: 16,\n  overlayUrl:\n    'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Reference/MapServer/tile/{z}/{y}/{x}',\n}\nconst ESRI_STREETS: LeafletTilePreset = {\n  url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',\n  attribution: ESRI_ATTR,\n  maxZoom: 19,\n}\nconst ESRI_SATELLITE: LeafletTilePreset = {\n  url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n  attribution: ESRI_ATTR,\n  maxZoom: 19,\n}\n\nexport const LEAFLET_TILES: Record<Exclude<LeafletMapVariant, 'default' | 'muted'>, LeafletTilePreset> = {\n  streets: OSM_STANDARD,\n  standard: OSM_STANDARD,\n  outdoors: {\n    url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',\n    attribution: TOPO_ATTR,\n    subdomains: 'abc',\n    maxZoom: 17,\n  },\n  light: ESRI_LIGHT,\n  dark: ESRI_DARK,\n  satellite: ESRI_SATELLITE,\n  'satellite-streets': {\n    ...ESRI_SATELLITE,\n    overlayUrl:\n      'https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}',\n  },\n  'navigation-day': ESRI_STREETS,\n  'navigation-night': ESRI_DARK,\n}\n\n/** Theme-aware default tiles: Esri light/dark canvas following the app theme. */\nexport const LEAFLET_THEME_TILES = { light: ESRI_LIGHT, dark: ESRI_DARK }\n\nexport const leafletMapVariants = cva('relative size-full overflow-hidden bg-muted isolate', {\n  variants: {\n    variant: {\n      default: '',\n      muted: '',\n      streets: '',\n      outdoors: '',\n      light: '',\n      dark: '',\n      satellite: '',\n      'satellite-streets': '',\n      'navigation-day': '',\n      'navigation-night': '',\n      standard: '',\n    },\n    size: {\n      default: 'h-96 w-full',\n      sm: 'h-64 w-full',\n      lg: 'h-[500px] w-full',\n      xl: 'h-[650px] w-full',\n      full: 'size-full',\n    },\n  },\n  defaultVariants: {\n    variant: 'default',\n  },\n})\n\nexport type LeafletMapVariants = VariantProps<typeof leafletMapVariants>\n",
      "type": "registry:ui",
      "target": "~/components/ui/leaflet-map/leaflet-map.variants.ts"
    },
    {
      "path": "packages/registry-react/components/leaflet-map/index.ts",
      "content": "export {\n  LeafletMap,\n  LeafletMarker,\n  LeafletPopup,\n  LeafletTooltip,\n  LeafletPolyline,\n  LeafletPolygon,\n  LeafletCircle,\n  LeafletCircleMarker,\n  LeafletGeoJson,\n  LeafletTileLayer,\n  useLeafletMap,\n  type LeafletMapProps,\n  type LeafletMapRef,\n  type LeafletFlyToOptions,\n  type LeafletViewOptions,\n  type LeafletMarkerProps,\n  type LeafletPopupProps,\n  type LeafletTooltipProps,\n  type LeafletPathProps,\n  type LeafletPolylineProps,\n  type LeafletPolygonProps,\n  type LeafletCircleProps,\n  type LeafletCircleMarkerProps,\n  type LeafletGeoJsonProps,\n  type LeafletTileLayerProps,\n} from './leaflet-map'\nexport {\n  leafletMapVariants,\n  LEAFLET_TILES,\n  LEAFLET_THEME_TILES,\n  type LeafletMapVariant,\n  type LeafletMapVariants,\n  type LeafletTilePreset,\n} from './leaflet-map.variants'\n",
      "type": "registry:ui",
      "target": "~/components/ui/leaflet-map/index.ts"
    },
    {
      "path": "packages/registry-react/components/leaflet-map/leaflet-map.css",
      "content": "/**\n * Leaflet map chrome — re-skins Leaflet's controls, popups, and tooltips to the\n * design-system tokens so they sit naturally on light and dark surfaces.\n * Global (Leaflet renders this chrome outside the component subtree). Import\n * once, e.g. in your root CSS: `@import './components/ui/leaflet-map/leaflet-map.css';`\n */\n.leaflet-container {\n  font: inherit;\n  background: var(--muted);\n}\n.leaflet-bar {\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  box-shadow: var(--shadow-sm);\n}\n.leaflet-bar a,\n.leaflet-bar a:hover {\n  background: var(--card);\n  color: var(--foreground);\n  border-bottom-color: var(--border);\n}\n.leaflet-bar a:hover {\n  background: var(--muted);\n}\n.leaflet-bar a.leaflet-disabled {\n  background: var(--card);\n  color: var(--muted-foreground);\n}\n.leaflet-popup-content-wrapper {\n  background: var(--popover);\n  color: var(--popover-foreground);\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  box-shadow: var(--shadow-md);\n}\n.leaflet-popup-content {\n  margin: var(--spacing-2, 8px) var(--spacing-3, 12px);\n  font: inherit;\n  line-height: 1.5;\n}\n.leaflet-popup-tip {\n  background: var(--popover);\n  border: 1px solid var(--border);\n  box-shadow: none;\n}\n.leaflet-popup-close-button {\n  color: var(--muted-foreground) !important;\n}\n.leaflet-tooltip {\n  background: var(--popover);\n  color: var(--popover-foreground);\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  box-shadow: var(--shadow-sm);\n  font: inherit;\n}\n.leaflet-tooltip-top:before {\n  border-top-color: var(--border);\n}\n.leaflet-tooltip-bottom:before {\n  border-bottom-color: var(--border);\n}\n/* `muted` variant — desaturate only the tile pane so overlaid data keeps its\n * colour. */\n[data-slot='leaflet-map'][data-muted='true'] .leaflet-tile-pane {\n  filter: grayscale(55%) contrast(0.92);\n}\n\n/* Custom marker icons: Leaflet wraps our element in its own icon div; keep it\n * unstyled so slot content controls the look. DivIcon forces a 12×12 inline\n * box plus anchor margins — override so the icon sizes to its content and the\n * inner anchor transform does the positioning. */\n.uipkge-leaflet-div-icon {\n  background: transparent;\n  border: none;\n  width: auto !important;\n  height: auto !important;\n  margin: 0 !important;\n}\n\n/* Marker slot content is a real DOM element moved into the icon. It stays\n * hidden while parked inside the component tree, and the anchor transform\n * decides which point of the content sits on the coordinate. */\n.uipkge-leaflet-anchor {\n  display: none;\n}\n.leaflet-marker-icon .uipkge-leaflet-anchor {\n  display: block;\n  /* shrink-to-fit inside the abspos icon resolves to min-content and wraps\n   * labels — force natural content width instead */\n  width: max-content;\n}\n.uipkge-leaflet-anchor[data-anchor='center'] {\n  transform: translate(-50%, -50%);\n}\n.uipkge-leaflet-anchor[data-anchor='top'] {\n  transform: translate(-50%, 0);\n}\n.uipkge-leaflet-anchor[data-anchor='bottom'] {\n  transform: translate(-50%, -100%);\n}\n.uipkge-leaflet-anchor[data-anchor='left'] {\n  transform: translate(0, -50%);\n}\n.uipkge-leaflet-anchor[data-anchor='right'] {\n  transform: translate(-100%, -50%);\n}\n.uipkge-leaflet-anchor[data-anchor='top-left'] {\n  transform: translate(0, 0);\n}\n.uipkge-leaflet-anchor[data-anchor='top-right'] {\n  transform: translate(-100%, 0);\n}\n.uipkge-leaflet-anchor[data-anchor='bottom-left'] {\n  transform: translate(0, -100%);\n}\n.uipkge-leaflet-anchor[data-anchor='bottom-right'] {\n  transform: translate(-100%, -100%);\n}\n\n/* Popup/tooltip slot sources are real DOM moved into the overlay's content\n * node when it opens. */\n.uipkge-leaflet-popup-src,\n.uipkge-leaflet-tooltip-src {\n  display: none;\n}\n.leaflet-popup-content .uipkge-leaflet-popup-src,\n.leaflet-tooltip .uipkge-leaflet-tooltip-src {\n  display: block;\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/leaflet-map/leaflet-map.css"
    }
  ],
  "dependencies": [
    "leaflet",
    "next-themes",
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "A thin, theme-aware Leaflet wrapper rendering free raster tiles (OpenStreetMap, OpenTopoMap, Esri) — no API key required. Drop LeafletMarker / LeafletPopup / LeafletPolyline / LeafletGeoJson into the children to build any map — fleet boards, journey maps, store locators. The base style follows light/dark automatically, an opt-in `muted` prop desaturates the tile pane so overlaid data is the only colour, and `onCreated` hands you the raw L.Map instance for custom layers and fitBounds. The Mapbox counterpart (`map`) adds GL styles, 3D, and globe; this one trades those for zero-key setup. Import `leaflet/dist/leaflet.css` once — the component does it for you.",
  "categories": [
    "data-display"
  ]
}