{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "leaflet-isochrone-reachability-map",
  "title": "Leaflet Isochrone Reachability Map",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/leaflet-isochrone-reachability-map/LeafletIsochroneReachabilityMap.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'\nimport { LeafletMap, LeafletMarker, LeafletPolygon, type LeafletMapRef } from '@/components/ui/leaflet-map'\n\nexport interface LeafletIsochroneReachabilityMapProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst hubLocation: [number, number] = [-122.419, 37.775] // Downtown San Francisco\n\n// Key-free drive-time approximation: a perturbed ring whose radius varies by\n// angle through fixed harmonic offsets, so each band reads as an irregular\n// blob rather than a perfect circle (a real isochrone needs a routing API).\nfunction createIsochroneCoords(radiusMiles: number, phase: number): [number, number][] {\n  const points: [number, number][] = []\n  const steps = 48\n  const latDelta = radiusMiles / 69\n  const lngDelta = radiusMiles / (69 * Math.cos((hubLocation[1] * Math.PI) / 180))\n  for (let i = 0; i <= steps; i++) {\n    const angle = (i / steps) * 2 * Math.PI\n    const jitter = 1 + 0.16 * Math.sin(angle * 3 + phase) + 0.07 * Math.sin(angle * 7 + phase * 2.3)\n    const lng = hubLocation[0] + lngDelta * Math.cos(angle) * jitter\n    const lat = hubLocation[1] + latDelta * Math.sin(angle) * jitter\n    points.push([lng, lat])\n  }\n  return points\n}\n\nconst isochrones = {\n  10: { coords: createIsochroneCoords(2.8, 0.4), pop: '340K', jobs: '520K', color: '#10b981', label: '10 Min Commute' },\n  20: {\n    coords: createIsochroneCoords(6.2, 1.7),\n    pop: '1.24M',\n    jobs: '890K',\n    color: '#3b82f6',\n    label: '20 Min Commute',\n  },\n  30: {\n    coords: createIsochroneCoords(11.5, 2.9),\n    pop: '2.85M',\n    jobs: '1.45M',\n    color: '#f59e0b',\n    label: '30 Min Commute',\n  },\n}\n\nconst destinations = [\n  { name: 'Mission Depot', lngLat: [-122.414, 37.762] as [number, number] },\n  { name: 'Daly City Hub', lngLat: [-122.47, 37.705] as [number, number] },\n  { name: 'Oakland Annex', lngLat: [-122.272, 37.804] as [number, number] },\n]\n\nexport function LeafletIsochroneReachabilityMap({ className, ...props }: LeafletIsochroneReachabilityMapProps) {\n  const mapRef = React.useRef<LeafletMapRef>(null)\n  const [selectedTime, setSelectedTime] = React.useState<10 | 20 | 30>(20)\n\n  return (\n    <div\n      data-slot=\"leaflet-isochrone-reachability-map\"\n      className={cn('grid gap-4 lg:grid-cols-[1fr_320px]', className)}\n      {...props}\n    >\n      <div className=\"border-border bg-card relative h-[480px] overflow-hidden rounded-xl border\">\n        <LeafletMap\n          ref={mapRef}\n          variant=\"light\"\n          center={hubLocation}\n          zoom={10.5}\n          className=\"absolute inset-0 size-full\"\n        >\n          {/* 30m Isochrone (outermost, drawn first) */}\n          <LeafletPolygon\n            lngLatPath={isochrones[30].coords}\n            color={isochrones[30].color}\n            weight={1.5}\n            dashArray=\"4 4\"\n            fill\n            fillColor={isochrones[30].color}\n            fillOpacity={selectedTime >= 30 ? 0.15 : 0.04}\n          />\n\n          {/* 20m Isochrone */}\n          <LeafletPolygon\n            lngLatPath={isochrones[20].coords}\n            color={isochrones[20].color}\n            weight={2}\n            fill\n            fillColor={isochrones[20].color}\n            fillOpacity={selectedTime >= 20 ? 0.22 : 0.06}\n          />\n\n          {/* 10m Isochrone (innermost, most opaque) */}\n          <LeafletPolygon\n            lngLatPath={isochrones[10].coords}\n            color={isochrones[10].color}\n            weight={2.5}\n            fill\n            fillColor={isochrones[10].color}\n            fillOpacity={0.3}\n          />\n\n          {/* Destinations */}\n          {destinations.map((dest) => (\n            <LeafletMarker key={dest.name} lngLat={dest.lngLat} anchor=\"center\">\n              <div className=\"flex flex-col items-center gap-0.5\">\n                <span className=\"border-background bg-foreground size-2 rounded-full border-2 shadow-xs\" />\n                <span className=\"bg-card/90 text-foreground rounded px-1 font-mono text-xs font-semibold whitespace-nowrap shadow-xs\">\n                  {dest.name}\n                </span>\n              </div>\n            </LeafletMarker>\n          ))}\n\n          {/* Central Hub Marker */}\n          <LeafletMarker lngLat={hubLocation} anchor=\"center\">\n            <div className=\"relative flex items-center justify-center\">\n              <span className=\"bg-primary/30 absolute size-6 animate-ping rounded-full\" />\n              <div className=\"bg-primary text-primary-foreground ring-background flex size-6 items-center justify-center rounded-full font-mono text-xs font-bold shadow-md ring-2\">\n                ★\n              </div>\n            </div>\n          </LeafletMarker>\n        </LeafletMap>\n      </div>\n\n      {/* Isochrone Analytics Card */}\n      <Card className=\"flex flex-col justify-between\">\n        <CardHeader>\n          <div className=\"flex items-center justify-between\">\n            <Badge variant=\"outline\" className=\"font-mono text-xs\">\n              COMMUTE ANALYSIS\n            </Badge>\n            <Badge className=\"border-info/20 bg-info/10 text-info\">Drive-Time</Badge>\n          </div>\n          <CardTitle className=\"mt-2 text-lg\">Reachability Zones</CardTitle>\n          <CardDescription>Transit reachability from Downtown Distribution Hub.</CardDescription>\n        </CardHeader>\n        <CardContent className=\"space-y-4\">\n          {/* Range Selector Tabs */}\n          <div className=\"border-border bg-muted grid grid-cols-3 gap-1 rounded-lg border p-1 text-xs\">\n            {([10, 20, 30] as const).map((t) => (\n              <button\n                key={t}\n                type=\"button\"\n                className={`rounded-md py-1.5 font-medium transition-colors ${\n                  selectedTime === t\n                    ? 'bg-background text-foreground shadow-xs'\n                    : 'text-muted-foreground hover:text-foreground'\n                }`}\n                onClick={() => setSelectedTime(t)}\n              >\n                {t} mins\n              </button>\n            ))}\n          </div>\n\n          <div className=\"border-border space-y-3 border-t pt-3\">\n            <div className=\"flex items-center justify-between\">\n              <span className=\"text-muted-foreground text-xs\">Population Reach</span>\n              <span className=\"text-foreground font-mono text-base font-bold\">{isochrones[selectedTime].pop}</span>\n            </div>\n            <div className=\"flex items-center justify-between\">\n              <span className=\"text-muted-foreground text-xs\">Workforce / Labor Pool</span>\n              <span className=\"text-foreground font-mono text-base font-bold\">{isochrones[selectedTime].jobs}</span>\n            </div>\n            <div className=\"flex items-center justify-between\">\n              <span className=\"text-muted-foreground text-xs\">Service Band</span>\n              <span className=\"font-mono text-xs font-semibold\" style={{ color: isochrones[selectedTime].color }}>\n                {isochrones[selectedTime].label}\n              </span>\n            </div>\n          </div>\n\n          <div className=\"border-border text-muted-foreground space-y-2 border-t pt-3 text-xs\">\n            <div className=\"flex items-center gap-2\">\n              <span className=\"bg-success size-2 rounded-full\" />\n              <span>Green ring: 10-minute urban core</span>\n            </div>\n            <div className=\"flex items-center gap-2\">\n              <span className=\"bg-info size-2 rounded-full\" />\n              <span>Blue ring: 20-minute metropolitan belt</span>\n            </div>\n            <div className=\"flex items-center gap-2\">\n              <span className=\"bg-warning size-2 rounded-full\" />\n              <span>Amber ring: 30-minute outer ring</span>\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n    </div>\n  )\n}\n\nexport default LeafletIsochroneReachabilityMap\n",
      "type": "registry:block",
      "target": "~/components/blocks/LeafletIsochroneReachabilityMap.tsx"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/leaflet-map.json"
  ],
  "description": "Drive-time reachability zones showing 10m, 20m, and 30m commute polygons with workforce population metrics — on free OpenStreetMap/Esri tiles, no API key.",
  "categories": [
    "logistics",
    "geo",
    "map"
  ]
}