@zendir/ui 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"GroundTrackMapLeaflet.js","sources":["../../../src/react/charts/GroundTrackMapLeaflet.tsx"],"sourcesContent":["/**\n * @zendir/ui - GroundTrackMap Leaflet Provider\n *\n * Leaflet-based 2D world map for ground tracks, stations, and terminator.\n * Uses Zendir hybrid theme; all interactions enabled (zoom, scroll, drag, etc.).\n * Import leaflet-zendir.css when using this component.\n */\n\nimport React, { useRef, useEffect, useCallback, useMemo, useState } from 'react';\nimport { useTheme } from '../theme';\nimport type { GroundStation } from '../types';\nimport type { SatelliteTrack, GroundStationEnhanced, SROGroundStation, MapPin, LightSource } from './GroundTrackMap';\n\n// Optional Leaflet — consumer must install leaflet when using mapProvider=\"leaflet\"\nimport L from 'leaflet';\nimport 'leaflet/dist/leaflet.css';\nimport './leaflet-zendir.css';\nimport {\n splitPolylineAtAntimeridian,\n segmentsWithWorldCopies,\n geodesicCirclePoints,\n splitRingAtAntimeridian,\n} from './groundTrackMapLeafletUtils';\nimport {\n DEFAULT_TILE,\n FALLBACK_TILE,\n TILE_ERROR_THRESHOLD,\n CARTO_ATTRIBUTION,\n OSM_ATTRIBUTION,\n TILE_PRESETS,\n} from './groundTrackMapLeafletTiles';\n\nconst STATUS_COLOR_MAP: Record<string, string> = {\n normal: '#56f000',\n standby: '#2dccff',\n caution: '#fce83a',\n serious: '#ffb302',\n critical: '#ff3838',\n off: '#a4abb6',\n};\n\nconst DEFAULT_TRACK_COLORS = [\n '#2dccff', '#3E3CFF', '#9D70FF', '#56f000', '#fce83a', '#ff7849', '#2dd4bf', '#ff3838',\n];\n\n// =============================================================================\n// Icon helpers — produce L.DivIcon with inline SVG (no external assets needed)\n// =============================================================================\n\n/** Satellite spacecraft icon with solar panels, matching Astro UXDS style. */\nfunction createSatDivIcon(statusColor: string, trackColor: string): L.DivIcon {\n // Status badge shape (Astro UXDS spec)\n const badge = statusColor === '#56f000'\n ? `<circle cx=\"7\" cy=\"7\" r=\"3\" fill=\"${statusColor}\"/>` // normal: filled dot\n : statusColor === '#2dccff'\n ? `<circle cx=\"7\" cy=\"7\" r=\"2.5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\"/>` // standby: ring\n : statusColor === '#fce83a'\n ? `<rect x=\"4.5\" y=\"4.5\" width=\"5\" height=\"5\" fill=\"${statusColor}\"/>` // caution: square\n : statusColor === '#ffb302'\n ? `<polygon points=\"7,3.5 10.5,7 7,10.5 3.5,7\" fill=\"${statusColor}\"/>` // serious: diamond (Astro UXDS)\n : statusColor === '#ff3838'\n ? `<polygon points=\"7,10 4,4 10,4\" fill=\"${statusColor}\"/>` // critical: triangle down\n : `<circle cx=\"7\" cy=\"7\" r=\"2\" fill=\"${statusColor}\"/>`; // off: small dot\n\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"34\" height=\"34\" viewBox=\"0 0 34 34\">\n <!-- Glow ring -->\n <circle cx=\"17\" cy=\"17\" r=\"15\" fill=\"${statusColor}\" fill-opacity=\"0.12\"/>\n <!-- Main body circle -->\n <circle cx=\"17\" cy=\"17\" r=\"11\" fill=\"#0d1323\" stroke=\"${trackColor}\" stroke-width=\"1.5\"/>\n <!-- Left solar panel -->\n <rect x=\"2\" y=\"15\" width=\"9\" height=\"4\" rx=\"0.5\" fill=\"${trackColor}\" fill-opacity=\"0.75\"/>\n <line x1=\"5.5\" y1=\"15\" x2=\"5.5\" y2=\"19\" stroke=\"${trackColor}\" stroke-width=\"0.5\" stroke-opacity=\"0.45\"/>\n <!-- Connector left -->\n <line x1=\"11\" y1=\"17\" x2=\"13\" y2=\"17\" stroke=\"${trackColor}\" stroke-width=\"1.2\"/>\n <!-- Satellite bus body -->\n <rect x=\"13\" y=\"14.5\" width=\"8\" height=\"5\" rx=\"1\" fill=\"${statusColor}\"/>\n <!-- Connector right -->\n <line x1=\"21\" y1=\"17\" x2=\"23\" y2=\"17\" stroke=\"${trackColor}\" stroke-width=\"1.2\"/>\n <!-- Right solar panel -->\n <rect x=\"23\" y=\"15\" width=\"9\" height=\"4\" rx=\"0.5\" fill=\"${trackColor}\" fill-opacity=\"0.75\"/>\n <line x1=\"26.5\" y1=\"15\" x2=\"26.5\" y2=\"19\" stroke=\"${trackColor}\" stroke-width=\"0.5\" stroke-opacity=\"0.45\"/>\n <!-- Status badge (top-left) -->\n <circle cx=\"7\" cy=\"7\" r=\"5.5\" fill=\"#0d1323\"/>\n ${badge}\n </svg>`;\n\n return L.divIcon({\n html: svg,\n className: 'zendir-sat-icon',\n iconSize: [34, 34] as unknown as L.PointExpression,\n iconAnchor: [17, 17] as unknown as L.PointExpression,\n tooltipAnchor: [0, -18] as unknown as L.PointExpression,\n });\n}\n\n/** Ground station icon — dish/antenna shape, matching Astro UXDS style. */\nfunction createStationDivIcon(statusColor: string, type: string, status: string): L.DivIcon {\n // Status badge\n const badge = status === 'normal'\n ? `<circle cx=\"5\" cy=\"5\" r=\"2.5\" fill=\"${statusColor}\"/>`\n : status === 'standby'\n ? `<circle cx=\"5\" cy=\"5\" r=\"2\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>`\n : status === 'caution'\n ? `<rect x=\"3\" y=\"3\" width=\"4\" height=\"4\" fill=\"${statusColor}\"/>`\n : status === 'serious'\n ? `<polygon points=\"5,2.5 7.5,5 5,7.5 2.5,5\" fill=\"${statusColor}\"/>`\n : status === 'critical'\n ? `<polygon points=\"5,7.5 2.5,2.5 7.5,2.5\" fill=\"${statusColor}\"/>`\n : `<circle cx=\"5\" cy=\"5\" r=\"1.5\" fill=\"${statusColor}\"/>`;\n\n let iconInner = '';\n if (type === 'phased-array') {\n // Grid pattern (phased array)\n iconInner = `\n <rect x=\"9\" y=\"7\" width=\"10\" height=\"8\" rx=\"0.5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"12\" y1=\"7\" x2=\"12\" y2=\"15\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"15\" y1=\"7\" x2=\"15\" y2=\"15\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"9\" y1=\"10\" x2=\"19\" y2=\"10\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"9\" y1=\"13\" x2=\"19\" y2=\"13\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"14\" y1=\"15\" x2=\"14\" y2=\"20\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"11\" y1=\"20\" x2=\"17\" y2=\"20\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>`;\n } else if (type === 'relay') {\n // Relay dish with signal arcs\n iconInner = `\n <path d=\"M 8 16 a 7 7 0 0 1 7 -7\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M 8 16 m 2 -2 a 5 5 0 0 1 5 -5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>\n <line x1=\"14\" y1=\"16\" x2=\"14\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"11\" y1=\"21\" x2=\"17\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <path d=\"M 16 8 a 3 3 0 0 1 0 4\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>\n <path d=\"M 17.5 6.5 a 5 5 0 0 1 0 7\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1\" stroke-linecap=\"round\"/>`;\n } else {\n // Default: dish antenna with concentric arcs (Astro UXDS antenna pattern)\n iconInner = `\n <path d=\"M 14 14 m -7 0 a 7 7 0 0 1 7 -7\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M 14 14 m -5 0 a 5 5 0 0 1 5 -5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M 14 14 m -3 0 a 3 3 0 0 1 3 -3\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <circle cx=\"14\" cy=\"14\" r=\"1.5\" fill=\"${statusColor}\"/>\n <line x1=\"14\" y1=\"15\" x2=\"14\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"11\" y1=\"21\" x2=\"17\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>`;\n }\n\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"28\" height=\"28\" viewBox=\"0 0 28 28\">\n <!-- Glow ring -->\n <circle cx=\"14\" cy=\"14\" r=\"13\" fill=\"${statusColor}\" fill-opacity=\"0.1\"/>\n <!-- Background -->\n <circle cx=\"14\" cy=\"14\" r=\"11\" fill=\"#0d1323\" stroke=\"${statusColor}\" stroke-width=\"1.2\" stroke-opacity=\"0.7\"/>\n ${iconInner}\n <!-- Status badge (top-left) -->\n <circle cx=\"5\" cy=\"5\" r=\"5\" fill=\"#0d1323\"/>\n ${badge}\n </svg>`;\n\n return L.divIcon({\n html: svg,\n className: 'zendir-station-icon',\n iconSize: [28, 28] as unknown as L.PointExpression,\n iconAnchor: [14, 14] as unknown as L.PointExpression,\n tooltipAnchor: [0, -14] as unknown as L.PointExpression,\n });\n}\n\n/**\n * Compute a terminator line with CONTINUOUS (unwrapped) longitudes.\n *\n * `depressionDeg` offsets the solar zenith angle to produce twilight\n * boundaries instead of the geometric sunset line:\n * - 0° = geometric sunset/sunrise (day/night boundary)\n * - 6° = civil twilight (horizon still visible, outdoor activities possible)\n * - 12° = nautical twilight (horizon barely visible, stars appearing)\n * - 18° = astronomical twilight (sky fully dark for observation)\n *\n * These thresholds follow IAU/USNO standard definitions used in celestial\n * navigation, Astro UX space ops dashboards, and STK/GMAT mission tools.\n */\nfunction calculateTerminatorContinuous(\n date: Date,\n depressionDeg: number = 0,\n numPoints: number = 360,\n): { sunset: Array<[number, number]>, sunrise: Array<[number, number]> } {\n const dayOfYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000);\n const declination = -23.44 * Math.cos((2 * Math.PI / 365) * (dayOfYear + 10));\n const decRad = (declination * Math.PI) / 180;\n const hourAngle = ((date.getUTCHours() + date.getUTCMinutes() / 60) / 24) * 360 - 180;\n const depRad = (depressionDeg * Math.PI) / 180;\n\n const sunset: Array<[number, number]> = [];\n const sunrise: Array<[number, number]> = [];\n\n for (let i = 0; i <= numPoints; i++) {\n // Avoid exact +/- 90 to prevent longitude singularities at the poles\n const lat = -89.999 + (i / numPoints) * 179.998;\n const latRad = lat * (Math.PI / 180);\n const cosH = -(Math.sin(depRad) + Math.sin(latRad) * Math.sin(decRad))\n / (Math.cos(latRad) * Math.cos(decRad));\n \n if (cosH < -1) {\n // All day. No points.\n } else if (cosH > 1) {\n // All night. Covers all longitudes.\n // We map the night as the 360-degree span starting at hourAngle.\n sunset.push([lat, hourAngle]);\n sunrise.push([lat, hourAngle + 360]);\n } else {\n // Normal twilight\n const H = (Math.acos(cosH) * 180) / Math.PI;\n sunset.push([lat, hourAngle + H]);\n // Sunrise is 360 - H degrees East of the sun (which is equivalent to West of the sun,\n // but keeping it > sunset ensures the night polygon is continuous Eastward).\n sunrise.push([lat, hourAngle + 360 - H]);\n }\n }\n \n return { sunset, sunrise };\n}\n\n/**\n * Build the full night polygon, properly closing the loops around the poles.\n * Ensures longitudes are strictly continuous so the map projection doesn't\n * draw antimeridian lines across the day side.\n */\nfunction buildNightPolygon(\n terminator: { sunset: Array<[number, number]>, sunrise: Array<[number, number]> }\n): [number, number][] {\n const { sunset, sunrise } = terminator;\n if (!sunset.length || !sunrise.length) return [];\n\n const poly: [number, number][] = [];\n\n // Left edge of night (sunset): South to North\n poly.push(...sunset);\n\n // Right edge of night (sunrise): North to South\n poly.push(...[...sunrise].reverse());\n\n // Leaflet will automatically close the polygon by connecting the last point \n // (South end of sunrise) back to the first point (South end of sunset).\n // Because longitudes are continuous and bounded (sunrise.lon - sunset.lon <= 360),\n // this closing line will simply span the bottom or top of the map, perfectly\n // enclosing the night area without crossing the day side!\n\n return poly;\n}\n\nexport type GroundStationMapItem = GroundStation | GroundStationEnhanced | SROGroundStation;\n\nexport interface GroundTrackMapLeafletProps {\n allSatellites: SatelliteTrack[];\n groundStations: GroundStationMapItem[];\n showTerminator?: boolean;\n /** Override the time used for the terminator calculation (defaults to wall-clock now). */\n terminatorTime?: Date;\n showGrid?: boolean;\n showLegend?: boolean;\n showEquator?: boolean;\n showRecenterButton?: boolean;\n /** Show a toggle button to switch between Dark and Satellite tile styles. Default true. */\n showMapStyleToggle?: boolean;\n defaultCenter?: [number, number];\n defaultZoom?: number;\n height?: number | string;\n width?: number | string;\n minHeight?: string;\n emptyMessage?: string;\n tileUrl?: string;\n /**\n * URL template for a \"night\" tile layer (e.g. NASA Black Marble, VIIRS city lights).\n * Rendered beneath the terminator overlay so it only appears on the dark side.\n * Requires `showTerminator`. Falls back to dark overlay when not provided.\n */\n nightTileUrl?: string;\n /**\n * Point light sources rendered on the night side (masked by the terminator).\n * Each light fades in through twilight and reaches full brightness at night.\n */\n lightSources?: LightSource[];\n className?: string;\n onSatelliteClick?: (id: string) => void;\n onStationClick?: (id: string) => void;\n /** Points-of-interest pins */\n pins?: MapPin[];\n /** Allow adding/editing pins via map clicks */\n pinsEditable?: boolean;\n onPinAdd?: (pin: Omit<MapPin, 'id'>) => void;\n onPinUpdate?: (pin: MapPin) => void;\n onPinRemove?: (pinId: string) => void;\n}\n\nexport function GroundTrackMapLeaflet({\n allSatellites,\n groundStations,\n showTerminator = true,\n terminatorTime,\n showGrid = false,\n showLegend = true,\n showEquator = false,\n showRecenterButton = true,\n showMapStyleToggle = true,\n defaultCenter = [20, 0],\n defaultZoom = 2,\n height = '100%',\n width = '100%',\n minHeight = '400px',\n emptyMessage = 'No orbital data available',\n tileUrl,\n nightTileUrl,\n lightSources,\n className = '',\n onSatelliteClick,\n onStationClick,\n pins,\n pinsEditable = false,\n onPinAdd,\n onPinUpdate,\n onPinRemove,\n}: GroundTrackMapLeafletProps): React.ReactElement {\n const { tokens } = useTheme();\n const containerRef = useRef<HTMLDivElement>(null);\n const mapRef = useRef<L.Map | null>(null);\n const tileLayerRef = useRef<L.TileLayer | null>(null);\n const overlayGroupRef = useRef<L.LayerGroup | null>(null);\n const controlsRef = useRef<L.Control[]>([]);\n /** Separate layer group so pin updates don't clear satellite/station layers */\n const pinsGroupRef = useRef<L.LayerGroup | null>(null);\n const [ready, setReady] = useState(false);\n\n /** Internal tile style toggle — only active when no explicit tileUrl prop is passed. */\n const [tileStyle, setTileStyle] = useState<'dark' | 'satellite'>('dark');\n const isExplicitTileUrl = tileUrl !== undefined;\n const effectiveTileUrl = isExplicitTileUrl ? tileUrl : TILE_PRESETS[tileStyle];\n\n const clearLayers = useCallback(() => {\n const map = mapRef.current;\n if (!map) return;\n overlayGroupRef.current?.clearLayers();\n controlsRef.current.forEach((ctrl) => map.removeControl(ctrl));\n controlsRef.current = [];\n }, []);\n\n const addLayer = useCallback((layer: L.Layer) => {\n overlayGroupRef.current?.addLayer(layer);\n }, []);\n\n // Create map once on mount (or when tileUrl changes). Intentionally not depending on\n // defaultCenter/defaultZoom so animated updates do not recreate the map (no flicker/zoom reset).\n useEffect(() => {\n // eslint-disable-next-line react-hooks/exhaustive-deps -- defaultCenter/defaultZoom used only for initial view\n const el = containerRef.current;\n if (!el) return;\n\n const center: L.LatLngExpression = defaultCenter ?? [20, 0];\n const zoom = defaultZoom ?? 2;\n\n const map = L.map(el, {\n center,\n zoom,\n zoomControl: false,\n scrollWheelZoom: true,\n doubleClickZoom: true,\n touchZoom: true,\n boxZoom: true,\n keyboard: true,\n dragging: true,\n attributionControl: true,\n maxBounds: [[-90, -540], [90, 540]],\n maxBoundsViscosity: 1.0,\n });\n\n L.control.zoom({ position: 'topright' }).addTo(map);\n\n map.attributionControl.setPrefix('');\n\n const overlayGroup = L.layerGroup();\n overlayGroup.addTo(map);\n overlayGroupRef.current = overlayGroup;\n\n const pinsGroup = L.layerGroup();\n pinsGroup.addTo(map);\n pinsGroupRef.current = pinsGroup;\n\n mapRef.current = map;\n setReady(true);\n\n const onSize = () => {\n map.invalidateSize({ animate: false });\n };\n // Immediate RAF + two deferred calls cover: first paint, CSS transitions, flex layout settle\n const raf = requestAnimationFrame(onSize);\n const t1 = setTimeout(onSize, 150);\n const t2 = setTimeout(onSize, 500);\n\n return () => {\n clearTimeout(t1);\n clearTimeout(t2);\n cancelAnimationFrame(raf);\n clearLayers();\n pinsGroupRef.current?.clearLayers();\n pinsGroupRef.current = null;\n tileLayerRef.current = null;\n map.remove();\n mapRef.current = null;\n overlayGroupRef.current = null;\n };\n // Mount-only: tile layer is swapped by a dedicated effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clearLayers]);\n\n // Swap tile layer without recreating the entire map (avoids flash on style toggle).\n useEffect(() => {\n const map = mapRef.current;\n if (!map || !ready) return;\n\n // Remove existing tile layer and its attribution\n if (tileLayerRef.current) {\n tileLayerRef.current.remove();\n tileLayerRef.current = null;\n }\n // Clear old attributions before adding new\n try {\n map.attributionControl.removeAttribution(CARTO_ATTRIBUTION);\n map.attributionControl.removeAttribution(OSM_ATTRIBUTION);\n } catch { /* ignore if not present */ }\n\n const isCartoTiles = effectiveTileUrl.includes('cartocdn');\n if (isCartoTiles) {\n map.attributionControl.addAttribution(CARTO_ATTRIBUTION);\n }\n\n const tile = L.tileLayer(effectiveTileUrl, {\n maxZoom: 19,\n subdomains: isCartoTiles ? 'abcd' : 'abc',\n crossOrigin: true,\n });\n tile.addTo(map);\n tile.bringToBack();\n tileLayerRef.current = tile;\n\n // Fallback on tile error\n let hasSwitchedToFallback = false;\n const onTileError = () => {\n if (hasSwitchedToFallback) return;\n hasSwitchedToFallback = true;\n tile.off('tileerror', onTileError);\n tile.remove();\n const fallback = L.tileLayer(FALLBACK_TILE, {\n maxZoom: 19,\n subdomains: 'abc',\n crossOrigin: true,\n });\n fallback.addTo(map);\n fallback.bringToBack();\n tileLayerRef.current = fallback;\n };\n tile.on('tileerror', onTileError);\n }, [effectiveTileUrl, ready]);\n\n useEffect(() => {\n if (!ready || !mapRef.current) return;\n const map = mapRef.current;\n clearLayers();\n\n // === Night Tile Layer (placed beneath terminator so night imagery appears on dark side) ===\n if (nightTileUrl && showTerminator) {\n const nightTile = L.tileLayer(nightTileUrl, {\n maxZoom: 19,\n crossOrigin: true,\n opacity: 0.7,\n className: 'zendir-night-tiles',\n });\n addLayer(nightTile);\n }\n\n if (showTerminator) {\n const now = terminatorTime ?? new Date();\n\n // Build a smooth graduated terminator using many thin bands (every 2°)\n // instead of hard-edged zones, creating a seamless day→night fade.\n // Depression angles: 0° (geometric sunset) through 24° (deep night).\n const BAND_STEP = 2;\n const MAX_DEP = 24;\n const NIGHT_OPACITY = 0.55;\n const bandSteps: Array<{ depression: number; opacity: number }> = [];\n for (let d = 0; d <= MAX_DEP; d += BAND_STEP) {\n // Smooth quadratic ramp: 0° → 0.0, 18° → ~0.45, 24° → 0.55\n const t = Math.min(d / 18, 1);\n const opacity = t * t * (NIGHT_OPACITY * 0.82) + (d > 18 ? (d - 18) / 6 * (NIGHT_OPACITY * 0.18) : 0);\n bandSteps.push({ depression: d, opacity: Math.min(opacity, NIGHT_OPACITY) });\n }\n // Final deep-night fill for everything beyond the last band\n bandSteps.push({ depression: 90, opacity: NIGHT_OPACITY });\n\n let prevOpacity = 0;\n\n // Draw each band as a cumulative overlay — each adds incremental darkening\n // By stacking full night polygons with small opacities, we avoid drawing\n // complex band-difference polygons, which is much more robust at the poles.\n for (const b of bandSteps) {\n const terminator = calculateTerminatorContinuous(now, Math.min(b.depression, 89));\n const bandOpacity = b.opacity - prevOpacity;\n if (bandOpacity < 0.002 || terminator.sunset.length === 0) {\n prevOpacity = b.opacity;\n continue;\n }\n\n const poly = buildNightPolygon(terminator);\n if (poly.length < 3) continue;\n\n [0, 360, -360].forEach((offset) => {\n const shifted = poly.map(([lat, lon]) => [lat, lon + offset] as [number, number]);\n addLayer(L.polygon(shifted, {\n color: 'transparent',\n fillColor: '#000a20',\n fillOpacity: bandOpacity,\n interactive: false,\n }));\n });\n \n prevOpacity = b.opacity;\n }\n\n // Draw a smooth terminator boundary line (0° depression)\n // — a thin, continuous stroke showing the day/night geometric edge.\n const terminatorEdge = calculateTerminatorContinuous(now, 0);\n if (terminatorEdge.sunset.length > 2) {\n const sunsetLine = terminatorEdge.sunset.map(([lat, lon]) => [lat, lon] as [number, number]);\n const sunriseLine = terminatorEdge.sunrise.map(([lat, lon]) => [lat, lon] as [number, number]);\n [0, 360, -360].forEach((offset) => {\n // Outer glow (wider, more transparent)\n addLayer(L.polyline(\n sunsetLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#5a8ec8', weight: 4, opacity: 0.12, interactive: false, smoothFactor: 1.5 }\n ));\n addLayer(L.polyline(\n sunriseLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#5a8ec8', weight: 4, opacity: 0.12, interactive: false, smoothFactor: 1.5 }\n ));\n // Core line\n addLayer(L.polyline(\n sunsetLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#7aa4d4', weight: 1, opacity: 0.5, interactive: false, smoothFactor: 1.5 }\n ));\n addLayer(L.polyline(\n sunriseLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#7aa4d4', weight: 1, opacity: 0.5, interactive: false, smoothFactor: 1.5 }\n ));\n });\n }\n }\n\n // === Light Sources (rendered as glowing markers on the night side) ===\n if (lightSources && lightSources.length > 0 && showTerminator) {\n const now = terminatorTime ?? new Date();\n const dayOfYear = Math.floor((now.getTime() - new Date(now.getFullYear(), 0, 0).getTime()) / 86400000);\n const declination = -23.44 * Math.cos((2 * Math.PI / 365) * (dayOfYear + 10));\n const decRad = (declination * Math.PI) / 180;\n const hourAngle = ((now.getUTCHours() + now.getUTCMinutes() / 60) / 24) * 360 - 180;\n\n for (const light of lightSources) {\n const latRad = (light.latitude * Math.PI) / 180;\n const sinAlt = Math.sin(latRad) * Math.sin(decRad) +\n Math.cos(latRad) * Math.cos(decRad) *\n Math.cos(((light.longitude - hourAngle) * Math.PI) / 180);\n const solarAltDeg = (Math.asin(sinAlt) * 180) / Math.PI;\n\n let nightFactor: number;\n if (solarAltDeg >= 0) nightFactor = 0;\n else if (solarAltDeg <= -18) nightFactor = 1;\n else nightFactor = Math.min(1, -solarAltDeg / 18);\n\n if (nightFactor < 0.01) continue;\n\n const r = light.radius ?? 4;\n const intensity = light.intensity ?? 0.8;\n const color = light.color ?? '#ffeedd';\n const alpha = intensity * nightFactor;\n\n // Outer glow circle\n const glowMarker = L.circleMarker([light.latitude, light.longitude], {\n radius: r * 2,\n fillColor: color,\n fillOpacity: alpha * 0.4,\n color: color,\n weight: 0,\n interactive: !!light.label,\n });\n addLayer(glowMarker);\n\n // Bright core\n const coreMarker = L.circleMarker([light.latitude, light.longitude], {\n radius: Math.max(1, r * 0.5),\n fillColor: color,\n fillOpacity: Math.min(1, alpha * 1.2),\n color: 'transparent',\n weight: 0,\n interactive: false,\n });\n addLayer(coreMarker);\n\n if (light.label) {\n glowMarker.bindTooltip(light.label, {\n permanent: false,\n direction: 'top',\n className: 'zendir-leaflet-tooltip',\n });\n }\n }\n }\n\n if (showGrid) {\n const gridStyle = { color: 'rgba(157, 112, 255, 0.12)', weight: 0.5, interactive: false };\n [-360, 0, 360].forEach((offset) => {\n for (let lon = -180; lon <= 180; lon += 30) {\n const l = lon + offset;\n addLayer(L.polyline([[90, l], [-90, l]], gridStyle));\n }\n for (let lat = -90; lat <= 90; lat += 30) {\n addLayer(L.polyline([[lat, -180 + offset], [lat, 180 + offset]], gridStyle));\n }\n });\n }\n\n if (showEquator) {\n [-360, 0, 360].forEach((offset) => {\n addLayer(L.polyline([[0, -180 + offset], [0, 180 + offset]], {\n color: 'rgba(88, 166, 255, 0.25)',\n weight: 1,\n dashArray: '6, 4',\n interactive: false,\n }));\n });\n }\n\n groundStations.forEach((gs) => {\n const status = ('status' in gs ? gs.status : undefined) ?? 'standby';\n const statusColor = STATUS_COLOR_MAP[status] || STATUS_COLOR_MAP.standby;\n const stationType = ('type' in gs ? (gs as { type?: string }).type : undefined) ?? 'dish';\n const radius = 'coverageRadius' in gs ? gs.coverageRadius : undefined;\n const showCoverage = 'showCoverage' in gs ? gs.showCoverage : false;\n if (radius != null && radius > 0 && showCoverage) {\n const ring = geodesicCirclePoints(gs.latitude, gs.longitude, radius, 64);\n const rings = splitRingAtAntimeridian(ring);\n [0, 360, -360].forEach((offset) => {\n rings.forEach((r) => {\n const shifted = r.map(([lat, lon]) => [lat, lon + offset] as [number, number]);\n addLayer(L.polygon(shifted, {\n color: `${statusColor}55`,\n fillColor: statusColor,\n fillOpacity: 0.1,\n weight: 1,\n dashArray: '4, 3',\n interactive: false,\n }));\n });\n });\n }\n\n const gsIcon = createStationDivIcon(statusColor, stationType, status);\n const marker = L.marker([gs.latitude, gs.longitude], { icon: gsIcon });\n addLayer(marker);\n const statusLabel = status !== 'standby' ? ` · ${status}` : '';\n marker.bindTooltip(\n `<strong>${gs.name}</strong>${statusLabel}${'network' in gs && gs.network ? `<br/><span style=\"opacity:0.7\">${gs.network}</span>` : ''}`,\n { permanent: false, direction: 'top', className: 'zendir-leaflet-tooltip' }\n );\n marker.on('click', () => {\n onStationClick?.('id' in gs ? String(gs.id) : gs.name);\n });\n });\n\n allSatellites.forEach((sat, satIdx) => {\n const track = sat.groundTrack;\n if (!track || track.length === 0) return;\n\n const color = sat.color || DEFAULT_TRACK_COLORS[satIdx % DEFAULT_TRACK_COLORS.length];\n const futureIdx = sat.futureTrackIndex ?? track.length;\n\n const pastLatLngs = track\n .slice(0, futureIdx)\n .map((p) => [p.latitude, p.longitude] as [number, number]);\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(pastLatLngs)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color, weight: 2.5, opacity: 1 }));\n }\n });\n\n if (futureIdx < track.length) {\n const futureLatLngs = track\n .slice(futureIdx)\n .map((p) => [p.latitude, p.longitude] as [number, number]);\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(futureLatLngs)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color, weight: 1.5, opacity: 0.5, dashArray: '6, 4' }));\n }\n });\n }\n\n // Access mask segments (contact passes) — solid green overlay on track\n if (sat.accessMask && sat.accessMask.some(Boolean)) {\n let segment: [number, number][] = [];\n for (let i = 0; i < track.length; i++) {\n if (sat.accessMask[i]) {\n segment.push([track[i].latitude, track[i].longitude]);\n } else if (segment.length >= 2) {\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(segment)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color: STATUS_COLOR_MAP.normal, weight: 4, opacity: 0.9 }));\n }\n });\n segment = [];\n } else {\n segment = [];\n }\n }\n if (segment.length >= 2) {\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(segment)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color: STATUS_COLOR_MAP.normal, weight: 4, opacity: 0.9 }));\n }\n });\n }\n }\n\n if (sat.passMarkers && sat.passMarkers.length > 0) {\n sat.passMarkers.forEach((pm) => {\n const isAos = pm.type === 'aos';\n const pmColor = isAos ? '#56f000' : '#ff3838';\n const pmMarker = L.circleMarker([pm.latitude, pm.longitude], {\n radius: 5,\n fillColor: pmColor,\n color: `${pmColor}cc`,\n weight: 1.5,\n fillOpacity: 0.9,\n });\n addLayer(pmMarker);\n pmMarker.bindTooltip(`${pm.type.toUpperCase()}${pm.label ? ` – ${pm.label}` : ''}`, {\n permanent: false,\n direction: 'top',\n className: 'zendir-leaflet-tooltip',\n });\n });\n }\n\n if (sat.showFootprint && sat.footprintRadius) {\n const lastIdx = futureIdx > 0 ? Math.min(futureIdx - 1, track.length - 1) : track.length - 1;\n const last = track[lastIdx];\n const ring = geodesicCirclePoints(last.latitude, last.longitude, sat.footprintRadius, 64);\n const rings = splitRingAtAntimeridian(ring);\n [0, 360, -360].forEach((offset) => {\n rings.forEach((r) => {\n const shifted = r.map(([lat, lon]) => [lat, lon + offset] as [number, number]);\n addLayer(L.polygon(shifted, {\n color: `${color}70`,\n fillColor: color,\n fillOpacity: 0.1,\n weight: 1,\n dashArray: '3, 3',\n interactive: false,\n }));\n });\n });\n }\n\n const currentIdx = futureIdx > 0 ? Math.min(futureIdx - 1, track.length - 1) : track.length - 1;\n const current = track[currentIdx];\n const statusColor = STATUS_COLOR_MAP[sat.status || 'normal'] || STATUS_COLOR_MAP.normal;\n\n const satIcon = createSatDivIcon(statusColor, color);\n const satMarker = L.marker([current.latitude, current.longitude], { icon: satIcon });\n addLayer(satMarker);\n satMarker.bindTooltip(\n `<strong style=\"color:${color}\">${sat.name}</strong><br/>` +\n `Lat ${current.latitude.toFixed(3)}° &nbsp; Lon ${current.longitude.toFixed(3)}°` +\n (current.altitude != null ? `<br/>Alt ${current.altitude.toFixed(1)} km` : '') +\n (sat.status ? `<br/><span style=\"color:${statusColor}\">${sat.status.toUpperCase()}</span>` : ''),\n { permanent: false, direction: 'top', className: 'zendir-leaflet-tooltip' }\n );\n satMarker.on('click', () => onSatelliteClick?.(sat.id));\n });\n\n if (showLegend && (allSatellites.length > 0 || groundStations.length > 0)) {\n const Legend = L.Control.extend({\n onAdd() {\n const div = L.DomUtil.create('div', 'leaflet-legend-zendir');\n div.style.cssText = `\n padding: 8px 12px; background: rgba(15,21,32,0.9); border: 1px solid rgba(157,112,255,0.2);\n border-radius: 8px; color: #e4e0f0; font-size: 11px; font-family: Roboto, sans-serif;\n `;\n allSatellites.forEach((s, i) => {\n const c = s.color || DEFAULT_TRACK_COLORS[i % DEFAULT_TRACK_COLORS.length];\n const row = document.createElement('div');\n row.style.cssText = 'display: flex; align-items: center; gap: 6px; margin: 2px 0;';\n const swatch = document.createElement('span');\n swatch.style.cssText = `width:8px;height:8px;border-radius:50%;background:${c};`;\n const label = document.createElement('span');\n label.textContent = s.name;\n row.appendChild(swatch);\n row.appendChild(label);\n div.appendChild(row);\n });\n if (groundStations.length > 0) {\n const row = document.createElement('div');\n row.style.cssText = 'display: flex; align-items: center; gap: 6px; margin: 2px 0; margin-top: 6px;';\n const swatch = document.createElement('span');\n swatch.style.cssText = 'width:8px;height:8px;border-radius:50%;background:#2dccff;';\n const label = document.createElement('span');\n label.textContent = `${groundStations.length} Ground Station${groundStations.length > 1 ? 's' : ''}`;\n row.appendChild(swatch);\n row.appendChild(label);\n div.appendChild(row);\n }\n return div;\n },\n });\n const legend = new Legend({ position: 'topleft' });\n legend.addTo(map);\n controlsRef.current.push(legend);\n }\n\n }, [\n ready,\n allSatellites,\n groundStations,\n showTerminator,\n terminatorTime,\n nightTileUrl,\n lightSources,\n showGrid,\n showEquator,\n showLegend,\n tokens.colors.text.secondary,\n addLayer,\n clearLayers,\n onSatelliteClick,\n onStationClick,\n ]);\n\n // ==========================================================================\n // Pins layer — separate from overlayGroup so pin updates don't rebuild tracks\n // ==========================================================================\n\n useEffect(() => {\n if (!ready || !mapRef.current) return;\n const group = pinsGroupRef.current;\n if (!group) return;\n group.clearLayers();\n\n if (!pins || pins.length === 0) return;\n const accentColor = tokens.colors.accent.primary;\n\n pins.forEach((pin) => {\n const pinColor = pin.color || accentColor;\n\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"36\" viewBox=\"0 0 24 36\">\n <defs>\n <filter id=\"pin-shadow-${pin.id}\" x=\"-30%\" y=\"-10%\" width=\"160%\" height=\"140%\">\n <feDropShadow dx=\"0\" dy=\"2\" stdDeviation=\"2\" flood-color=\"#000\" flood-opacity=\"0.35\"/>\n </filter>\n </defs>\n <g filter=\"url(#pin-shadow-${pin.id})\">\n <path d=\"M12 2 C6.5 2 2 6.5 2 12 C2 19 12 34 12 34 C12 34 22 19 22 12 C22 6.5 17.5 2 12 2Z\"\n fill=\"${pinColor}\" stroke=\"rgba(255,255,255,0.5)\" stroke-width=\"1\"/>\n <circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"#0d1323\" stroke=\"rgba(255,255,255,0.7)\" stroke-width=\"0.8\"/>\n </g>\n </svg>`;\n\n const icon = L.divIcon({\n html: svg,\n className: 'zendir-pin-icon',\n iconSize: [24, 36] as unknown as L.PointExpression,\n iconAnchor: [12, 36] as unknown as L.PointExpression,\n tooltipAnchor: [0, -36] as unknown as L.PointExpression,\n });\n\n const marker = L.marker([pin.latitude, pin.longitude], {\n icon,\n draggable: pinsEditable,\n title: pin.label ?? '',\n });\n\n // Rich tooltip\n const tooltipLines = [\n pin.label ? `<strong>${pin.label}</strong>` : '',\n pin.description ? `<div style=\"opacity:0.7;font-size:11px\">${pin.description}</div>` : '',\n `<div style=\"font-size:10px;opacity:0.5;font-family:monospace\">${pin.latitude.toFixed(4)}°, ${pin.longitude.toFixed(4)}°</div>`,\n pin.createdBy ? `<div style=\"font-size:10px;opacity:0.4;margin-top:2px\">by ${pin.createdBy}</div>` : '',\n ].filter(Boolean).join('');\n marker.bindTooltip(tooltipLines, {\n className: 'zendir-pin-tooltip',\n direction: 'top',\n offset: [0, -4],\n });\n\n if (pinsEditable) {\n // Drag to reposition\n marker.on('dragend', () => {\n const latlng = marker.getLatLng();\n onPinUpdate?.({ ...pin, latitude: latlng.lat, longitude: latlng.lng });\n });\n\n // Right-click to delete\n marker.on('contextmenu', (e: L.LeafletEvent) => {\n L.DomEvent.stopPropagation(e as unknown as Event);\n onPinRemove?.(pin.id);\n });\n }\n\n group.addLayer(marker);\n });\n }, [ready, pins, pinsEditable, onPinUpdate, onPinRemove, tokens.colors.accent.primary]);\n\n // Click-to-add: listen for clicks on the map background when pinsEditable is true\n useEffect(() => {\n if (!ready || !mapRef.current || !pinsEditable || !onPinAdd) return;\n const map = mapRef.current;\n\n const handleClick = (e: L.LeafletMouseEvent) => {\n // Only fire on map background clicks — ignore clicks that bubbled from markers\n if ((e.originalEvent?.target as HTMLElement)?.closest?.('.leaflet-marker-icon')) return;\n onPinAdd({\n latitude: e.latlng.lat,\n longitude: e.latlng.lng,\n label: '',\n color: tokens.colors.accent.primary,\n });\n };\n\n map.on('click', handleClick);\n return () => { map.off('click', handleClick); };\n }, [ready, pinsEditable, onPinAdd, tokens.colors.accent.primary]);\n\n const handleRecenter = useCallback(() => {\n const map = mapRef.current;\n if (!map) return;\n const points: L.LatLngExpression[] = [];\n allSatellites.forEach((s) => {\n s.groundTrack.forEach((p) => points.push([p.latitude, p.longitude]));\n });\n groundStations.forEach((gs) => points.push([gs.latitude, gs.longitude]));\n if (points.length > 0) {\n const bounds = L.latLngBounds(points);\n map.fitBounds(bounds, { padding: [40, 40], maxZoom: 8 });\n } else {\n map.setView(defaultCenter, defaultZoom);\n }\n }, [allSatellites, groundStations, defaultCenter, defaultZoom]);\n\n const resolvedMinHeight = minHeight || (typeof height === 'number' ? `${height}px` : '400px');\n\n const isEmpty = allSatellites.length === 0 && groundStations.length === 0 && (!pins || pins.length === 0);\n\n return (\n <div\n className={`zendir-ground-track-map zendir-ground-track-map-leaflet${pinsEditable ? ' zendir-pins-editable' : ''} ${className}`}\n data-map-backend=\"leaflet\"\n style={{\n width,\n height,\n minHeight: resolvedMinHeight,\n backgroundColor: tokens.colors.background.base,\n borderRadius: 8,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n <div\n ref={containerRef}\n style={{ width: '100%', height: '100%', minHeight: resolvedMinHeight }}\n />\n {isEmpty && (\n <div\n style={{\n position: 'absolute',\n left: '50%',\n top: '50%',\n transform: 'translate(-50%, -50%)',\n color: tokens.colors.text.secondary,\n fontSize: 14,\n pointerEvents: 'none',\n }}\n >\n {emptyMessage}\n </div>\n )}\n {/* Map controls — positioned below the Leaflet zoom controls */}\n {(showRecenterButton || (showMapStyleToggle && !isExplicitTileUrl)) && (\n <div\n className=\"zendir-map-controls\"\n style={{\n position: 'absolute',\n bottom: 28,\n left: 10,\n zIndex: 1000,\n display: 'flex',\n flexDirection: 'row',\n gap: 6,\n alignItems: 'flex-end',\n }}\n >\n {showRecenterButton && (\n <button\n type=\"button\"\n onClick={handleRecenter}\n title=\"Recenter map to fit all assets and ground stations\"\n aria-label=\"Recenter map\"\n style={{\n background: 'rgba(20, 24, 38, 0.92)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n border: '1px solid rgba(120, 100, 180, 0.2)',\n borderRadius: 4,\n color: '#c8c0d8',\n cursor: 'pointer',\n padding: '6px 10px',\n fontSize: 11,\n fontWeight: 500,\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n transition: 'background 0.15s, border-color 0.15s',\n letterSpacing: '0.02em',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = 'rgba(30, 34, 52, 0.95)';\n e.currentTarget.style.borderColor = 'rgba(157, 112, 255, 0.4)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = 'rgba(20, 24, 38, 0.92)';\n e.currentTarget.style.borderColor = 'rgba(120, 100, 180, 0.2)';\n }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M12 2v4M12 18v4M2 12h4M18 12h4\" />\n </svg>\n Recenter\n </button>\n )}\n {showMapStyleToggle && !isExplicitTileUrl && (\n <div\n style={{\n background: 'rgba(20, 24, 38, 0.92)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n border: '1px solid rgba(120, 100, 180, 0.2)',\n borderRadius: 4,\n display: 'flex',\n overflow: 'hidden',\n }}\n >\n {(['dark', 'satellite'] as const).map((style) => (\n <button\n key={style}\n type=\"button\"\n onClick={() => setTileStyle(style)}\n title={style === 'dark' ? 'Dark ops map' : 'Satellite imagery'}\n aria-label={style === 'dark' ? 'Dark map style' : 'Satellite imagery'}\n aria-pressed={tileStyle === style}\n style={{\n background: tileStyle === style ? 'rgba(157, 112, 255, 0.22)' : 'transparent',\n border: 'none',\n borderRight: style === 'dark' ? '1px solid rgba(120, 100, 180, 0.15)' : 'none',\n color: tileStyle === style ? '#d0c4ee' : '#7a748e',\n cursor: 'pointer',\n padding: '5px 8px',\n fontSize: 10,\n fontWeight: tileStyle === style ? 600 : 400,\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n transition: 'all 0.15s ease',\n letterSpacing: '0.02em',\n }}\n >\n {style === 'dark' ? (\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\">\n <path d=\"M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z\" />\n </svg>\n ) : (\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z\" />\n </svg>\n )}\n {style === 'dark' ? 'Dark' : 'Satellite'}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport default GroundTrackMapLeaflet;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GroundTrackMapLeaflet.js","sources":["../../../src/react/charts/GroundTrackMapLeaflet.tsx"],"sourcesContent":["/**\n * @zendir/ui - GroundTrackMap Leaflet Provider\n *\n * Leaflet-based 2D world map for ground tracks, stations, and terminator.\n * Uses Zendir hybrid theme; all interactions enabled (zoom, scroll, drag, etc.).\n * Import leaflet-zendir.css when using this component.\n */\n\nimport React, { useRef, useEffect, useCallback, useMemo, useState } from 'react';\nimport { useTheme } from '../theme';\nimport type { GroundStation } from '../types';\nimport type { SatelliteTrack, GroundStationEnhanced, SROGroundStation, MapPin, LightSource } from './GroundTrackMap';\nimport type { MapLayerDef } from './GroundTrackMap';\n\n// Optional Leaflet — consumer must install leaflet when using mapProvider=\"leaflet\"\nimport L from 'leaflet';\nimport 'leaflet/dist/leaflet.css';\nimport './leaflet-zendir.css';\nimport {\n splitPolylineAtAntimeridian,\n segmentsWithWorldCopies,\n geodesicCirclePoints,\n splitRingAtAntimeridian,\n} from './groundTrackMapLeafletUtils';\nimport {\n DEFAULT_TILE,\n FALLBACK_TILE,\n TILE_ERROR_THRESHOLD,\n OSM_ATTRIBUTION,\n TILE_PRESETS,\n getBasemapAttribution,\n} from './groundTrackMapLeafletTiles';\n\nconst STATUS_COLOR_MAP: Record<string, string> = {\n normal: '#56f000',\n standby: '#2dccff',\n caution: '#fce83a',\n serious: '#ffb302',\n critical: '#ff3838',\n off: '#a4abb6',\n};\n\nconst DEFAULT_TRACK_COLORS = [\n '#2dccff', '#3E3CFF', '#9D70FF', '#56f000', '#fce83a', '#ff7849', '#2dd4bf', '#ff3838',\n];\n\n// =============================================================================\n// Icon helpers — produce L.DivIcon with inline SVG (no external assets needed)\n// =============================================================================\n\n/** Satellite spacecraft icon with solar panels, matching Astro UXDS style. */\nfunction createSatDivIcon(statusColor: string, trackColor: string): L.DivIcon {\n // Status badge shape (Astro UXDS spec)\n const badge = statusColor === '#56f000'\n ? `<circle cx=\"7\" cy=\"7\" r=\"3\" fill=\"${statusColor}\"/>` // normal: filled dot\n : statusColor === '#2dccff'\n ? `<circle cx=\"7\" cy=\"7\" r=\"2.5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\"/>` // standby: ring\n : statusColor === '#fce83a'\n ? `<rect x=\"4.5\" y=\"4.5\" width=\"5\" height=\"5\" fill=\"${statusColor}\"/>` // caution: square\n : statusColor === '#ffb302'\n ? `<polygon points=\"7,3.5 10.5,7 7,10.5 3.5,7\" fill=\"${statusColor}\"/>` // serious: diamond (Astro UXDS)\n : statusColor === '#ff3838'\n ? `<polygon points=\"7,10 4,4 10,4\" fill=\"${statusColor}\"/>` // critical: triangle down\n : `<circle cx=\"7\" cy=\"7\" r=\"2\" fill=\"${statusColor}\"/>`; // off: small dot\n\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"34\" height=\"34\" viewBox=\"0 0 34 34\">\n <!-- Glow ring -->\n <circle cx=\"17\" cy=\"17\" r=\"15\" fill=\"${statusColor}\" fill-opacity=\"0.12\"/>\n <!-- Main body circle -->\n <circle cx=\"17\" cy=\"17\" r=\"11\" fill=\"#0d1323\" stroke=\"${trackColor}\" stroke-width=\"1.5\"/>\n <!-- Left solar panel -->\n <rect x=\"2\" y=\"15\" width=\"9\" height=\"4\" rx=\"0.5\" fill=\"${trackColor}\" fill-opacity=\"0.75\"/>\n <line x1=\"5.5\" y1=\"15\" x2=\"5.5\" y2=\"19\" stroke=\"${trackColor}\" stroke-width=\"0.5\" stroke-opacity=\"0.45\"/>\n <!-- Connector left -->\n <line x1=\"11\" y1=\"17\" x2=\"13\" y2=\"17\" stroke=\"${trackColor}\" stroke-width=\"1.2\"/>\n <!-- Satellite bus body -->\n <rect x=\"13\" y=\"14.5\" width=\"8\" height=\"5\" rx=\"1\" fill=\"${statusColor}\"/>\n <!-- Connector right -->\n <line x1=\"21\" y1=\"17\" x2=\"23\" y2=\"17\" stroke=\"${trackColor}\" stroke-width=\"1.2\"/>\n <!-- Right solar panel -->\n <rect x=\"23\" y=\"15\" width=\"9\" height=\"4\" rx=\"0.5\" fill=\"${trackColor}\" fill-opacity=\"0.75\"/>\n <line x1=\"26.5\" y1=\"15\" x2=\"26.5\" y2=\"19\" stroke=\"${trackColor}\" stroke-width=\"0.5\" stroke-opacity=\"0.45\"/>\n <!-- Status badge (top-left) -->\n <circle cx=\"7\" cy=\"7\" r=\"5.5\" fill=\"#0d1323\"/>\n ${badge}\n </svg>`;\n\n return L.divIcon({\n html: svg,\n className: 'zendir-sat-icon',\n iconSize: [34, 34] as unknown as L.PointExpression,\n iconAnchor: [17, 17] as unknown as L.PointExpression,\n tooltipAnchor: [0, -18] as unknown as L.PointExpression,\n });\n}\n\n/** Ground station icon — dish/antenna shape, matching Astro UXDS style. */\nfunction createStationDivIcon(statusColor: string, type: string, status: string): L.DivIcon {\n // Status badge\n const badge = status === 'normal'\n ? `<circle cx=\"5\" cy=\"5\" r=\"2.5\" fill=\"${statusColor}\"/>`\n : status === 'standby'\n ? `<circle cx=\"5\" cy=\"5\" r=\"2\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>`\n : status === 'caution'\n ? `<rect x=\"3\" y=\"3\" width=\"4\" height=\"4\" fill=\"${statusColor}\"/>`\n : status === 'serious'\n ? `<polygon points=\"5,2.5 7.5,5 5,7.5 2.5,5\" fill=\"${statusColor}\"/>`\n : status === 'critical'\n ? `<polygon points=\"5,7.5 2.5,2.5 7.5,2.5\" fill=\"${statusColor}\"/>`\n : `<circle cx=\"5\" cy=\"5\" r=\"1.5\" fill=\"${statusColor}\"/>`;\n\n let iconInner = '';\n if (type === 'phased-array') {\n // Grid pattern (phased array)\n iconInner = `\n <rect x=\"9\" y=\"7\" width=\"10\" height=\"8\" rx=\"0.5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"12\" y1=\"7\" x2=\"12\" y2=\"15\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"15\" y1=\"7\" x2=\"15\" y2=\"15\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"9\" y1=\"10\" x2=\"19\" y2=\"10\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"9\" y1=\"13\" x2=\"19\" y2=\"13\" stroke=\"${statusColor}\" stroke-width=\"0.7\"/>\n <line x1=\"14\" y1=\"15\" x2=\"14\" y2=\"20\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"11\" y1=\"20\" x2=\"17\" y2=\"20\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>`;\n } else if (type === 'relay') {\n // Relay dish with signal arcs\n iconInner = `\n <path d=\"M 8 16 a 7 7 0 0 1 7 -7\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M 8 16 m 2 -2 a 5 5 0 0 1 5 -5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>\n <line x1=\"14\" y1=\"16\" x2=\"14\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"11\" y1=\"21\" x2=\"17\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <path d=\"M 16 8 a 3 3 0 0 1 0 4\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>\n <path d=\"M 17.5 6.5 a 5 5 0 0 1 0 7\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1\" stroke-linecap=\"round\"/>`;\n } else {\n // Default: dish antenna with concentric arcs (Astro UXDS antenna pattern)\n iconInner = `\n <path d=\"M 14 14 m -7 0 a 7 7 0 0 1 7 -7\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M 14 14 m -5 0 a 5 5 0 0 1 5 -5\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <path d=\"M 14 14 m -3 0 a 3 3 0 0 1 3 -3\" fill=\"none\" stroke=\"${statusColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <circle cx=\"14\" cy=\"14\" r=\"1.5\" fill=\"${statusColor}\"/>\n <line x1=\"14\" y1=\"15\" x2=\"14\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>\n <line x1=\"11\" y1=\"21\" x2=\"17\" y2=\"21\" stroke=\"${statusColor}\" stroke-width=\"1.2\"/>`;\n }\n\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"28\" height=\"28\" viewBox=\"0 0 28 28\">\n <!-- Glow ring -->\n <circle cx=\"14\" cy=\"14\" r=\"13\" fill=\"${statusColor}\" fill-opacity=\"0.1\"/>\n <!-- Background -->\n <circle cx=\"14\" cy=\"14\" r=\"11\" fill=\"#0d1323\" stroke=\"${statusColor}\" stroke-width=\"1.2\" stroke-opacity=\"0.7\"/>\n ${iconInner}\n <!-- Status badge (top-left) -->\n <circle cx=\"5\" cy=\"5\" r=\"5\" fill=\"#0d1323\"/>\n ${badge}\n </svg>`;\n\n return L.divIcon({\n html: svg,\n className: 'zendir-station-icon',\n iconSize: [28, 28] as unknown as L.PointExpression,\n iconAnchor: [14, 14] as unknown as L.PointExpression,\n tooltipAnchor: [0, -14] as unknown as L.PointExpression,\n });\n}\n\n/**\n * Compute a terminator line with CONTINUOUS (unwrapped) longitudes.\n *\n * `depressionDeg` offsets the solar zenith angle to produce twilight\n * boundaries instead of the geometric sunset line:\n * - 0° = geometric sunset/sunrise (day/night boundary)\n * - 6° = civil twilight (horizon still visible, outdoor activities possible)\n * - 12° = nautical twilight (horizon barely visible, stars appearing)\n * - 18° = astronomical twilight (sky fully dark for observation)\n *\n * These thresholds follow IAU/USNO standard definitions used in celestial\n * navigation, Astro UX space ops dashboards, and STK/GMAT mission tools.\n */\nfunction calculateTerminatorContinuous(\n date: Date,\n depressionDeg: number = 0,\n numPoints: number = 360,\n): { sunset: Array<[number, number]>, sunrise: Array<[number, number]> } {\n const dayOfYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000);\n const declination = -23.44 * Math.cos((2 * Math.PI / 365) * (dayOfYear + 10));\n const decRad = (declination * Math.PI) / 180;\n const hourAngle = ((date.getUTCHours() + date.getUTCMinutes() / 60) / 24) * 360 - 180;\n const depRad = (depressionDeg * Math.PI) / 180;\n\n const sunset: Array<[number, number]> = [];\n const sunrise: Array<[number, number]> = [];\n\n for (let i = 0; i <= numPoints; i++) {\n // Avoid exact +/- 90 to prevent longitude singularities at the poles\n const lat = -89.999 + (i / numPoints) * 179.998;\n const latRad = lat * (Math.PI / 180);\n const cosH = -(Math.sin(depRad) + Math.sin(latRad) * Math.sin(decRad))\n / (Math.cos(latRad) * Math.cos(decRad));\n \n if (cosH < -1) {\n // All day. No points.\n } else if (cosH > 1) {\n // All night. Covers all longitudes.\n // We map the night as the 360-degree span starting at hourAngle.\n sunset.push([lat, hourAngle]);\n sunrise.push([lat, hourAngle + 360]);\n } else {\n // Normal twilight\n const H = (Math.acos(cosH) * 180) / Math.PI;\n sunset.push([lat, hourAngle + H]);\n // Sunrise is 360 - H degrees East of the sun (which is equivalent to West of the sun,\n // but keeping it > sunset ensures the night polygon is continuous Eastward).\n sunrise.push([lat, hourAngle + 360 - H]);\n }\n }\n \n return { sunset, sunrise };\n}\n\n/**\n * Build the full night polygon, properly closing the loops around the poles.\n * Ensures longitudes are strictly continuous so the map projection doesn't\n * draw antimeridian lines across the day side.\n */\nfunction buildNightPolygon(\n terminator: { sunset: Array<[number, number]>, sunrise: Array<[number, number]> }\n): [number, number][] {\n const { sunset, sunrise } = terminator;\n if (!sunset.length || !sunrise.length) return [];\n\n const poly: [number, number][] = [];\n\n // Left edge of night (sunset): South to North\n poly.push(...sunset);\n\n // Right edge of night (sunrise): North to South\n poly.push(...[...sunrise].reverse());\n\n // Leaflet will automatically close the polygon by connecting the last point \n // (South end of sunrise) back to the first point (South end of sunset).\n // Because longitudes are continuous and bounded (sunrise.lon - sunset.lon <= 360),\n // this closing line will simply span the bottom or top of the map, perfectly\n // enclosing the night area without crossing the day side!\n\n return poly;\n}\n\nexport type GroundStationMapItem = GroundStation | GroundStationEnhanced | SROGroundStation;\n\nexport interface GroundTrackMapLeafletProps {\n allSatellites: SatelliteTrack[];\n groundStations: GroundStationMapItem[];\n showTerminator?: boolean;\n /** Override the time used for the terminator calculation (defaults to wall-clock now). */\n terminatorTime?: Date;\n showGrid?: boolean;\n showLegend?: boolean;\n showEquator?: boolean;\n showRecenterButton?: boolean;\n /** Show a toggle button to switch between Dark and Satellite tile styles. Default true. */\n showMapStyleToggle?: boolean;\n defaultCenter?: [number, number];\n defaultZoom?: number;\n height?: number | string;\n width?: number | string;\n minHeight?: string;\n emptyMessage?: string;\n tileUrl?: string;\n /**\n * URL template for a \"night\" tile layer (e.g. NASA Black Marble, VIIRS city lights).\n * Rendered beneath the terminator overlay so it only appears on the dark side.\n * Requires `showTerminator`. Falls back to dark overlay when not provided.\n */\n nightTileUrl?: string;\n /**\n * Point light sources rendered on the night side (masked by the terminator).\n * Each light fades in through twilight and reaches full brightness at night.\n */\n lightSources?: LightSource[];\n className?: string;\n onSatelliteClick?: (id: string) => void;\n onStationClick?: (id: string) => void;\n /** Points-of-interest pins */\n pins?: MapPin[];\n /** Allow adding/editing pins via map clicks */\n pinsEditable?: boolean;\n onPinAdd?: (pin: Omit<MapPin, 'id'>) => void;\n onPinUpdate?: (pin: MapPin) => void;\n onPinRemove?: (pinId: string) => void;\n /** Custom overlay layers for the Layers panel */\n customLayers?: MapLayerDef[];\n /** Called when any layer is toggled */\n onLayerChange?: (layerId: string, enabled: boolean) => void;\n}\n\nexport function GroundTrackMapLeaflet({\n allSatellites,\n groundStations,\n showTerminator = true,\n terminatorTime,\n showGrid = false,\n showLegend = true,\n showEquator = false,\n showRecenterButton = true,\n showMapStyleToggle = true,\n defaultCenter = [20, 0],\n defaultZoom = 2,\n height = '100%',\n width = '100%',\n minHeight = '400px',\n emptyMessage = 'No orbital data available',\n tileUrl,\n nightTileUrl,\n lightSources,\n className = '',\n onSatelliteClick,\n onStationClick,\n pins,\n pinsEditable = false,\n onPinAdd,\n onPinUpdate,\n onPinRemove,\n customLayers,\n onLayerChange,\n}: GroundTrackMapLeafletProps): React.ReactElement {\n const { tokens } = useTheme();\n const containerRef = useRef<HTMLDivElement>(null);\n const mapRef = useRef<L.Map | null>(null);\n const tileLayerRef = useRef<L.TileLayer | null>(null);\n const overlayGroupRef = useRef<L.LayerGroup | null>(null);\n const controlsRef = useRef<L.Control[]>([]);\n /** Separate layer group so pin updates don't clear satellite/station layers */\n const pinsGroupRef = useRef<L.LayerGroup | null>(null);\n const [ready, setReady] = useState(false);\n\n // ── Layers panel open/close ──\n const [layersPanelOpen, setLayersPanelOpen] = useState(false);\n const layersPanelRef = useRef<HTMLDivElement>(null);\n\n // Close layers panel when clicking outside\n useEffect(() => {\n if (!layersPanelOpen) return;\n const handleClickOutside = (e: MouseEvent) => {\n if (layersPanelRef.current && !layersPanelRef.current.contains(e.target as Node)) {\n setLayersPanelOpen(false);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [layersPanelOpen]);\n\n // ── Tile style with localStorage persistence ──\n const TILE_STORAGE_KEY = 'zendir-map-tile-style';\n const isExplicitTileUrl = tileUrl !== undefined;\n const [tileStyle, setTileStyle] = useState<'dark' | 'satellite'>(() => {\n if (isExplicitTileUrl) return 'dark';\n try {\n const saved = localStorage.getItem(TILE_STORAGE_KEY);\n if (saved === 'dark' || saved === 'satellite') return saved;\n } catch { /* SSR / storage blocked */ }\n return 'dark';\n });\n\n const handleTileStyleChange = useCallback((style: 'dark' | 'satellite') => {\n setTileStyle(style);\n try { localStorage.setItem(TILE_STORAGE_KEY, style); } catch { /* noop */ }\n }, []);\n\n const effectiveTileUrl = isExplicitTileUrl ? tileUrl : TILE_PRESETS[tileStyle];\n\n // ── Internal overlay toggle state (built-in layers: terminator, grid) ──\n const [internalTerminator, setInternalTerminator] = useState(showTerminator);\n const [internalGrid, setInternalGrid] = useState(showGrid);\n\n // Sync with prop changes (props are the initial source of truth)\n useEffect(() => { setInternalTerminator(showTerminator); }, [showTerminator]);\n useEffect(() => { setInternalGrid(showGrid); }, [showGrid]);\n\n // ── Custom layer toggle state ──\n const [customLayerState, setCustomLayerState] = useState<Record<string, boolean>>(() => {\n const state: Record<string, boolean> = {};\n for (const l of customLayers ?? []) {\n state[l.id] = l.defaultEnabled ?? true;\n }\n return state;\n });\n\n // Sync when customLayers prop changes (add new layers with defaults)\n useEffect(() => {\n setCustomLayerState((prev) => {\n const next = { ...prev };\n for (const l of customLayers ?? []) {\n if (!(l.id in next)) next[l.id] = l.defaultEnabled ?? true;\n }\n return next;\n });\n }, [customLayers]);\n\n const handleLayerToggle = useCallback((layerId: string, enabled: boolean) => {\n if (layerId === 'terminator') {\n setInternalTerminator(enabled);\n } else if (layerId === 'grid') {\n setInternalGrid(enabled);\n } else {\n setCustomLayerState((prev) => ({ ...prev, [layerId]: enabled }));\n }\n onLayerChange?.(layerId, enabled);\n }, [onLayerChange]);\n\n const clearLayers = useCallback(() => {\n const map = mapRef.current;\n if (!map) return;\n overlayGroupRef.current?.clearLayers();\n controlsRef.current.forEach((ctrl) => map.removeControl(ctrl));\n controlsRef.current = [];\n }, []);\n\n const addLayer = useCallback((layer: L.Layer) => {\n overlayGroupRef.current?.addLayer(layer);\n }, []);\n\n // Create map once on mount (or when tileUrl changes). Intentionally not depending on\n // defaultCenter/defaultZoom so animated updates do not recreate the map (no flicker/zoom reset).\n useEffect(() => {\n // eslint-disable-next-line react-hooks/exhaustive-deps -- defaultCenter/defaultZoom used only for initial view\n const el = containerRef.current;\n if (!el) return;\n\n const center: L.LatLngExpression = defaultCenter ?? [20, 0];\n const zoom = defaultZoom ?? 2;\n\n const map = L.map(el, {\n center,\n zoom,\n zoomControl: false,\n scrollWheelZoom: true,\n doubleClickZoom: true,\n touchZoom: true,\n boxZoom: true,\n keyboard: true,\n dragging: true,\n attributionControl: true,\n maxBounds: [[-90, -540], [90, 540]],\n maxBoundsViscosity: 1.0,\n });\n\n map.attributionControl.setPrefix('');\n\n const overlayGroup = L.layerGroup();\n overlayGroup.addTo(map);\n overlayGroupRef.current = overlayGroup;\n\n const pinsGroup = L.layerGroup();\n pinsGroup.addTo(map);\n pinsGroupRef.current = pinsGroup;\n\n mapRef.current = map;\n setReady(true);\n\n const onSize = () => {\n map.invalidateSize({ animate: false });\n };\n // Immediate RAF + two deferred calls cover: first paint, CSS transitions, flex layout settle\n const raf = requestAnimationFrame(onSize);\n const t1 = setTimeout(onSize, 150);\n const t2 = setTimeout(onSize, 500);\n\n return () => {\n clearTimeout(t1);\n clearTimeout(t2);\n cancelAnimationFrame(raf);\n clearLayers();\n pinsGroupRef.current?.clearLayers();\n pinsGroupRef.current = null;\n tileLayerRef.current = null;\n map.remove();\n mapRef.current = null;\n overlayGroupRef.current = null;\n };\n // Mount-only: tile layer is swapped by a dedicated effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clearLayers]);\n\n // Swap tile layer without recreating the entire map (avoids flash on style toggle).\n useEffect(() => {\n const map = mapRef.current;\n if (!map || !ready) return;\n\n // Remove existing tile layer (Leaflet drops that layer's attribution with it)\n if (tileLayerRef.current) {\n tileLayerRef.current.remove();\n tileLayerRef.current = null;\n }\n\n const isCartoTiles = effectiveTileUrl.includes('cartocdn');\n const basemapAttribution = getBasemapAttribution(effectiveTileUrl);\n\n const tile = L.tileLayer(effectiveTileUrl, {\n maxZoom: 19,\n subdomains: isCartoTiles ? 'abcd' : 'abc',\n crossOrigin: true,\n attribution: basemapAttribution || undefined,\n });\n tile.addTo(map);\n tile.bringToBack();\n tileLayerRef.current = tile;\n\n // Fallback on tile error\n let hasSwitchedToFallback = false;\n const onTileError = () => {\n if (hasSwitchedToFallback) return;\n hasSwitchedToFallback = true;\n tile.off('tileerror', onTileError);\n tile.remove();\n const fallback = L.tileLayer(FALLBACK_TILE, {\n maxZoom: 19,\n subdomains: 'abc',\n crossOrigin: true,\n attribution: OSM_ATTRIBUTION,\n });\n fallback.addTo(map);\n fallback.bringToBack();\n tileLayerRef.current = fallback;\n };\n tile.on('tileerror', onTileError);\n }, [effectiveTileUrl, ready]);\n\n useEffect(() => {\n if (!ready || !mapRef.current) return;\n const map = mapRef.current;\n clearLayers();\n\n // === Night Tile Layer (placed beneath terminator so night imagery appears on dark side) ===\n if (nightTileUrl && internalTerminator) {\n const nightTile = L.tileLayer(nightTileUrl, {\n maxZoom: 19,\n crossOrigin: true,\n opacity: 0.7,\n className: 'zendir-night-tiles',\n });\n addLayer(nightTile);\n }\n\n if (internalTerminator) {\n const now = terminatorTime ?? new Date();\n\n // Build a smooth graduated terminator using many thin bands (every 2°)\n // instead of hard-edged zones, creating a seamless day→night fade.\n // Depression angles: 0° (geometric sunset) through 24° (deep night).\n const BAND_STEP = 2;\n const MAX_DEP = 24;\n const NIGHT_OPACITY = 0.55;\n const bandSteps: Array<{ depression: number; opacity: number }> = [];\n for (let d = 0; d <= MAX_DEP; d += BAND_STEP) {\n // Smooth quadratic ramp: 0° → 0.0, 18° → ~0.45, 24° → 0.55\n const t = Math.min(d / 18, 1);\n const opacity = t * t * (NIGHT_OPACITY * 0.82) + (d > 18 ? (d - 18) / 6 * (NIGHT_OPACITY * 0.18) : 0);\n bandSteps.push({ depression: d, opacity: Math.min(opacity, NIGHT_OPACITY) });\n }\n // Final deep-night fill for everything beyond the last band\n bandSteps.push({ depression: 90, opacity: NIGHT_OPACITY });\n\n let prevOpacity = 0;\n\n // Draw each band as a cumulative overlay — each adds incremental darkening\n // By stacking full night polygons with small opacities, we avoid drawing\n // complex band-difference polygons, which is much more robust at the poles.\n for (const b of bandSteps) {\n const terminator = calculateTerminatorContinuous(now, Math.min(b.depression, 89));\n const bandOpacity = b.opacity - prevOpacity;\n if (bandOpacity < 0.002 || terminator.sunset.length === 0) {\n prevOpacity = b.opacity;\n continue;\n }\n\n const poly = buildNightPolygon(terminator);\n if (poly.length < 3) continue;\n\n [0, 360, -360].forEach((offset) => {\n const shifted = poly.map(([lat, lon]) => [lat, lon + offset] as [number, number]);\n addLayer(L.polygon(shifted, {\n color: 'transparent',\n fillColor: '#000a20',\n fillOpacity: bandOpacity,\n interactive: false,\n }));\n });\n \n prevOpacity = b.opacity;\n }\n\n // Draw a smooth terminator boundary line (0° depression)\n // — a thin, continuous stroke showing the day/night geometric edge.\n const terminatorEdge = calculateTerminatorContinuous(now, 0);\n if (terminatorEdge.sunset.length > 2) {\n const sunsetLine = terminatorEdge.sunset.map(([lat, lon]) => [lat, lon] as [number, number]);\n const sunriseLine = terminatorEdge.sunrise.map(([lat, lon]) => [lat, lon] as [number, number]);\n [0, 360, -360].forEach((offset) => {\n // Outer glow (wider, more transparent)\n addLayer(L.polyline(\n sunsetLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#5a8ec8', weight: 4, opacity: 0.12, interactive: false, smoothFactor: 1.5 }\n ));\n addLayer(L.polyline(\n sunriseLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#5a8ec8', weight: 4, opacity: 0.12, interactive: false, smoothFactor: 1.5 }\n ));\n // Core line\n addLayer(L.polyline(\n sunsetLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#7aa4d4', weight: 1, opacity: 0.5, interactive: false, smoothFactor: 1.5 }\n ));\n addLayer(L.polyline(\n sunriseLine.map(([lat, lon]) => [lat, lon + offset] as [number, number]),\n { color: '#7aa4d4', weight: 1, opacity: 0.5, interactive: false, smoothFactor: 1.5 }\n ));\n });\n }\n }\n\n // === Light Sources (rendered as glowing markers on the night side) ===\n if (lightSources && lightSources.length > 0 && internalTerminator) {\n const now = terminatorTime ?? new Date();\n const dayOfYear = Math.floor((now.getTime() - new Date(now.getFullYear(), 0, 0).getTime()) / 86400000);\n const declination = -23.44 * Math.cos((2 * Math.PI / 365) * (dayOfYear + 10));\n const decRad = (declination * Math.PI) / 180;\n const hourAngle = ((now.getUTCHours() + now.getUTCMinutes() / 60) / 24) * 360 - 180;\n\n for (const light of lightSources) {\n const latRad = (light.latitude * Math.PI) / 180;\n const sinAlt = Math.sin(latRad) * Math.sin(decRad) +\n Math.cos(latRad) * Math.cos(decRad) *\n Math.cos(((light.longitude - hourAngle) * Math.PI) / 180);\n const solarAltDeg = (Math.asin(sinAlt) * 180) / Math.PI;\n\n let nightFactor: number;\n if (solarAltDeg >= 0) nightFactor = 0;\n else if (solarAltDeg <= -18) nightFactor = 1;\n else nightFactor = Math.min(1, -solarAltDeg / 18);\n\n if (nightFactor < 0.01) continue;\n\n const r = light.radius ?? 4;\n const intensity = light.intensity ?? 0.8;\n const color = light.color ?? '#ffeedd';\n const alpha = intensity * nightFactor;\n\n // Outer glow circle\n const glowMarker = L.circleMarker([light.latitude, light.longitude], {\n radius: r * 2,\n fillColor: color,\n fillOpacity: alpha * 0.4,\n color: color,\n weight: 0,\n interactive: !!light.label,\n });\n addLayer(glowMarker);\n\n // Bright core\n const coreMarker = L.circleMarker([light.latitude, light.longitude], {\n radius: Math.max(1, r * 0.5),\n fillColor: color,\n fillOpacity: Math.min(1, alpha * 1.2),\n color: 'transparent',\n weight: 0,\n interactive: false,\n });\n addLayer(coreMarker);\n\n if (light.label) {\n glowMarker.bindTooltip(light.label, {\n permanent: false,\n direction: 'top',\n className: 'zendir-leaflet-tooltip',\n });\n }\n }\n }\n\n if (internalGrid) {\n const gridStyle = { color: 'rgba(157, 112, 255, 0.12)', weight: 0.5, interactive: false };\n [-360, 0, 360].forEach((offset) => {\n for (let lon = -180; lon <= 180; lon += 30) {\n const l = lon + offset;\n addLayer(L.polyline([[90, l], [-90, l]], gridStyle));\n }\n for (let lat = -90; lat <= 90; lat += 30) {\n addLayer(L.polyline([[lat, -180 + offset], [lat, 180 + offset]], gridStyle));\n }\n });\n }\n\n if (showEquator) {\n [-360, 0, 360].forEach((offset) => {\n addLayer(L.polyline([[0, -180 + offset], [0, 180 + offset]], {\n color: 'rgba(88, 166, 255, 0.25)',\n weight: 1,\n dashArray: '6, 4',\n interactive: false,\n }));\n });\n }\n\n groundStations.forEach((gs) => {\n const status = ('status' in gs ? gs.status : undefined) ?? 'standby';\n const statusColor = STATUS_COLOR_MAP[status] || STATUS_COLOR_MAP.standby;\n const stationType = ('type' in gs ? (gs as { type?: string }).type : undefined) ?? 'dish';\n const radius = 'coverageRadius' in gs ? gs.coverageRadius : undefined;\n const showCoverage = 'showCoverage' in gs ? gs.showCoverage : false;\n if (radius != null && radius > 0 && showCoverage) {\n const ring = geodesicCirclePoints(gs.latitude, gs.longitude, radius, 64);\n const rings = splitRingAtAntimeridian(ring);\n [0, 360, -360].forEach((offset) => {\n rings.forEach((r) => {\n const shifted = r.map(([lat, lon]) => [lat, lon + offset] as [number, number]);\n addLayer(L.polygon(shifted, {\n color: `${statusColor}55`,\n fillColor: statusColor,\n fillOpacity: 0.1,\n weight: 1,\n dashArray: '4, 3',\n interactive: false,\n }));\n });\n });\n }\n\n const gsIcon = createStationDivIcon(statusColor, stationType, status);\n const marker = L.marker([gs.latitude, gs.longitude], { icon: gsIcon });\n addLayer(marker);\n const statusLabel = status !== 'standby' ? ` · ${status}` : '';\n marker.bindTooltip(\n `<strong>${gs.name}</strong>${statusLabel}${'network' in gs && gs.network ? `<br/><span style=\"opacity:0.7\">${gs.network}</span>` : ''}`,\n { permanent: false, direction: 'top', className: 'zendir-leaflet-tooltip' }\n );\n marker.on('click', () => {\n onStationClick?.('id' in gs ? String(gs.id) : gs.name);\n });\n });\n\n allSatellites.forEach((sat, satIdx) => {\n const track = sat.groundTrack;\n if (!track || track.length === 0) return;\n\n const color = sat.color || DEFAULT_TRACK_COLORS[satIdx % DEFAULT_TRACK_COLORS.length];\n const futureIdx = sat.futureTrackIndex ?? track.length;\n\n const pastLatLngs = track\n .slice(0, futureIdx)\n .map((p) => [p.latitude, p.longitude] as [number, number]);\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(pastLatLngs)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color, weight: 2.5, opacity: 1 }));\n }\n });\n\n if (futureIdx < track.length) {\n const futureLatLngs = track\n .slice(futureIdx)\n .map((p) => [p.latitude, p.longitude] as [number, number]);\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(futureLatLngs)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color, weight: 1.5, opacity: 0.5, dashArray: '6, 4' }));\n }\n });\n }\n\n // Access mask segments (contact passes) — solid green overlay on track\n if (sat.accessMask && sat.accessMask.some(Boolean)) {\n let segment: [number, number][] = [];\n for (let i = 0; i < track.length; i++) {\n if (sat.accessMask[i]) {\n segment.push([track[i].latitude, track[i].longitude]);\n } else if (segment.length >= 2) {\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(segment)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color: STATUS_COLOR_MAP.normal, weight: 4, opacity: 0.9 }));\n }\n });\n segment = [];\n } else {\n segment = [];\n }\n }\n if (segment.length >= 2) {\n segmentsWithWorldCopies(splitPolylineAtAntimeridian(segment)).forEach((seg) => {\n if (seg.length >= 2) {\n addLayer(L.polyline(seg, { color: STATUS_COLOR_MAP.normal, weight: 4, opacity: 0.9 }));\n }\n });\n }\n }\n\n if (sat.passMarkers && sat.passMarkers.length > 0) {\n sat.passMarkers.forEach((pm) => {\n const isAos = pm.type === 'aos';\n const pmColor = isAos ? '#56f000' : '#ff3838';\n const pmMarker = L.circleMarker([pm.latitude, pm.longitude], {\n radius: 5,\n fillColor: pmColor,\n color: `${pmColor}cc`,\n weight: 1.5,\n fillOpacity: 0.9,\n });\n addLayer(pmMarker);\n pmMarker.bindTooltip(`${pm.type.toUpperCase()}${pm.label ? ` – ${pm.label}` : ''}`, {\n permanent: false,\n direction: 'top',\n className: 'zendir-leaflet-tooltip',\n });\n });\n }\n\n if (sat.showFootprint && sat.footprintRadius) {\n const lastIdx = futureIdx > 0 ? Math.min(futureIdx - 1, track.length - 1) : track.length - 1;\n const last = track[lastIdx];\n const ring = geodesicCirclePoints(last.latitude, last.longitude, sat.footprintRadius, 64);\n const rings = splitRingAtAntimeridian(ring);\n [0, 360, -360].forEach((offset) => {\n rings.forEach((r) => {\n const shifted = r.map(([lat, lon]) => [lat, lon + offset] as [number, number]);\n addLayer(L.polygon(shifted, {\n color: `${color}70`,\n fillColor: color,\n fillOpacity: 0.1,\n weight: 1,\n dashArray: '3, 3',\n interactive: false,\n }));\n });\n });\n }\n\n const currentIdx = futureIdx > 0 ? Math.min(futureIdx - 1, track.length - 1) : track.length - 1;\n const current = track[currentIdx];\n const statusColor = STATUS_COLOR_MAP[sat.status || 'normal'] || STATUS_COLOR_MAP.normal;\n\n const satIcon = createSatDivIcon(statusColor, color);\n const satMarker = L.marker([current.latitude, current.longitude], { icon: satIcon });\n addLayer(satMarker);\n satMarker.bindTooltip(\n `<strong style=\"color:${color}\">${sat.name}</strong><br/>` +\n `Lat ${current.latitude.toFixed(3)}° &nbsp; Lon ${current.longitude.toFixed(3)}°` +\n (current.altitude != null ? `<br/>Alt ${current.altitude.toFixed(1)} km` : '') +\n (sat.status ? `<br/><span style=\"color:${statusColor}\">${sat.status.toUpperCase()}</span>` : ''),\n { permanent: false, direction: 'top', className: 'zendir-leaflet-tooltip' }\n );\n satMarker.on('click', () => onSatelliteClick?.(sat.id));\n });\n\n if (showLegend && (allSatellites.length > 0 || groundStations.length > 0)) {\n const Legend = L.Control.extend({\n onAdd() {\n const div = L.DomUtil.create('div', 'leaflet-legend-zendir');\n div.style.cssText = `\n padding: 8px 12px; background: rgba(15,21,32,0.9); border: 1px solid rgba(157,112,255,0.2);\n border-radius: 8px; color: #e4e0f0; font-size: 11px; font-family: Roboto, sans-serif;\n `;\n allSatellites.forEach((s, i) => {\n const c = s.color || DEFAULT_TRACK_COLORS[i % DEFAULT_TRACK_COLORS.length];\n const row = document.createElement('div');\n row.style.cssText = 'display: flex; align-items: center; gap: 6px; margin: 2px 0;';\n const swatch = document.createElement('span');\n swatch.style.cssText = `width:8px;height:8px;border-radius:50%;background:${c};`;\n const label = document.createElement('span');\n label.textContent = s.name;\n row.appendChild(swatch);\n row.appendChild(label);\n div.appendChild(row);\n });\n if (groundStations.length > 0) {\n const row = document.createElement('div');\n row.style.cssText = 'display: flex; align-items: center; gap: 6px; margin: 2px 0; margin-top: 6px;';\n const swatch = document.createElement('span');\n swatch.style.cssText = 'width:8px;height:8px;border-radius:50%;background:#2dccff;';\n const label = document.createElement('span');\n label.textContent = `${groundStations.length} Ground Station${groundStations.length > 1 ? 's' : ''}`;\n row.appendChild(swatch);\n row.appendChild(label);\n div.appendChild(row);\n }\n return div;\n },\n });\n const legend = new Legend({ position: 'topleft' });\n legend.addTo(map);\n controlsRef.current.push(legend);\n }\n\n }, [\n ready,\n allSatellites,\n groundStations,\n internalTerminator,\n terminatorTime,\n nightTileUrl,\n lightSources,\n internalGrid,\n showEquator,\n showLegend,\n tokens.colors.text.secondary,\n addLayer,\n clearLayers,\n onSatelliteClick,\n onStationClick,\n ]);\n\n // ==========================================================================\n // Pins layer — separate from overlayGroup so pin updates don't rebuild tracks\n // ==========================================================================\n\n useEffect(() => {\n if (!ready || !mapRef.current) return;\n const group = pinsGroupRef.current;\n if (!group) return;\n group.clearLayers();\n\n if (!pins || pins.length === 0) return;\n const accentColor = tokens.colors.accent.primary;\n\n pins.forEach((pin) => {\n const pinColor = pin.color || accentColor;\n\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"36\" viewBox=\"0 0 24 36\">\n <defs>\n <filter id=\"pin-shadow-${pin.id}\" x=\"-30%\" y=\"-10%\" width=\"160%\" height=\"140%\">\n <feDropShadow dx=\"0\" dy=\"2\" stdDeviation=\"2\" flood-color=\"#000\" flood-opacity=\"0.35\"/>\n </filter>\n </defs>\n <g filter=\"url(#pin-shadow-${pin.id})\">\n <path d=\"M12 2 C6.5 2 2 6.5 2 12 C2 19 12 34 12 34 C12 34 22 19 22 12 C22 6.5 17.5 2 12 2Z\"\n fill=\"${pinColor}\" stroke=\"rgba(255,255,255,0.5)\" stroke-width=\"1\"/>\n <circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"#0d1323\" stroke=\"rgba(255,255,255,0.7)\" stroke-width=\"0.8\"/>\n </g>\n </svg>`;\n\n const icon = L.divIcon({\n html: svg,\n className: 'zendir-pin-icon',\n iconSize: [24, 36] as unknown as L.PointExpression,\n iconAnchor: [12, 36] as unknown as L.PointExpression,\n tooltipAnchor: [0, -36] as unknown as L.PointExpression,\n });\n\n const marker = L.marker([pin.latitude, pin.longitude], {\n icon,\n draggable: pinsEditable,\n title: pin.label ?? '',\n });\n\n // Rich tooltip\n const tooltipLines = [\n pin.label ? `<strong>${pin.label}</strong>` : '',\n pin.description ? `<div style=\"opacity:0.7;font-size:11px\">${pin.description}</div>` : '',\n `<div style=\"font-size:10px;opacity:0.5;font-family:monospace\">${pin.latitude.toFixed(4)}°, ${pin.longitude.toFixed(4)}°</div>`,\n pin.createdBy ? `<div style=\"font-size:10px;opacity:0.4;margin-top:2px\">by ${pin.createdBy}</div>` : '',\n ].filter(Boolean).join('');\n marker.bindTooltip(tooltipLines, {\n className: 'zendir-pin-tooltip',\n direction: 'top',\n offset: [0, -4],\n });\n\n if (pinsEditable) {\n // Drag to reposition\n marker.on('dragend', () => {\n const latlng = marker.getLatLng();\n onPinUpdate?.({ ...pin, latitude: latlng.lat, longitude: latlng.lng });\n });\n\n // Right-click to delete\n marker.on('contextmenu', (e: L.LeafletEvent) => {\n L.DomEvent.stopPropagation(e as unknown as Event);\n onPinRemove?.(pin.id);\n });\n }\n\n group.addLayer(marker);\n });\n }, [ready, pins, pinsEditable, onPinUpdate, onPinRemove, tokens.colors.accent.primary]);\n\n // Click-to-add: listen for clicks on the map background when pinsEditable is true\n useEffect(() => {\n if (!ready || !mapRef.current || !pinsEditable || !onPinAdd) return;\n const map = mapRef.current;\n\n const handleClick = (e: L.LeafletMouseEvent) => {\n // Only fire on map background clicks — ignore clicks that bubbled from markers\n if ((e.originalEvent?.target as HTMLElement)?.closest?.('.leaflet-marker-icon')) return;\n onPinAdd({\n latitude: e.latlng.lat,\n longitude: e.latlng.lng,\n label: '',\n color: tokens.colors.accent.primary,\n });\n };\n\n map.on('click', handleClick);\n return () => { map.off('click', handleClick); };\n }, [ready, pinsEditable, onPinAdd, tokens.colors.accent.primary]);\n\n const handleRecenter = useCallback(() => {\n const map = mapRef.current;\n if (!map) return;\n const points: L.LatLngExpression[] = [];\n allSatellites.forEach((s) => {\n s.groundTrack.forEach((p) => points.push([p.latitude, p.longitude]));\n });\n groundStations.forEach((gs) => points.push([gs.latitude, gs.longitude]));\n if (points.length > 0) {\n const bounds = L.latLngBounds(points);\n map.fitBounds(bounds, { padding: [40, 40], maxZoom: 8 });\n } else {\n map.setView(defaultCenter, defaultZoom);\n }\n }, [allSatellites, groundStations, defaultCenter, defaultZoom]);\n\n const handleZoomIn = useCallback(() => { mapRef.current?.zoomIn(); }, []);\n const handleZoomOut = useCallback(() => { mapRef.current?.zoomOut(); }, []);\n\n const resolvedMinHeight = minHeight || (typeof height === 'number' ? `${height}px` : '400px');\n\n const isEmpty = allSatellites.length === 0 && groundStations.length === 0 && (!pins || pins.length === 0);\n\n // ── Shared control button style ──\n const ctrlBtnBase: React.CSSProperties = {\n background: 'rgba(20, 24, 38, 0.92)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n border: '1px solid rgba(120, 100, 180, 0.18)',\n color: '#c8c0d8',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'background 0.15s, border-color 0.15s',\n };\n\n const ctrlHover = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'rgba(30, 34, 52, 0.95)';\n e.currentTarget.style.borderColor = 'rgba(157, 112, 255, 0.4)';\n };\n const ctrlLeave = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'rgba(20, 24, 38, 0.92)';\n e.currentTarget.style.borderColor = 'rgba(120, 100, 180, 0.18)';\n };\n\n // Overlay items shown in the Layers panel\n const overlayItems: Array<{ id: string; label: string; enabled: boolean }> = [];\n overlayItems.push({ id: 'terminator', label: 'Day / Night', enabled: internalTerminator });\n overlayItems.push({ id: 'grid', label: 'Grid Lines', enabled: internalGrid });\n for (const l of customLayers ?? []) {\n overlayItems.push({ id: l.id, label: l.label, enabled: customLayerState[l.id] ?? (l.defaultEnabled ?? true) });\n }\n\n return (\n <div\n className={`zendir-ground-track-map zendir-ground-track-map-leaflet${pinsEditable ? ' zendir-pins-editable' : ''} ${className}`}\n data-map-backend=\"leaflet\"\n style={{\n width,\n height,\n minHeight: resolvedMinHeight,\n backgroundColor: tokens.colors.background.base,\n borderRadius: 8,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n <div\n ref={containerRef}\n style={{ width: '100%', height: '100%', minHeight: resolvedMinHeight }}\n />\n {isEmpty && (\n <div\n style={{\n position: 'absolute',\n left: '50%',\n top: '50%',\n transform: 'translate(-50%, -50%)',\n color: tokens.colors.text.secondary,\n fontSize: 14,\n pointerEvents: 'none',\n }}\n >\n {emptyMessage}\n </div>\n )}\n {/* ── Unified Map Controls — top-right control bar ── */}\n <div\n style={{\n position: 'absolute',\n top: 10,\n right: 10,\n zIndex: 1000,\n display: 'flex',\n flexDirection: 'row',\n gap: 4,\n alignItems: 'flex-start',\n pointerEvents: 'none',\n }}\n >\n {/* Recenter */}\n {showRecenterButton && (\n <button\n type=\"button\"\n onClick={handleRecenter}\n title=\"Recenter map to fit all assets and ground stations\"\n aria-label=\"Recenter map\"\n style={{\n ...ctrlBtnBase,\n borderRadius: 4,\n padding: '6px 8px',\n gap: 4,\n fontSize: 10,\n fontWeight: 500,\n letterSpacing: '0.03em',\n pointerEvents: 'auto',\n }}\n onMouseEnter={ctrlHover}\n onMouseLeave={ctrlLeave}\n >\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M12 2v4M12 18v4M2 12h4M18 12h4\" />\n </svg>\n </button>\n )}\n\n {/* Layers panel */}\n <div ref={layersPanelRef} style={{ position: 'relative', pointerEvents: 'auto' }}>\n <button\n type=\"button\"\n onClick={() => setLayersPanelOpen((o) => !o)}\n title=\"Map layers\"\n aria-label=\"Toggle map layers panel\"\n aria-expanded={layersPanelOpen}\n style={{\n ...ctrlBtnBase,\n borderRadius: 4,\n padding: '6px 8px',\n pointerEvents: 'auto',\n borderColor: layersPanelOpen ? 'rgba(157, 112, 255, 0.4)' : ctrlBtnBase.border ? undefined : 'rgba(120, 100, 180, 0.18)',\n background: layersPanelOpen ? 'rgba(30, 34, 52, 0.95)' : ctrlBtnBase.background,\n }}\n onMouseEnter={ctrlHover}\n onMouseLeave={ctrlLeave}\n >\n {/* Layers icon — stacked diamonds */}\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinejoin=\"round\">\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" />\n <path d=\"M2 17l10 5 10-5\" />\n <path d=\"M2 12l10 5 10-5\" />\n </svg>\n </button>\n\n {/* Dropdown panel */}\n {layersPanelOpen && (\n <div\n style={{\n position: 'absolute',\n top: 'calc(100% + 6px)',\n right: 0,\n minWidth: 180,\n background: 'rgba(16, 18, 30, 0.96)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n border: '1px solid rgba(120, 100, 180, 0.18)',\n borderRadius: 6,\n padding: '8px 0',\n boxShadow: '0 8px 32px rgba(0,0,0,0.5)',\n }}\n >\n {/* Base Map section (only when no explicit tileUrl) */}\n {!isExplicitTileUrl && (\n <>\n <div style={{\n padding: '4px 12px 6px',\n fontSize: 9,\n fontWeight: 600,\n letterSpacing: '0.08em',\n textTransform: 'uppercase' as const,\n color: '#7a748e',\n }}>\n Base Map\n </div>\n <div style={{ display: 'flex', gap: 2, padding: '0 8px 8px' }}>\n {(['dark', 'satellite'] as const).map((style) => (\n <button\n key={style}\n type=\"button\"\n onClick={() => handleTileStyleChange(style)}\n aria-pressed={tileStyle === style}\n style={{\n flex: 1,\n background: tileStyle === style ? 'rgba(157, 112, 255, 0.18)' : 'rgba(255,255,255,0.03)',\n border: `1px solid ${tileStyle === style ? 'rgba(157, 112, 255, 0.35)' : 'rgba(120, 100, 180, 0.1)'}`,\n borderRadius: 4,\n color: tileStyle === style ? '#d0c4ee' : '#7a748e',\n cursor: 'pointer',\n padding: '5px 6px',\n fontSize: 10,\n fontWeight: tileStyle === style ? 600 : 400,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 4,\n transition: 'all 0.15s ease',\n }}\n >\n {style === 'dark' ? (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\">\n <path d=\"M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z\" />\n </svg>\n ) : (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z\" />\n </svg>\n )}\n {style === 'dark' ? 'Dark' : 'Satellite'}\n </button>\n ))}\n </div>\n <div style={{ height: 1, background: 'rgba(120, 100, 180, 0.1)', margin: '0 8px' }} />\n </>\n )}\n\n {/* Overlays section */}\n <div style={{\n padding: `${isExplicitTileUrl ? '4px' : '8px'} 12px 4px`,\n fontSize: 9,\n fontWeight: 600,\n letterSpacing: '0.08em',\n textTransform: 'uppercase' as const,\n color: '#7a748e',\n }}>\n Overlays\n </div>\n {overlayItems.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => handleLayerToggle(item.id, !item.enabled)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n width: '100%',\n padding: '5px 12px',\n background: 'transparent',\n border: 'none',\n color: item.enabled ? '#c8c0d8' : '#5a5470',\n cursor: 'pointer',\n fontSize: 11,\n textAlign: 'left' as const,\n transition: 'background 0.1s',\n }}\n onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(157, 112, 255, 0.08)'; }}\n onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}\n >\n {/* Toggle indicator */}\n <span style={{\n width: 14,\n height: 14,\n borderRadius: 3,\n border: `1.5px solid ${item.enabled ? 'rgba(157, 112, 255, 0.6)' : 'rgba(120, 100, 180, 0.25)'}`,\n background: item.enabled ? 'rgba(157, 112, 255, 0.22)' : 'transparent',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n transition: 'all 0.15s ease',\n }}>\n {item.enabled && (\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"#d0c4ee\" strokeWidth=\"2\">\n <path d=\"M2 6l3 3 5-5\" />\n </svg>\n )}\n </span>\n {item.label}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Zoom controls */}\n <div style={{ display: 'flex', flexDirection: 'column', pointerEvents: 'auto' }}>\n <button\n type=\"button\"\n onClick={handleZoomIn}\n title=\"Zoom in\"\n aria-label=\"Zoom in\"\n style={{\n ...ctrlBtnBase,\n borderRadius: '4px 4px 0 0',\n borderBottom: 'none',\n width: 30,\n height: 28,\n fontSize: 16,\n fontWeight: 300,\n pointerEvents: 'auto',\n }}\n onMouseEnter={ctrlHover}\n onMouseLeave={ctrlLeave}\n >+</button>\n <button\n type=\"button\"\n onClick={handleZoomOut}\n title=\"Zoom out\"\n aria-label=\"Zoom out\"\n style={{\n ...ctrlBtnBase,\n borderRadius: '0 0 4px 4px',\n width: 30,\n height: 28,\n fontSize: 16,\n fontWeight: 300,\n pointerEvents: 'auto',\n }}\n onMouseEnter={ctrlHover}\n onMouseLeave={ctrlLeave}\n >−</button>\n </div>\n </div>\n </div>\n );\n}\n\nexport default GroundTrackMapLeaflet;\n"],"names":[],"mappings":";;;;;;;;AAiCA,MAAM,mBAA2C;AAAA,EAC/C,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,KAAK;AACP;AAEA,MAAM,uBAAuB;AAAA,EAC3B;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAC/E;AAOA,SAAS,iBAAiB,aAAqB,YAA+B;AAE5E,QAAM,QAAQ,gBAAgB,YAC1B,qCAAqC,WAAW,QAChD,gBAAgB,YAChB,qDAAqD,WAAW,2BAChE,gBAAgB,YAChB,oDAAoD,WAAW,QAC/D,gBAAgB,YAChB,qDAAqD,WAAW,QAChE,gBAAgB,YAChB,yCAAyC,WAAW,QACpD,qCAAqC,WAAW;AAEpD,QAAM,MAAM;AAAA;AAAA,2CAE6B,WAAW;AAAA;AAAA,4DAEM,UAAU;AAAA;AAAA,6DAET,UAAU;AAAA,sDACjB,UAAU;AAAA;AAAA,oDAEZ,UAAU;AAAA;AAAA,8DAEA,WAAW;AAAA;AAAA,oDAErB,UAAU;AAAA;AAAA,8DAEA,UAAU;AAAA,wDAChB,UAAU;AAAA;AAAA;AAAA,MAG5D,KAAK;AAAA;AAGT,SAAO,EAAE,QAAQ;AAAA,IACf,MAAM;AAAA,IACN,WAAW;AAAA,IACX,UAAU,CAAC,IAAI,EAAE;AAAA,IACjB,YAAY,CAAC,IAAI,EAAE;AAAA,IACnB,eAAe,CAAC,GAAG,GAAG;AAAA,EAAA,CACvB;AACH;AAGA,SAAS,qBAAqB,aAAqB,MAAc,QAA2B;AAE1F,QAAM,QAAQ,WAAW,WACrB,uCAAuC,WAAW,QAClD,WAAW,YACX,mDAAmD,WAAW,2BAC9D,WAAW,YACX,gDAAgD,WAAW,QAC3D,WAAW,YACX,mDAAmD,WAAW,QAC9D,WAAW,aACX,iDAAiD,WAAW,QAC5D,uCAAuC,WAAW;AAEtD,MAAI,YAAY;AAChB,MAAI,SAAS,gBAAgB;AAE3B,gBAAY;AAAA,6EAC6D,WAAW;AAAA,qDACnC,WAAW;AAAA,qDACX,WAAW;AAAA,qDACX,WAAW;AAAA,qDACX,WAAW;AAAA,sDACV,WAAW;AAAA,sDACX,WAAW;AAAA,EAC/D,WAAW,SAAS,SAAS;AAE3B,gBAAY;AAAA,8DAC8C,WAAW;AAAA,qEACJ,WAAW;AAAA,sDAC1B,WAAW;AAAA,sDACX,WAAW;AAAA,6DACJ,WAAW;AAAA,iEACP,WAAW;AAAA,EAC1E,OAAO;AAEL,gBAAY;AAAA,sEACsD,WAAW;AAAA,sEACX,WAAW;AAAA,sEACX,WAAW;AAAA,8CACnC,WAAW;AAAA,sDACH,WAAW;AAAA,sDACX,WAAW;AAAA,EAC/D;AAEA,QAAM,MAAM;AAAA;AAAA,2CAE6B,WAAW;AAAA;AAAA,4DAEM,WAAW;AAAA,MACjE,SAAS;AAAA;AAAA;AAAA,MAGT,KAAK;AAAA;AAGT,SAAO,EAAE,QAAQ;AAAA,IACf,MAAM;AAAA,IACN,WAAW;AAAA,IACX,UAAU,CAAC,IAAI,EAAE;AAAA,IACjB,YAAY,CAAC,IAAI,EAAE;AAAA,IACnB,eAAe,CAAC,GAAG,GAAG;AAAA,EAAA,CACvB;AACH;AAeA,SAAS,8BACP,MACA,gBAAwB,GACxB,YAAoB,KACmD;AACvE,QAAM,YAAY,KAAK,OAAO,KAAK,QAAA,IAAY,IAAI,KAAK,KAAK,YAAA,GAAe,GAAG,CAAC,EAAE,QAAA,KAAa,KAAQ;AACvG,QAAM,cAAc,SAAS,KAAK,IAAK,IAAI,KAAK,KAAK,OAAQ,YAAY,GAAG;AAC5E,QAAM,SAAU,cAAc,KAAK,KAAM;AACzC,QAAM,aAAc,KAAK,gBAAgB,KAAK,cAAA,IAAkB,MAAM,KAAM,MAAM;AAClF,QAAM,SAAU,gBAAgB,KAAK,KAAM;AAE3C,QAAM,SAAkC,CAAA;AACxC,QAAM,UAAmC,CAAA;AAEzC,WAAS,IAAI,GAAG,KAAK,WAAW,KAAK;AAEnC,UAAM,MAAM,UAAW,IAAI,YAAa;AACxC,UAAM,SAAS,OAAO,KAAK,KAAK;AAChC,UAAM,OAAO,EAAE,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,MACnD,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM;AAEnD,QAAI,OAAO,GAAI;AAAA,aAEJ,OAAO,GAAG;AAGnB,aAAO,KAAK,CAAC,KAAK,SAAS,CAAC;AAC5B,cAAQ,KAAK,CAAC,KAAK,YAAY,GAAG,CAAC;AAAA,IACrC,OAAO;AAEL,YAAM,IAAK,KAAK,KAAK,IAAI,IAAI,MAAO,KAAK;AACzC,aAAO,KAAK,CAAC,KAAK,YAAY,CAAC,CAAC;AAGhC,cAAQ,KAAK,CAAC,KAAK,YAAY,MAAM,CAAC,CAAC;AAAA,IACzC;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,QAAA;AACnB;AAOA,SAAS,kBACP,YACoB;AACpB,QAAM,EAAE,QAAQ,QAAA,IAAY;AAC5B,MAAI,CAAC,OAAO,UAAU,CAAC,QAAQ,eAAe,CAAA;AAE9C,QAAM,OAA2B,CAAA;AAGjC,OAAK,KAAK,GAAG,MAAM;AAGnB,OAAK,KAAK,GAAG,CAAC,GAAG,OAAO,EAAE,SAAS;AAQnC,SAAO;AACT;AAkDO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,gBAAgB,CAAC,IAAI,CAAC;AAAA,EACtB,cAAc;AAAA,EACd,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmD;AACjD,QAAM,EAAE,OAAA,IAAW,SAAA;AACnB,QAAM,eAAe,OAAuB,IAAI;AAChD,QAAM,SAAS,OAAqB,IAAI;AACxC,QAAM,eAAe,OAA2B,IAAI;AACpD,QAAM,kBAAkB,OAA4B,IAAI;AACxD,QAAM,cAAc,OAAoB,EAAE;AAE1C,QAAM,eAAe,OAA4B,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,KAAK;AAGxC,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,KAAK;AAC5D,QAAM,iBAAiB,OAAuB,IAAI;AAGlD,YAAU,MAAM;AACd,QAAI,CAAC,gBAAiB;AACtB,UAAM,qBAAqB,CAAC,MAAkB;AAC5C,UAAI,eAAe,WAAW,CAAC,eAAe,QAAQ,SAAS,EAAE,MAAc,GAAG;AAChF,2BAAmB,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,aAAS,iBAAiB,aAAa,kBAAkB;AACzD,WAAO,MAAM,SAAS,oBAAoB,aAAa,kBAAkB;AAAA,EAC3E,GAAG,CAAC,eAAe,CAAC;AAGpB,QAAM,mBAAmB;AACzB,QAAM,oBAAoB,YAAY;AACtC,QAAM,CAAC,WAAW,YAAY,IAAI,SAA+B,MAAM;AACrE,QAAI,kBAAmB,QAAO;AAC9B,QAAI;AACF,YAAM,QAAQ,aAAa,QAAQ,gBAAgB;AACnD,UAAI,UAAU,UAAU,UAAU,YAAa,QAAO;AAAA,IACxD,QAAQ;AAAA,IAA8B;AACtC,WAAO;AAAA,EACT,CAAC;AAED,QAAM,wBAAwB,YAAY,CAAC,UAAgC;AACzE,iBAAa,KAAK;AAClB,QAAI;AAAE,mBAAa,QAAQ,kBAAkB,KAAK;AAAA,IAAG,QAAQ;AAAA,IAAa;AAAA,EAC5E,GAAG,CAAA,CAAE;AAEL,QAAM,mBAAmB,oBAAoB,UAAU,aAAa,SAAS;AAG7E,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,cAAc;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,QAAQ;AAGzD,YAAU,MAAM;AAAE,0BAAsB,cAAc;AAAA,EAAG,GAAG,CAAC,cAAc,CAAC;AAC5E,YAAU,MAAM;AAAE,oBAAgB,QAAQ;AAAA,EAAG,GAAG,CAAC,QAAQ,CAAC;AAG1D,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAkC,MAAM;AACtF,UAAM,QAAiC,CAAA;AACvC,eAAW,KAAK,gBAAgB,IAAI;AAClC,YAAM,EAAE,EAAE,IAAI,EAAE,kBAAkB;AAAA,IACpC;AACA,WAAO;AAAA,EACT,CAAC;AAGD,YAAU,MAAM;AACd,wBAAoB,CAAC,SAAS;AAC5B,YAAM,OAAO,EAAE,GAAG,KAAA;AAClB,iBAAW,KAAK,gBAAgB,IAAI;AAClC,YAAI,EAAE,EAAE,MAAM,YAAY,EAAE,EAAE,IAAI,EAAE,kBAAkB;AAAA,MACxD;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,oBAAoB,YAAY,CAAC,SAAiB,YAAqB;AAC3E,QAAI,YAAY,cAAc;AAC5B,4BAAsB,OAAO;AAAA,IAC/B,WAAW,YAAY,QAAQ;AAC7B,sBAAgB,OAAO;AAAA,IACzB,OAAO;AACL,0BAAoB,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,OAAO,GAAG,QAAA,EAAU;AAAA,IACjE;AACA,mDAAgB,SAAS;AAAA,EAC3B,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,cAAc,YAAY,MAAM;;AACpC,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,IAAK;AACV,0BAAgB,YAAhB,mBAAyB;AACzB,gBAAY,QAAQ,QAAQ,CAAC,SAAS,IAAI,cAAc,IAAI,CAAC;AAC7D,gBAAY,UAAU,CAAA;AAAA,EACxB,GAAG,CAAA,CAAE;AAEL,QAAM,WAAW,YAAY,CAAC,UAAmB;;AAC/C,0BAAgB,YAAhB,mBAAyB,SAAS;AAAA,EACpC,GAAG,CAAA,CAAE;AAIL,YAAU,MAAM;AAEd,UAAM,KAAK,aAAa;AACxB,QAAI,CAAC,GAAI;AAET,UAAM,SAA6B,iBAAiB,CAAC,IAAI,CAAC;AAC1D,UAAM,OAAO,eAAe;AAE5B,UAAM,MAAM,EAAE,IAAI,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,WAAW;AAAA,MACX,SAAS;AAAA,MACT,UAAU;AAAA,MACV,UAAU;AAAA,MACV,oBAAoB;AAAA,MACpB,WAAW,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC;AAAA,MAClC,oBAAoB;AAAA,IAAA,CACrB;AAED,QAAI,mBAAmB,UAAU,EAAE;AAEnC,UAAM,eAAe,EAAE,WAAA;AACvB,iBAAa,MAAM,GAAG;AACtB,oBAAgB,UAAU;AAE1B,UAAM,YAAY,EAAE,WAAA;AACpB,cAAU,MAAM,GAAG;AACnB,iBAAa,UAAU;AAEvB,WAAO,UAAU;AACjB,aAAS,IAAI;AAEb,UAAM,SAAS,MAAM;AACnB,UAAI,eAAe,EAAE,SAAS,MAAA,CAAO;AAAA,IACvC;AAEA,UAAM,MAAM,sBAAsB,MAAM;AACxC,UAAM,KAAK,WAAW,QAAQ,GAAG;AACjC,UAAM,KAAK,WAAW,QAAQ,GAAG;AAEjC,WAAO,MAAM;;AACX,mBAAa,EAAE;AACf,mBAAa,EAAE;AACf,2BAAqB,GAAG;AACxB,kBAAA;AACA,yBAAa,YAAb,mBAAsB;AACtB,mBAAa,UAAU;AACvB,mBAAa,UAAU;AACvB,UAAI,OAAA;AACJ,aAAO,UAAU;AACjB,sBAAgB,UAAU;AAAA,IAC5B;AAAA,EAGF,GAAG,CAAC,WAAW,CAAC;AAGhB,YAAU,MAAM;AACd,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,OAAO,CAAC,MAAO;AAGpB,QAAI,aAAa,SAAS;AACxB,mBAAa,QAAQ,OAAA;AACrB,mBAAa,UAAU;AAAA,IACzB;AAEA,UAAM,eAAe,iBAAiB,SAAS,UAAU;AACzD,UAAM,qBAAqB,sBAAsB,gBAAgB;AAEjE,UAAM,OAAO,EAAE,UAAU,kBAAkB;AAAA,MACzC,SAAS;AAAA,MACT,YAAY,eAAe,SAAS;AAAA,MACpC,aAAa;AAAA,MACb,aAAa,sBAAsB;AAAA,IAAA,CACpC;AACD,SAAK,MAAM,GAAG;AACd,SAAK,YAAA;AACL,iBAAa,UAAU;AAGvB,QAAI,wBAAwB;AAC5B,UAAM,cAAc,MAAM;AACxB,UAAI,sBAAuB;AAC3B,8BAAwB;AACxB,WAAK,IAAI,aAAa,WAAW;AACjC,WAAK,OAAA;AACL,YAAM,WAAW,EAAE,UAAU,eAAe;AAAA,QAC1C,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,aAAa;AAAA,QACb,aAAa;AAAA,MAAA,CACd;AACD,eAAS,MAAM,GAAG;AAClB,eAAS,YAAA;AACT,mBAAa,UAAU;AAAA,IACzB;AACA,SAAK,GAAG,aAAa,WAAW;AAAA,EAClC,GAAG,CAAC,kBAAkB,KAAK,CAAC;AAE5B,YAAU,MAAM;AACd,QAAI,CAAC,SAAS,CAAC,OAAO,QAAS;AAC/B,UAAM,MAAM,OAAO;AACnB,gBAAA;AAGA,QAAI,gBAAgB,oBAAoB;AACtC,YAAM,YAAY,EAAE,UAAU,cAAc;AAAA,QAC1C,SAAS;AAAA,QACT,aAAa;AAAA,QACb,SAAS;AAAA,QACT,WAAW;AAAA,MAAA,CACZ;AACD,eAAS,SAAS;AAAA,IACpB;AAEA,QAAI,oBAAoB;AACtB,YAAM,MAAM,kBAAkB,oBAAI,KAAA;AAKlC,YAAM,YAAY;AAClB,YAAM,UAAU;AAChB,YAAM,gBAAgB;AACtB,YAAM,YAA4D,CAAA;AAClE,eAAS,IAAI,GAAG,KAAK,SAAS,KAAK,WAAW;AAE5C,cAAM,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC;AAC5B,cAAM,UAAU,IAAI,KAAK,gBAAgB,SAAS,IAAI,MAAM,IAAI,MAAM,KAAK,gBAAgB,QAAQ;AACnG,kBAAU,KAAK,EAAE,YAAY,GAAG,SAAS,KAAK,IAAI,SAAS,aAAa,GAAG;AAAA,MAC7E;AAEA,gBAAU,KAAK,EAAE,YAAY,IAAI,SAAS,eAAe;AAEzD,UAAI,cAAc;AAKlB,iBAAW,KAAK,WAAW;AACzB,cAAM,aAAa,8BAA8B,KAAK,KAAK,IAAI,EAAE,YAAY,EAAE,CAAC;AAChF,cAAM,cAAc,EAAE,UAAU;AAChC,YAAI,cAAc,QAAS,WAAW,OAAO,WAAW,GAAG;AACzD,wBAAc,EAAE;AAChB;AAAA,QACF;AAEA,cAAM,OAAO,kBAAkB,UAAU;AACzC,YAAI,KAAK,SAAS,EAAG;AAErB,SAAC,GAAG,KAAK,IAAI,EAAE,QAAQ,CAAC,WAAW;AACjC,gBAAM,UAAU,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAqB;AAChF,mBAAS,EAAE,QAAQ,SAAS;AAAA,YAC1B,OAAO;AAAA,YACP,WAAW;AAAA,YACX,aAAa;AAAA,YACb,aAAa;AAAA,UAAA,CACd,CAAC;AAAA,QACJ,CAAC;AAED,sBAAc,EAAE;AAAA,MAClB;AAIA,YAAM,iBAAiB,8BAA8B,KAAK,CAAC;AAC3D,UAAI,eAAe,OAAO,SAAS,GAAG;AACpC,cAAM,aAAa,eAAe,OAAO,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,CAAqB;AAC3F,cAAM,cAAc,eAAe,QAAQ,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,CAAqB;AAC7F,SAAC,GAAG,KAAK,IAAI,EAAE,QAAQ,CAAC,WAAW;AAEjC,mBAAS,EAAE;AAAA,YACT,WAAW,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAqB;AAAA,YACtE,EAAE,OAAO,WAAW,QAAQ,GAAG,SAAS,MAAM,aAAa,OAAO,cAAc,IAAA;AAAA,UAAI,CACrF;AACD,mBAAS,EAAE;AAAA,YACT,YAAY,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAqB;AAAA,YACvE,EAAE,OAAO,WAAW,QAAQ,GAAG,SAAS,MAAM,aAAa,OAAO,cAAc,IAAA;AAAA,UAAI,CACrF;AAED,mBAAS,EAAE;AAAA,YACT,WAAW,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAqB;AAAA,YACtE,EAAE,OAAO,WAAW,QAAQ,GAAG,SAAS,KAAK,aAAa,OAAO,cAAc,IAAA;AAAA,UAAI,CACpF;AACD,mBAAS,EAAE;AAAA,YACT,YAAY,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAqB;AAAA,YACvE,EAAE,OAAO,WAAW,QAAQ,GAAG,SAAS,KAAK,aAAa,OAAO,cAAc,IAAA;AAAA,UAAI,CACpF;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,gBAAgB,aAAa,SAAS,KAAK,oBAAoB;AACjE,YAAM,MAAM,kBAAkB,oBAAI,KAAA;AAClC,YAAM,YAAY,KAAK,OAAO,IAAI,QAAA,IAAY,IAAI,KAAK,IAAI,YAAA,GAAe,GAAG,CAAC,EAAE,QAAA,KAAa,KAAQ;AACrG,YAAM,cAAc,SAAS,KAAK,IAAK,IAAI,KAAK,KAAK,OAAQ,YAAY,GAAG;AAC5E,YAAM,SAAU,cAAc,KAAK,KAAM;AACzC,YAAM,aAAc,IAAI,gBAAgB,IAAI,cAAA,IAAkB,MAAM,KAAM,MAAM;AAEhF,iBAAW,SAAS,cAAc;AAChC,cAAM,SAAU,MAAM,WAAW,KAAK,KAAM;AAC5C,cAAM,SAAS,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IACjC,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAClC,KAAK,KAAM,MAAM,YAAY,aAAa,KAAK,KAAM,GAAG;AACxE,cAAM,cAAe,KAAK,KAAK,MAAM,IAAI,MAAO,KAAK;AAErD,YAAI;AACJ,YAAI,eAAe,EAAG,eAAc;AAAA,iBAC3B,eAAe,IAAK,eAAc;AAAA,2BACxB,KAAK,IAAI,GAAG,CAAC,cAAc,EAAE;AAEhD,YAAI,cAAc,KAAM;AAExB,cAAM,IAAI,MAAM,UAAU;AAC1B,cAAM,YAAY,MAAM,aAAa;AACrC,cAAM,QAAQ,MAAM,SAAS;AAC7B,cAAM,QAAQ,YAAY;AAG1B,cAAM,aAAa,EAAE,aAAa,CAAC,MAAM,UAAU,MAAM,SAAS,GAAG;AAAA,UACnE,QAAQ,IAAI;AAAA,UACZ,WAAW;AAAA,UACX,aAAa,QAAQ;AAAA,UACrB;AAAA,UACA,QAAQ;AAAA,UACR,aAAa,CAAC,CAAC,MAAM;AAAA,QAAA,CACtB;AACD,iBAAS,UAAU;AAGnB,cAAM,aAAa,EAAE,aAAa,CAAC,MAAM,UAAU,MAAM,SAAS,GAAG;AAAA,UACnE,QAAQ,KAAK,IAAI,GAAG,IAAI,GAAG;AAAA,UAC3B,WAAW;AAAA,UACX,aAAa,KAAK,IAAI,GAAG,QAAQ,GAAG;AAAA,UACpC,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,aAAa;AAAA,QAAA,CACd;AACD,iBAAS,UAAU;AAEnB,YAAI,MAAM,OAAO;AACf,qBAAW,YAAY,MAAM,OAAO;AAAA,YAClC,WAAW;AAAA,YACX,WAAW;AAAA,YACX,WAAW;AAAA,UAAA,CACZ;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,QAAI,cAAc;AAChB,YAAM,YAAY,EAAE,OAAO,6BAA6B,QAAQ,KAAK,aAAa,MAAA;AAClF,OAAC,MAAM,GAAG,GAAG,EAAE,QAAQ,CAAC,WAAW;AACjC,iBAAS,MAAM,MAAM,OAAO,KAAK,OAAO,IAAI;AAC1C,gBAAM,IAAI,MAAM;AAChB,mBAAS,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;AAAA,QACrD;AACA,iBAAS,MAAM,KAAK,OAAO,IAAI,OAAO,IAAI;AACxC,mBAAS,EAAE,SAAS,CAAC,CAAC,KAAK,OAAO,MAAM,GAAG,CAAC,KAAK,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;AAAA,QAC7E;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,aAAa;AACf,OAAC,MAAM,GAAG,GAAG,EAAE,QAAQ,CAAC,WAAW;AACjC,iBAAS,EAAE,SAAS,CAAC,CAAC,GAAG,OAAO,MAAM,GAAG,CAAC,GAAG,MAAM,MAAM,CAAC,GAAG;AAAA,UAC3D,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,WAAW;AAAA,UACX,aAAa;AAAA,QAAA,CACd,CAAC;AAAA,MACJ,CAAC;AAAA,IACH;AAEA,mBAAe,QAAQ,CAAC,OAAO;AAC7B,YAAM,UAAU,YAAY,KAAK,GAAG,SAAS,WAAc;AAC3D,YAAM,cAAc,iBAAiB,MAAM,KAAK,iBAAiB;AACjE,YAAM,eAAe,UAAU,KAAM,GAAyB,OAAO,WAAc;AACnF,YAAM,SAAS,oBAAoB,KAAK,GAAG,iBAAiB;AAC5D,YAAM,eAAe,kBAAkB,KAAK,GAAG,eAAe;AAC9D,UAAI,UAAU,QAAQ,SAAS,KAAK,cAAc;AAChD,cAAM,OAAO,qBAAqB,GAAG,UAAU,GAAG,WAAW,QAAQ,EAAE;AACvE,cAAM,QAAQ,wBAAwB,IAAI;AAC1C,SAAC,GAAG,KAAK,IAAI,EAAE,QAAQ,CAAC,WAAW;AACjC,gBAAM,QAAQ,CAAC,MAAM;AACnB,kBAAM,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAqB;AAC7E,qBAAS,EAAE,QAAQ,SAAS;AAAA,cAC1B,OAAO,GAAG,WAAW;AAAA,cACrB,WAAW;AAAA,cACX,aAAa;AAAA,cACb,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,aAAa;AAAA,YAAA,CACd,CAAC;AAAA,UACJ,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAEA,YAAM,SAAS,qBAAqB,aAAa,aAAa,MAAM;AACpE,YAAM,SAAS,EAAE,OAAO,CAAC,GAAG,UAAU,GAAG,SAAS,GAAG,EAAE,MAAM,OAAA,CAAQ;AACrE,eAAS,MAAM;AACf,YAAM,cAAc,WAAW,YAAY,MAAM,MAAM,KAAK;AAC5D,aAAO;AAAA,QACL,WAAW,GAAG,IAAI,YAAY,WAAW,GAAG,aAAa,MAAM,GAAG,UAAU,kCAAkC,GAAG,OAAO,YAAY,EAAE;AAAA,QACtI,EAAE,WAAW,OAAO,WAAW,OAAO,WAAW,yBAAA;AAAA,MAAyB;AAE5E,aAAO,GAAG,SAAS,MAAM;AACvB,yDAAiB,QAAQ,KAAK,OAAO,GAAG,EAAE,IAAI,GAAG;AAAA,MACnD,CAAC;AAAA,IACH,CAAC;AAED,kBAAc,QAAQ,CAAC,KAAK,WAAW;AACrC,YAAM,QAAQ,IAAI;AAClB,UAAI,CAAC,SAAS,MAAM,WAAW,EAAG;AAElC,YAAM,QAAQ,IAAI,SAAS,qBAAqB,SAAS,qBAAqB,MAAM;AACpF,YAAM,YAAY,IAAI,oBAAoB,MAAM;AAEhD,YAAM,cAAc,MACjB,MAAM,GAAG,SAAS,EAClB,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,CAAqB;AAC3D,8BAAwB,4BAA4B,WAAW,CAAC,EAAE,QAAQ,CAAC,QAAQ;AACjF,YAAI,IAAI,UAAU,GAAG;AACnB,mBAAS,EAAE,SAAS,KAAK,EAAE,OAAO,QAAQ,KAAK,SAAS,EAAA,CAAG,CAAC;AAAA,QAC9D;AAAA,MACF,CAAC;AAED,UAAI,YAAY,MAAM,QAAQ;AAC5B,cAAM,gBAAgB,MACnB,MAAM,SAAS,EACf,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,CAAqB;AAC3D,gCAAwB,4BAA4B,aAAa,CAAC,EAAE,QAAQ,CAAC,QAAQ;AACnF,cAAI,IAAI,UAAU,GAAG;AACnB,qBAAS,EAAE,SAAS,KAAK,EAAE,OAAO,QAAQ,KAAK,SAAS,KAAK,WAAW,OAAA,CAAQ,CAAC;AAAA,UACnF;AAAA,QACF,CAAC;AAAA,MACH;AAGA,UAAI,IAAI,cAAc,IAAI,WAAW,KAAK,OAAO,GAAG;AAClD,YAAI,UAA8B,CAAA;AAClC,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAI,IAAI,WAAW,CAAC,GAAG;AACrB,oBAAQ,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,MAAM,CAAC,EAAE,SAAS,CAAC;AAAA,UACtD,WAAW,QAAQ,UAAU,GAAG;AAC9B,oCAAwB,4BAA4B,OAAO,CAAC,EAAE,QAAQ,CAAC,QAAQ;AAC7E,kBAAI,IAAI,UAAU,GAAG;AACnB,yBAAS,EAAE,SAAS,KAAK,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,GAAG,SAAS,IAAA,CAAK,CAAC;AAAA,cACvF;AAAA,YACF,CAAC;AACD,sBAAU,CAAA;AAAA,UACZ,OAAO;AACL,sBAAU,CAAA;AAAA,UACZ;AAAA,QACF;AACA,YAAI,QAAQ,UAAU,GAAG;AACvB,kCAAwB,4BAA4B,OAAO,CAAC,EAAE,QAAQ,CAAC,QAAQ;AAC7E,gBAAI,IAAI,UAAU,GAAG;AACnB,uBAAS,EAAE,SAAS,KAAK,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,GAAG,SAAS,IAAA,CAAK,CAAC;AAAA,YACvF;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAEA,UAAI,IAAI,eAAe,IAAI,YAAY,SAAS,GAAG;AACjD,YAAI,YAAY,QAAQ,CAAC,OAAO;AAC9B,gBAAM,QAAQ,GAAG,SAAS;AAC1B,gBAAM,UAAU,QAAQ,YAAY;AACpC,gBAAM,WAAW,EAAE,aAAa,CAAC,GAAG,UAAU,GAAG,SAAS,GAAG;AAAA,YAC3D,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,OAAO,GAAG,OAAO;AAAA,YACjB,QAAQ;AAAA,YACR,aAAa;AAAA,UAAA,CACd;AACD,mBAAS,QAAQ;AACjB,mBAAS,YAAY,GAAG,GAAG,KAAK,aAAa,GAAG,GAAG,QAAQ,MAAM,GAAG,KAAK,KAAK,EAAE,IAAI;AAAA,YAClF,WAAW;AAAA,YACX,WAAW;AAAA,YACX,WAAW;AAAA,UAAA,CACZ;AAAA,QACH,CAAC;AAAA,MACH;AAEA,UAAI,IAAI,iBAAiB,IAAI,iBAAiB;AAC5C,cAAM,UAAU,YAAY,IAAI,KAAK,IAAI,YAAY,GAAG,MAAM,SAAS,CAAC,IAAI,MAAM,SAAS;AAC3F,cAAM,OAAO,MAAM,OAAO;AAC1B,cAAM,OAAO,qBAAqB,KAAK,UAAU,KAAK,WAAW,IAAI,iBAAiB,EAAE;AACxF,cAAM,QAAQ,wBAAwB,IAAI;AAC1C,SAAC,GAAG,KAAK,IAAI,EAAE,QAAQ,CAAC,WAAW;AACjC,gBAAM,QAAQ,CAAC,MAAM;AACnB,kBAAM,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAqB;AAC7E,qBAAS,EAAE,QAAQ,SAAS;AAAA,cAC1B,OAAO,GAAG,KAAK;AAAA,cACf,WAAW;AAAA,cACX,aAAa;AAAA,cACb,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,aAAa;AAAA,YAAA,CACd,CAAC;AAAA,UACJ,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAEA,YAAM,aAAa,YAAY,IAAI,KAAK,IAAI,YAAY,GAAG,MAAM,SAAS,CAAC,IAAI,MAAM,SAAS;AAC9F,YAAM,UAAU,MAAM,UAAU;AAChC,YAAM,cAAc,iBAAiB,IAAI,UAAU,QAAQ,KAAK,iBAAiB;AAEjF,YAAM,UAAU,iBAAiB,aAAa,KAAK;AACnD,YAAM,YAAY,EAAE,OAAO,CAAC,QAAQ,UAAU,QAAQ,SAAS,GAAG,EAAE,MAAM,QAAA,CAAS;AACnF,eAAS,SAAS;AAClB,gBAAU;AAAA,QACR,wBAAwB,KAAK,KAAK,IAAI,IAAI,qBACnC,QAAQ,SAAS,QAAQ,CAAC,CAAC,gBAAgB,QAAQ,UAAU,QAAQ,CAAC,CAAC,OAC7E,QAAQ,YAAY,OAAO,YAAY,QAAQ,SAAS,QAAQ,CAAC,CAAC,QAAQ,OAC1E,IAAI,SAAS,2BAA2B,WAAW,KAAK,IAAI,OAAO,YAAA,CAAa,YAAY;AAAA,QAC7F,EAAE,WAAW,OAAO,WAAW,OAAO,WAAW,yBAAA;AAAA,MAAyB;AAE5E,gBAAU,GAAG,SAAS,MAAM,qDAAmB,IAAI,GAAG;AAAA,IACxD,CAAC;AAED,QAAI,eAAe,cAAc,SAAS,KAAK,eAAe,SAAS,IAAI;AACzE,YAAM,SAAS,EAAE,QAAQ,OAAO;AAAA,QAC9B,QAAQ;AACN,gBAAM,MAAM,EAAE,QAAQ,OAAO,OAAO,uBAAuB;AAC3D,cAAI,MAAM,UAAU;AAAA;AAAA;AAAA;AAIpB,wBAAc,QAAQ,CAAC,GAAG,MAAM;AAC9B,kBAAM,IAAI,EAAE,SAAS,qBAAqB,IAAI,qBAAqB,MAAM;AACzE,kBAAM,MAAM,SAAS,cAAc,KAAK;AACxC,gBAAI,MAAM,UAAU;AACpB,kBAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,mBAAO,MAAM,UAAU,qDAAqD,CAAC;AAC7E,kBAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,kBAAM,cAAc,EAAE;AACtB,gBAAI,YAAY,MAAM;AACtB,gBAAI,YAAY,KAAK;AACrB,gBAAI,YAAY,GAAG;AAAA,UACrB,CAAC;AACD,cAAI,eAAe,SAAS,GAAG;AAC7B,kBAAM,MAAM,SAAS,cAAc,KAAK;AACxC,gBAAI,MAAM,UAAU;AACpB,kBAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,mBAAO,MAAM,UAAU;AACvB,kBAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,kBAAM,cAAc,GAAG,eAAe,MAAM,kBAAkB,eAAe,SAAS,IAAI,MAAM,EAAE;AAClG,gBAAI,YAAY,MAAM;AACtB,gBAAI,YAAY,KAAK;AACrB,gBAAI,YAAY,GAAG;AAAA,UACrB;AACA,iBAAO;AAAA,QACT;AAAA,MAAA,CACD;AACD,YAAM,SAAS,IAAI,OAAO,EAAE,UAAU,WAAW;AACjD,aAAO,MAAM,GAAG;AAChB,kBAAY,QAAQ,KAAK,MAAM;AAAA,IACjC;AAAA,EAEF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,OAAO,KAAK;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAMD,YAAU,MAAM;AACd,QAAI,CAAC,SAAS,CAAC,OAAO,QAAS;AAC/B,UAAM,QAAQ,aAAa;AAC3B,QAAI,CAAC,MAAO;AACZ,UAAM,YAAA;AAEN,QAAI,CAAC,QAAQ,KAAK,WAAW,EAAG;AAChC,UAAM,cAAc,OAAO,OAAO,OAAO;AAEzC,SAAK,QAAQ,CAAC,QAAQ;AACpB,YAAM,WAAW,IAAI,SAAS;AAE9B,YAAM,MAAM;AAAA;AAAA,mCAEiB,IAAI,EAAE;AAAA;AAAA;AAAA;AAAA,qCAIJ,IAAI,EAAE;AAAA;AAAA,wBAEnB,QAAQ;AAAA;AAAA;AAAA;AAK1B,YAAM,OAAO,EAAE,QAAQ;AAAA,QACrB,MAAM;AAAA,QACN,WAAW;AAAA,QACX,UAAU,CAAC,IAAI,EAAE;AAAA,QACjB,YAAY,CAAC,IAAI,EAAE;AAAA,QACnB,eAAe,CAAC,GAAG,GAAG;AAAA,MAAA,CACvB;AAED,YAAM,SAAS,EAAE,OAAO,CAAC,IAAI,UAAU,IAAI,SAAS,GAAG;AAAA,QACrD;AAAA,QACA,WAAW;AAAA,QACX,OAAO,IAAI,SAAS;AAAA,MAAA,CACrB;AAGD,YAAM,eAAe;AAAA,QACnB,IAAI,QAAQ,WAAW,IAAI,KAAK,cAAc;AAAA,QAC9C,IAAI,cAAc,2CAA2C,IAAI,WAAW,WAAW;AAAA,QACvF,iEAAiE,IAAI,SAAS,QAAQ,CAAC,CAAC,MAAM,IAAI,UAAU,QAAQ,CAAC,CAAC;AAAA,QACtH,IAAI,YAAY,6DAA6D,IAAI,SAAS,WAAW;AAAA,MAAA,EACrG,OAAO,OAAO,EAAE,KAAK,EAAE;AACzB,aAAO,YAAY,cAAc;AAAA,QAC/B,WAAW;AAAA,QACX,WAAW;AAAA,QACX,QAAQ,CAAC,GAAG,EAAE;AAAA,MAAA,CACf;AAED,UAAI,cAAc;AAEhB,eAAO,GAAG,WAAW,MAAM;AACzB,gBAAM,SAAS,OAAO,UAAA;AACtB,qDAAc,EAAE,GAAG,KAAK,UAAU,OAAO,KAAK,WAAW,OAAO;QAClE,CAAC;AAGD,eAAO,GAAG,eAAe,CAAC,MAAsB;AAC9C,YAAE,SAAS,gBAAgB,CAAqB;AAChD,qDAAc,IAAI;AAAA,QACpB,CAAC;AAAA,MACH;AAEA,YAAM,SAAS,MAAM;AAAA,IACvB,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,MAAM,cAAc,aAAa,aAAa,OAAO,OAAO,OAAO,OAAO,CAAC;AAGtF,YAAU,MAAM;AACd,QAAI,CAAC,SAAS,CAAC,OAAO,WAAW,CAAC,gBAAgB,CAAC,SAAU;AAC7D,UAAM,MAAM,OAAO;AAEnB,UAAM,cAAc,CAAC,MAA2B;;AAE9C,WAAK,mBAAE,kBAAF,mBAAiB,WAAjB,mBAAyC,YAAzC,4BAAmD,wBAAyB;AACjF,eAAS;AAAA,QACP,UAAU,EAAE,OAAO;AAAA,QACnB,WAAW,EAAE,OAAO;AAAA,QACpB,OAAO;AAAA,QACP,OAAO,OAAO,OAAO,OAAO;AAAA,MAAA,CAC7B;AAAA,IACH;AAEA,QAAI,GAAG,SAAS,WAAW;AAC3B,WAAO,MAAM;AAAE,UAAI,IAAI,SAAS,WAAW;AAAA,IAAG;AAAA,EAChD,GAAG,CAAC,OAAO,cAAc,UAAU,OAAO,OAAO,OAAO,OAAO,CAAC;AAEhE,QAAM,iBAAiB,YAAY,MAAM;AACvC,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,IAAK;AACV,UAAM,SAA+B,CAAA;AACrC,kBAAc,QAAQ,CAAC,MAAM;AAC3B,QAAE,YAAY,QAAQ,CAAC,MAAM,OAAO,KAAK,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAAA,IACrE,CAAC;AACD,mBAAe,QAAQ,CAAC,OAAO,OAAO,KAAK,CAAC,GAAG,UAAU,GAAG,SAAS,CAAC,CAAC;AACvE,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,SAAS,EAAE,aAAa,MAAM;AACpC,UAAI,UAAU,QAAQ,EAAE,SAAS,CAAC,IAAI,EAAE,GAAG,SAAS,GAAG;AAAA,IACzD,OAAO;AACL,UAAI,QAAQ,eAAe,WAAW;AAAA,IACxC;AAAA,EACF,GAAG,CAAC,eAAe,gBAAgB,eAAe,WAAW,CAAC;AAE9D,QAAM,eAAe,YAAY,MAAM;;AAAE,iBAAO,YAAP,mBAAgB;AAAA,EAAU,GAAG,CAAA,CAAE;AACxE,QAAM,gBAAgB,YAAY,MAAM;;AAAE,iBAAO,YAAP,mBAAgB;AAAA,EAAW,GAAG,CAAA,CAAE;AAE1E,QAAM,oBAAoB,cAAc,OAAO,WAAW,WAAW,GAAG,MAAM,OAAO;AAErF,QAAM,UAAU,cAAc,WAAW,KAAK,eAAe,WAAW,MAAM,CAAC,QAAQ,KAAK,WAAW;AAGvG,QAAM,cAAmC;AAAA,IACvC,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,sBAAsB;AAAA,IACtB,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,YAAY;AAAA,EAAA;AAGd,QAAM,YAAY,CAAC,MAA2C;AAC5D,MAAE,cAAc,MAAM,aAAa;AACnC,MAAE,cAAc,MAAM,cAAc;AAAA,EACtC;AACA,QAAM,YAAY,CAAC,MAA2C;AAC5D,MAAE,cAAc,MAAM,aAAa;AACnC,MAAE,cAAc,MAAM,cAAc;AAAA,EACtC;AAGA,QAAM,eAAuE,CAAA;AAC7E,eAAa,KAAK,EAAE,IAAI,cAAc,OAAO,eAAe,SAAS,oBAAoB;AACzF,eAAa,KAAK,EAAE,IAAI,QAAQ,OAAO,cAAc,SAAS,cAAc;AAC5E,aAAW,KAAK,gBAAgB,IAAI;AAClC,iBAAa,KAAK,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,OAAO,SAAS,iBAAiB,EAAE,EAAE,MAAM,EAAE,kBAAkB,OAAO;AAAA,EAC/G;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,0DAA0D,eAAe,0BAA0B,EAAE,IAAI,SAAS;AAAA,MAC7H,oBAAiB;AAAA,MACjB,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,iBAAiB,OAAO,OAAO,WAAW;AAAA,QAC1C,cAAc;AAAA,QACd,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA,MAGZ,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,WAAW,kBAAA;AAAA,UAAkB;AAAA,QAAA;AAAA,QAEtE,WACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,MAAM;AAAA,cACN,KAAK;AAAA,cACL,WAAW;AAAA,cACX,OAAO,OAAO,OAAO,KAAK;AAAA,cAC1B,UAAU;AAAA,cACV,eAAe;AAAA,YAAA;AAAA,YAGhB,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAIL;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,KAAK;AAAA,cACL,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,eAAe;AAAA,cACf,KAAK;AAAA,cACL,YAAY;AAAA,cACZ,eAAe;AAAA,YAAA;AAAA,YAIhB,UAAA;AAAA,cAAA,sBACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,OAAM;AAAA,kBACN,cAAW;AAAA,kBACX,OAAO;AAAA,oBACL,GAAG;AAAA,oBACH,cAAc;AAAA,oBACd,SAAS;AAAA,oBACT,KAAK;AAAA,oBACL,UAAU;AAAA,oBACV,YAAY;AAAA,oBACZ,eAAe;AAAA,oBACf,eAAe;AAAA,kBAAA;AAAA,kBAEjB,cAAc;AAAA,kBACd,cAAc;AAAA,kBAEd,UAAA,qBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAC5F,UAAA;AAAA,oBAAA,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,oBAC9B,oBAAC,QAAA,EAAK,GAAE,iCAAA,CAAiC;AAAA,kBAAA,EAAA,CAC3C;AAAA,gBAAA;AAAA,cAAA;AAAA,cAKJ,qBAAC,OAAA,EAAI,KAAK,gBAAgB,OAAO,EAAE,UAAU,YAAY,eAAe,OAAA,GACtE,UAAA;AAAA,gBAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAAA,oBAC3C,OAAM;AAAA,oBACN,cAAW;AAAA,oBACX,iBAAe;AAAA,oBACf,OAAO;AAAA,sBACL,GAAG;AAAA,sBACH,cAAc;AAAA,sBACd,SAAS;AAAA,sBACT,eAAe;AAAA,sBACf,aAAa,kBAAkB,6BAAkD;AAAA,sBACjF,YAAY,kBAAkB,2BAA2B,YAAY;AAAA,oBAAA;AAAA,oBAEvE,cAAc;AAAA,oBACd,cAAc;AAAA,oBAGd,UAAA,qBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,gBAAe,SACjH,UAAA;AAAA,sBAAA,oBAAC,QAAA,EAAK,GAAE,4BAAA,CAA4B;AAAA,sBACpC,oBAAC,QAAA,EAAK,GAAE,kBAAA,CAAkB;AAAA,sBAC1B,oBAAC,QAAA,EAAK,GAAE,kBAAA,CAAkB;AAAA,oBAAA,EAAA,CAC5B;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAID,mBACC;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,OAAO;AAAA,sBACL,UAAU;AAAA,sBACV,KAAK;AAAA,sBACL,OAAO;AAAA,sBACP,UAAU;AAAA,sBACV,YAAY;AAAA,sBACZ,gBAAgB;AAAA,sBAChB,sBAAsB;AAAA,sBACtB,QAAQ;AAAA,sBACR,cAAc;AAAA,sBACd,SAAS;AAAA,sBACT,WAAW;AAAA,oBAAA;AAAA,oBAIZ,UAAA;AAAA,sBAAA,CAAC,qBACA,qBAAA,UAAA,EACE,UAAA;AAAA,wBAAA,oBAAC,SAAI,OAAO;AAAA,0BACV,SAAS;AAAA,0BACT,UAAU;AAAA,0BACV,YAAY;AAAA,0BACZ,eAAe;AAAA,0BACf,eAAe;AAAA,0BACf,OAAO;AAAA,wBAAA,GACN,UAAA,YAEH;AAAA,4CACC,OAAA,EAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,SAAS,YAAA,GAC5C,UAAA,CAAC,QAAQ,WAAW,EAAY,IAAI,CAAC,UACrC;AAAA,0BAAC;AAAA,0BAAA;AAAA,4BAEC,MAAK;AAAA,4BACL,SAAS,MAAM,sBAAsB,KAAK;AAAA,4BAC1C,gBAAc,cAAc;AAAA,4BAC5B,OAAO;AAAA,8BACL,MAAM;AAAA,8BACN,YAAY,cAAc,QAAQ,8BAA8B;AAAA,8BAChE,QAAQ,aAAa,cAAc,QAAQ,8BAA8B,0BAA0B;AAAA,8BACnG,cAAc;AAAA,8BACd,OAAO,cAAc,QAAQ,YAAY;AAAA,8BACzC,QAAQ;AAAA,8BACR,SAAS;AAAA,8BACT,UAAU;AAAA,8BACV,YAAY,cAAc,QAAQ,MAAM;AAAA,8BACxC,SAAS;AAAA,8BACT,YAAY;AAAA,8BACZ,gBAAgB;AAAA,8BAChB,KAAK;AAAA,8BACL,YAAY;AAAA,4BAAA;AAAA,4BAGb,UAAA;AAAA,8BAAA,UAAU,SACT,oBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,gBAAe,QAAO,QACzE,8BAAC,QAAA,EAAK,GAAE,8CAAA,CAA8C,EAAA,CACxD,IAEA,qBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAC5F,UAAA;AAAA,gCAAA,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,gCAC/B,oBAAC,QAAA,EAAK,GAAE,+FAAA,CAA+F;AAAA,8BAAA,GACzG;AAAA,8BAED,UAAU,SAAS,SAAS;AAAA,4BAAA;AAAA,0BAAA;AAAA,0BA/BxB;AAAA,wBAAA,CAiCR,GACH;AAAA,wBACA,oBAAC,OAAA,EAAI,OAAO,EAAE,QAAQ,GAAG,YAAY,4BAA4B,QAAQ,UAAQ,CAAG;AAAA,sBAAA,GACtF;AAAA,sBAIF,oBAAC,SAAI,OAAO;AAAA,wBACV,SAAS,GAAG,oBAAoB,QAAQ,KAAK;AAAA,wBAC7C,UAAU;AAAA,wBACV,YAAY;AAAA,wBACZ,eAAe;AAAA,wBACf,eAAe;AAAA,wBACf,OAAO;AAAA,sBAAA,GACN,UAAA,YAEH;AAAA,sBACC,aAAa,IAAI,CAAC,SACjB;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BAEC,MAAK;AAAA,0BACL,SAAS,MAAM,kBAAkB,KAAK,IAAI,CAAC,KAAK,OAAO;AAAA,0BACvD,OAAO;AAAA,4BACL,SAAS;AAAA,4BACT,YAAY;AAAA,4BACZ,KAAK;AAAA,4BACL,OAAO;AAAA,4BACP,SAAS;AAAA,4BACT,YAAY;AAAA,4BACZ,QAAQ;AAAA,4BACR,OAAO,KAAK,UAAU,YAAY;AAAA,4BAClC,QAAQ;AAAA,4BACR,UAAU;AAAA,4BACV,WAAW;AAAA,4BACX,YAAY;AAAA,0BAAA;AAAA,0BAEd,cAAc,CAAC,MAAM;AAAE,8BAAE,cAAc,MAAM,aAAa;AAAA,0BAA6B;AAAA,0BACvF,cAAc,CAAC,MAAM;AAAE,8BAAE,cAAc,MAAM,aAAa;AAAA,0BAAe;AAAA,0BAGzE,UAAA;AAAA,4BAAA,oBAAC,UAAK,OAAO;AAAA,8BACX,OAAO;AAAA,8BACP,QAAQ;AAAA,8BACR,cAAc;AAAA,8BACd,QAAQ,eAAe,KAAK,UAAU,6BAA6B,2BAA2B;AAAA,8BAC9F,YAAY,KAAK,UAAU,8BAA8B;AAAA,8BACzD,SAAS;AAAA,8BACT,YAAY;AAAA,8BACZ,gBAAgB;AAAA,8BAChB,YAAY;AAAA,8BACZ,YAAY;AAAA,4BAAA,GAEX,eAAK,WACJ,oBAAC,SAAI,OAAM,KAAI,QAAO,KAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,WAAU,aAAY,KACrF,8BAAC,QAAA,EAAK,GAAE,gBAAe,EAAA,CACzB,EAAA,CAEJ;AAAA,4BACC,KAAK;AAAA,0BAAA;AAAA,wBAAA;AAAA,wBAvCD,KAAK;AAAA,sBAAA,CAyCb;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACH,GAEJ;AAAA,cAGA,qBAAC,OAAA,EAAI,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,eAAe,OAAA,GACrE,UAAA;AAAA,gBAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,OAAM;AAAA,oBACN,cAAW;AAAA,oBACX,OAAO;AAAA,sBACL,GAAG;AAAA,sBACH,cAAc;AAAA,sBACd,cAAc;AAAA,sBACd,OAAO;AAAA,sBACP,QAAQ;AAAA,sBACR,UAAU;AAAA,sBACV,YAAY;AAAA,sBACZ,eAAe;AAAA,oBAAA;AAAA,oBAEjB,cAAc;AAAA,oBACd,cAAc;AAAA,oBACf,UAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBACD;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,OAAM;AAAA,oBACN,cAAW;AAAA,oBACX,OAAO;AAAA,sBACL,GAAG;AAAA,sBACH,cAAc;AAAA,sBACd,OAAO;AAAA,sBACP,QAAQ;AAAA,sBACR,UAAU;AAAA,sBACV,YAAY;AAAA,sBACZ,eAAe;AAAA,oBAAA;AAAA,oBAEjB,cAAc;AAAA,oBACd,cAAc;AAAA,oBACf,UAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAC,EAAA,CACJ;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -16,6 +16,16 @@ export declare const CARTO_ATTRIBUTION = "&copy; <a href=\"https://carto.com/att
16
16
  /** Fallback when primary tiles fail to avoid black tiles. */
17
17
  export declare const FALLBACK_TILE = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
18
18
  export declare const OSM_ATTRIBUTION = "&copy; <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\" rel=\"noopener noreferrer\">OpenStreetMap contributors</a>";
19
+ /**
20
+ * Esri World Imagery — required when using tiles from ArcGIS Online MapServer.
21
+ * @see https://developers.arcgis.com/documentation/mapping-apis-and-services/deployment/basemap-attribution/
22
+ */
23
+ export declare const ESRI_WORLD_IMAGERY_ATTRIBUTION: string;
24
+ /**
25
+ * Resolve HTML attribution for a basemap URL template (Leaflet `TileLayer` `attribution` option).
26
+ * Unknown / custom URLs return an empty string — apps should document their own terms for those.
27
+ */
28
+ export declare function getBasemapAttribution(tileUrl: string): string;
19
29
  /**
20
30
  * After this many consecutive tile errors, switch to the fallback layer.
21
31
  * Set to 1 so we bail out immediately on the first network failure instead
@@ -9,10 +9,20 @@ const TILE_PRESETS = {
9
9
  const CARTO_ATTRIBUTION = '&copy; <a href="https://carto.com/attribution" target="_blank" rel="noopener noreferrer">CARTO</a>';
10
10
  const FALLBACK_TILE = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
11
11
  const OSM_ATTRIBUTION = '&copy; <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap contributors</a>';
12
+ const ESRI_WORLD_IMAGERY_ATTRIBUTION = 'Tiles &copy; <a href="https://www.esri.com/" target="_blank" rel="noopener noreferrer">Esri</a> &mdash; Source: Esri, Maxar, Earthstar Geographics, and the GIS User Community';
13
+ function getBasemapAttribution(tileUrl) {
14
+ const u = tileUrl.toLowerCase();
15
+ if (u.includes("cartocdn")) return CARTO_ATTRIBUTION;
16
+ if (u.includes("arcgisonline.com") || u.includes("arcgis.com")) return ESRI_WORLD_IMAGERY_ATTRIBUTION;
17
+ if (u.includes("openstreetmap.org")) return OSM_ATTRIBUTION;
18
+ return "";
19
+ }
12
20
  export {
13
21
  CARTO_ATTRIBUTION,
22
+ ESRI_WORLD_IMAGERY_ATTRIBUTION,
14
23
  FALLBACK_TILE,
15
24
  OSM_ATTRIBUTION,
16
- TILE_PRESETS
25
+ TILE_PRESETS,
26
+ getBasemapAttribution
17
27
  };
18
28
  //# sourceMappingURL=groundTrackMapLeafletTiles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"groundTrackMapLeafletTiles.js","sources":["../../../src/react/charts/groundTrackMapLeafletTiles.ts"],"sourcesContent":["/**\n * Tile layer URL presets for common map styles.\n * Import and use as `tileUrl` to switch between map themes.\n */\nexport const TILE_PRESETS = {\n /** CartoDB dark — default space ops aesthetic. */\n dark: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',\n /** Esri World Imagery — natural satellite photos, makes the terminator dramatically visible. */\n satellite: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n /** CartoDB Voyager — light/neutral with labels. */\n light: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',\n} as const;\n\n/** Primary basemap: CARTO dark with labels. */\nexport const DEFAULT_TILE =\n 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png';\n\nexport const CARTO_ATTRIBUTION =\n '&copy; <a href=\"https://carto.com/attribution\" target=\"_blank\" rel=\"noopener noreferrer\">CARTO</a>';\n\n/** Fallback when primary tiles fail to avoid black tiles. */\nexport const FALLBACK_TILE = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';\n\nexport const OSM_ATTRIBUTION =\n '&copy; <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\" rel=\"noopener noreferrer\">OpenStreetMap contributors</a>';\n\n/**\n * After this many consecutive tile errors, switch to the fallback layer.\n * Set to 1 so we bail out immediately on the first network failure instead\n * of waiting for several timeouts (which shows a black map for ~5 seconds).\n */\nexport const TILE_ERROR_THRESHOLD = 1;\n"],"names":[],"mappings":"AAIO,MAAM,eAAe;AAAA;AAAA,EAE1B,MAAM;AAAA;AAAA,EAEN,WAAW;AAAA;AAAA,EAEX,OAAO;AACT;AAMO,MAAM,oBACX;AAGK,MAAM,gBAAgB;AAEtB,MAAM,kBACX;"}
1
+ {"version":3,"file":"groundTrackMapLeafletTiles.js","sources":["../../../src/react/charts/groundTrackMapLeafletTiles.ts"],"sourcesContent":["/**\n * Tile layer URL presets for common map styles.\n * Import and use as `tileUrl` to switch between map themes.\n */\nexport const TILE_PRESETS = {\n /** CartoDB dark — default space ops aesthetic. */\n dark: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',\n /** Esri World Imagery — natural satellite photos, makes the terminator dramatically visible. */\n satellite: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n /** CartoDB Voyager — light/neutral with labels. */\n light: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',\n} as const;\n\n/** Primary basemap: CARTO dark with labels. */\nexport const DEFAULT_TILE =\n 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png';\n\nexport const CARTO_ATTRIBUTION =\n '&copy; <a href=\"https://carto.com/attribution\" target=\"_blank\" rel=\"noopener noreferrer\">CARTO</a>';\n\n/** Fallback when primary tiles fail to avoid black tiles. */\nexport const FALLBACK_TILE = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';\n\nexport const OSM_ATTRIBUTION =\n '&copy; <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\" rel=\"noopener noreferrer\">OpenStreetMap contributors</a>';\n\n/**\n * Esri World Imagery — required when using tiles from ArcGIS Online MapServer.\n * @see https://developers.arcgis.com/documentation/mapping-apis-and-services/deployment/basemap-attribution/\n */\nexport const ESRI_WORLD_IMAGERY_ATTRIBUTION =\n 'Tiles &copy; <a href=\"https://www.esri.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Esri</a> '\n + '&mdash; Source: Esri, Maxar, Earthstar Geographics, and the GIS User Community';\n\n/**\n * Resolve HTML attribution for a basemap URL template (Leaflet `TileLayer` `attribution` option).\n * Unknown / custom URLs return an empty string — apps should document their own terms for those.\n */\nexport function getBasemapAttribution(tileUrl: string): string {\n const u = tileUrl.toLowerCase();\n if (u.includes('cartocdn')) return CARTO_ATTRIBUTION;\n if (u.includes('arcgisonline.com') || u.includes('arcgis.com')) return ESRI_WORLD_IMAGERY_ATTRIBUTION;\n if (u.includes('openstreetmap.org')) return OSM_ATTRIBUTION;\n return '';\n}\n\n/**\n * After this many consecutive tile errors, switch to the fallback layer.\n * Set to 1 so we bail out immediately on the first network failure instead\n * of waiting for several timeouts (which shows a black map for ~5 seconds).\n */\nexport const TILE_ERROR_THRESHOLD = 1;\n"],"names":[],"mappings":"AAIO,MAAM,eAAe;AAAA;AAAA,EAE1B,MAAM;AAAA;AAAA,EAEN,WAAW;AAAA;AAAA,EAEX,OAAO;AACT;AAMO,MAAM,oBACX;AAGK,MAAM,gBAAgB;AAEtB,MAAM,kBACX;AAMK,MAAM,iCACX;AAOK,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,IAAI,QAAQ,YAAA;AAClB,MAAI,EAAE,SAAS,UAAU,EAAG,QAAO;AACnC,MAAI,EAAE,SAAS,kBAAkB,KAAK,EAAE,SAAS,YAAY,EAAG,QAAO;AACvE,MAAI,EAAE,SAAS,mBAAmB,EAAG,QAAO;AAC5C,SAAO;AACT;"}
@@ -7,5 +7,5 @@ export { AstroChart, LineChart, AreaChart, BarChart, ScatterChart, PieChart, Don
7
7
  export type { AstroChartHandle, AstroChartProps, ChartType, ChartTheme, DataPoint, TimeSeriesPoint, SeriesData, LineStyleOptions, AreaStyleOptions, GradientOptions, SymbolOptions, AxisOptions, ZoomOptions, TooltipOptions, LegendOptions, BrushOptions, MarkLineOptions, MarkAreaOptions, AnnotationOptions, RealTimeOptions, ExportOptions, ChartEventParams, ZoomEventParams, BrushEventParams, PowerChartData, AttitudeChartData, ThermalChartData, LinkBudgetChartData, TelemetryStreamPoint, ContactWindowData, SpectrumDataPoint, ManeuverBudgetData, EclipseTimelineData, BubbleDataPoint, RoseDiagramData, HeliocentricOrbitData, ExportFileNameContext, Data3DPoint, Scatter3DData, Bar3DData, Surface3DData, Lines3DData, LineChartProps, AreaChartProps, BarChartProps, ScatterChartProps, PieChartProps, GaugeChartProps, RadarChartProps, HeatmapChartProps, TimeSeriesChartProps, DualAxisChartProps, BubbleChartProps, HistogramChartProps, SankeyChartProps, TreemapChartProps, TreemapDataNode, SunburstChartProps, SunburstDataNode, ParallelChartProps, ParallelAxisDef, CandlestickChartProps, CandlestickDataPoint, GraphChartProps, GraphNode, GraphLink, GraphCategory, // Domain Props Types
8
8
  PowerChartProps, AttitudeChartProps, ThermalHeatmapChartProps, LinkBudgetChartProps, SubsystemGaugeProps, OrbitChartProps, ContactWindowChartProps, SpectrumChartProps, ManeuverBudgetChartProps, EclipseTimelineChartProps, RoseDiagramProps, HeliocentricOrbitProps, Scatter3DChartProps, Bar3DChartProps, Surface3DChartProps, Lines3DChartProps, SphericalRadar3DChartProps, SphericalRadar3DData, TransferOrbit3DChartProps, TransferOrbitSegment, TransferOrbitWaypoint, AttitudeHistory3DChartProps, AttitudeHistoryPoint, ConjunctionAssessment3DChartProps, ConjunctionEvent, LaunchCorridor3DChartProps, LaunchCorridorData, AntennaPattern3DChartProps, AntennaPattern3DData, WaterfallChartProps, DopplerTrackChartProps, LinkMarginChartProps, ConstellationCoverageChartProps, ConjunctionChartProps, WaterfallDataPoint, WaterfallSignalMarker, DopplerTrackPoint, SatellitePassInfo, LinkMarginPoint, CoverageGridData, PowerGeneratorConfig, AttitudeGeneratorConfig, DopplerGeneratorConfig, WaterfallGeneratorConfig, ContactGeneratorConfig, HeliocentricGeneratorConfig, SpectrumGeneratorConfig, EclipseGeneratorConfig, CoverageGeneratorConfig, ConjunctionGeneratorConfig, PowerOverviewChartProps, PowerTimelinePoint, PowerEvent, PowerState, OperationalMode, EventType, PowerEventType, StreamOptions, StreamController, WebSocketStreamOptions, WebSocketStreamController, UseChartSyncOptions, EChartsTheme, } from './unified';
9
9
  export { GroundTrackMap } from './GroundTrackMap';
10
- export type { GroundTrackMapProps, SatelliteTrack, GroundStationEnhanced, TeamPath, SROGroundStation, MapPin, LightSource } from './GroundTrackMap';
11
- export { TILE_PRESETS } from './groundTrackMapLeafletTiles';
10
+ export type { GroundTrackMapProps, SatelliteTrack, GroundStationEnhanced, TeamPath, SROGroundStation, MapPin, LightSource, MapLayerDef } from './GroundTrackMap';
11
+ export { TILE_PRESETS, CARTO_ATTRIBUTION, OSM_ATTRIBUTION, ESRI_WORLD_IMAGERY_ATTRIBUTION, getBasemapAttribution, } from './groundTrackMapLeafletTiles';
@@ -618,7 +618,7 @@ const Container = memo(function Container2({
618
618
  children: subtitle
619
619
  }
620
620
  ),
621
- (title || actions) && !useGlassMode && /* @__PURE__ */ jsx(
621
+ (title || actions) && !useGlassMode && /* @__PURE__ */ jsxs(
622
622
  "div",
623
623
  {
624
624
  style: {
@@ -626,20 +626,28 @@ const Container = memo(function Container2({
626
626
  position: "relative",
627
627
  zIndex: 1
628
628
  },
629
- children: /* @__PURE__ */ jsx(
630
- CardHeader,
631
- {
632
- icon: icon || (iconStatus ? "mission" : ""),
633
- title: typeof title === "string" ? title : String(title || ""),
634
- subtitle: typeof subtitle === "string" ? subtitle : subtitle ? String(subtitle) : void 0,
635
- status: iconStatus || status,
636
- iconColor,
637
- iconSize: headingConfig.iconSize,
638
- hideStatusIndicator: !icon && !iconStatus && !status,
639
- rightSlot: actions,
640
- style: { marginBottom: tokens.spacing.sm }
641
- }
642
- )
629
+ children: [
630
+ /* @__PURE__ */ jsx(
631
+ CardHeader,
632
+ {
633
+ icon: icon || (iconStatus ? "mission" : ""),
634
+ title: typeof title === "string" ? title : String(title || ""),
635
+ subtitle: typeof subtitle === "string" ? subtitle : void 0,
636
+ status: iconStatus || status,
637
+ iconColor,
638
+ iconSize: headingConfig.iconSize,
639
+ hideStatusIndicator: !icon && !iconStatus && !status,
640
+ rightSlot: actions,
641
+ style: { marginBottom: tokens.spacing.sm }
642
+ }
643
+ ),
644
+ subtitle && typeof subtitle !== "string" && /* @__PURE__ */ jsx("div", { style: {
645
+ fontSize: tokens.typography.fontSize.sm,
646
+ color: tokens.colors.text.secondary,
647
+ marginTop: -parseInt(tokens.spacing.xs || "4", 10) || -4,
648
+ marginBottom: parseInt(tokens.spacing.sm || "8", 10) || 8
649
+ }, children: subtitle })
650
+ ]
643
651
  }
644
652
  ),
645
653
  /* @__PURE__ */ jsx(