ethio-map-kit 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -83,15 +83,75 @@ const data: RegionRecord<RegionDatum> = {
83
83
  | `locations` | `LatLngLocation[]` | Optional marker/location data. |
84
84
  | `theme` | `"light" \| "dark"` | Map theme. |
85
85
  | `layer` | `"regions" \| "heat" \| "terrain"` | Active visual layer. |
86
+ | `defaultLayer` | `"regions" \| "heat" \| "terrain"` | Initial visual layer for uncontrolled maps. |
87
+ | `onLayerChange` | `(layer) => void` | Called when the built-in layer control requests a layer change. |
86
88
  | `viewMode` | `"map" \| "bars"` | Map view or bar view. |
87
89
  | `selectionAnimation` | `"pulse" \| "static" \| "none" \| "outline"` | Selected-region animation style. |
88
90
  | `showLabels` | `boolean` | Show or hide region labels. |
89
91
  | `showMarkers` | `boolean` | Show or hide location markers. |
92
+ | `showInMapStats` | `boolean` | Show or hide the selected region service overlay inside the map surface. |
90
93
  | `regionColors` | `PartialRegionRecord<string>` | Override region colors. |
91
94
  | `selectedRegionId` | `EthiopiaRegionId \| null` | Controlled selected region. |
92
95
  | `defaultSelectedRegionId` | `EthiopiaRegionId \| null` | Initial selected region. |
93
96
  | `onRegionSelect` | `(selection) => void` | Called when a region is selected or cleared. |
97
+ | `className` | `string` | Class applied to the map surface in the all-in-one `EthiopiaMap`. |
98
+ | `style` | `React.CSSProperties` | Inline style applied to the map surface in the all-in-one `EthiopiaMap`. Useful for height and width. |
94
99
  | `terrainImageUrl` | `string` | Custom terrain image URL. |
100
+ | `showLayerControl` | `boolean` | Show a bottom-left Normal/Terrain map toggle inside the map surface. |
101
+ | `layerControlClassName` | `string` | Extra class for the built-in layer toggle. |
102
+ | `layerControlLabels` | `{ regions?: string; terrain?: string }` | Custom labels for the built-in layer toggle. |
103
+
104
+ ## In-Map Layer Toggle
105
+
106
+ Use `showLayerControl` when you want a small Normal/Terrain switch inside the map, similar to common map viewers.
107
+
108
+ ```tsx
109
+ <EthiopiaMap
110
+ data={defaultRegionData}
111
+ defaultLayer="regions"
112
+ showLayerControl
113
+ />
114
+ ```
115
+
116
+ For a controlled map, keep your own layer state and handle `onLayerChange`:
117
+
118
+ ```tsx
119
+ const [layer, setLayer] = useState<EthiopiaMapLayer>("regions");
120
+
121
+ <EthiopiaMap
122
+ data={defaultRegionData}
123
+ layer={layer}
124
+ onLayerChange={setLayer}
125
+ showLayerControl
126
+ />;
127
+ ```
128
+
129
+ ## Sizing The Map
130
+
131
+ The map surface fills its own container and has a default minimum height. Use `className`, `style`, or your layout wrapper to control the rendered size.
132
+
133
+ ```tsx
134
+ <EthiopiaMap
135
+ data={defaultRegionData}
136
+ serviceStats={defaultRegionServiceStats}
137
+ className="w-full"
138
+ style={{ width: "100%", height: 640, minHeight: 640 }}
139
+ />
140
+ ```
141
+
142
+ With composable components, size `EthiopiaMapSurface` directly:
143
+
144
+ ```tsx
145
+ <EthiopiaMapProvider data={defaultRegionData}>
146
+ <div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_320px]">
147
+ <EthiopiaMapSurface
148
+ className="w-full"
149
+ style={{ height: 620, minHeight: 620 }}
150
+ />
151
+ <EthiopiaSelectedRegionCard />
152
+ </div>
153
+ </EthiopiaMapProvider>
154
+ ```
95
155
 
96
156
  ## Composable Components
97
157
 
@@ -104,13 +164,18 @@ import {
104
164
  EthiopiaMapLegend,
105
165
  EthiopiaSelectedRegionCard,
106
166
  EthiopiaRegionServicesCard,
167
+ EthiopiaRegionServicesPanel,
107
168
  EthiopiaLocationsPanel,
108
169
  defaultRegionData,
170
+ defaultRegionServiceStats,
109
171
  } from "ethio-map-kit";
110
172
 
111
173
  export function Dashboard() {
112
174
  return (
113
- <EthiopiaMapProvider data={defaultRegionData}>
175
+ <EthiopiaMapProvider
176
+ data={defaultRegionData}
177
+ serviceStats={defaultRegionServiceStats}
178
+ >
114
179
  <EthiopiaMapSurface />
115
180
  <EthiopiaMapLegend />
116
181
  <EthiopiaSelectedRegionCard />
@@ -121,6 +186,94 @@ export function Dashboard() {
121
186
  }
122
187
  ```
123
188
 
189
+ ## Service Stats Layouts
190
+
191
+ Service stats can appear inside the map surface, outside the map, or in a standalone details page. Use either the in-map overlay or the external card unless you intentionally want both.
192
+
193
+ In-map overlay only:
194
+
195
+ ```tsx
196
+ <EthiopiaMapProvider
197
+ data={defaultRegionData}
198
+ serviceStats={defaultRegionServiceStats}
199
+ showInMapStats
200
+ >
201
+ <EthiopiaMapSurface />
202
+ <EthiopiaSelectedRegionCard />
203
+ </EthiopiaMapProvider>
204
+ ```
205
+
206
+ External service card only:
207
+
208
+ ```tsx
209
+ <EthiopiaMapProvider
210
+ data={defaultRegionData}
211
+ serviceStats={defaultRegionServiceStats}
212
+ showInMapStats={false}
213
+ >
214
+ <div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_320px]">
215
+ <EthiopiaMapSurface className="w-full" style={{ height: 620 }} />
216
+ <EthiopiaRegionServicesCard />
217
+ </div>
218
+ </EthiopiaMapProvider>
219
+ ```
220
+
221
+ Customize the in-map overlay and its scroll list from `EthiopiaMapSurface`:
222
+
223
+ ```tsx
224
+ <EthiopiaMapSurface
225
+ inMapStatsClassName="my-map-stats"
226
+ inMapStatsStyle={{ maxHeight: 320 }}
227
+ inMapStatsListClassName="my-map-stats-list"
228
+ inMapStatsListStyle={{ maxHeight: 240 }}
229
+ />
230
+ ```
231
+
232
+ Opt in to incremental row rendering when a region has many indicators:
233
+
234
+ ```tsx
235
+ <EthiopiaRegionServicesCard
236
+ incrementalRows={{
237
+ enabled: true,
238
+ initialCount: 8,
239
+ step: 8,
240
+ thresholdPx: 240,
241
+ }}
242
+ />
243
+ ```
244
+
245
+ The same option works on the in-map overlay:
246
+
247
+ ```tsx
248
+ <EthiopiaMapSurface
249
+ incrementalRows={{
250
+ enabled: true,
251
+ initialCount: 8,
252
+ step: 8,
253
+ thresholdPx: 320,
254
+ showMoreFallback: false,
255
+ animateNewRows: false,
256
+ sentinelVisible: false,
257
+ }}
258
+ />
259
+ ```
260
+
261
+ `incrementalRows` is client-side only. Pass the full service list, and the component reveals more rows from that already-loaded data as the user scrolls. For compact in-map overlays, keep `animateNewRows` false and use a larger `thresholdPx` so rows appear before the user reaches the end of the visible list.
262
+
263
+ For server-rendered detail pages, fetch data in your app and pass it directly to the context-free panel. The package does not include Prisma, Next.js caching, route generation, ISR, or database fetching.
264
+
265
+ ```tsx
266
+ const services = await getCachedMapsData("afar", 2021, true);
267
+
268
+ return (
269
+ <EthiopiaRegionServicesPanel
270
+ regionName="Afar"
271
+ regionValue={34}
272
+ services={services.afar ?? []}
273
+ />
274
+ );
275
+ ```
276
+
124
277
  ## Exports
125
278
 
126
279
  - `EthiopiaMap`
@@ -129,6 +282,7 @@ export function Dashboard() {
129
282
  - `EthiopiaMapLegend`
130
283
  - `EthiopiaSelectedRegionCard`
131
284
  - `EthiopiaRegionServicesCard`
285
+ - `EthiopiaRegionServicesPanel`
132
286
  - `EthiopiaLocationsPanel`
133
287
  - `useEthiopiaMap`
134
288
  - `useSelectedRegionId`
@@ -1,10 +1,21 @@
1
- import type { EthiopiaMapProps, EthiopiaRegionId } from "../types";
1
+ import { type CSSProperties } from "react";
2
+ import type { EthiopiaMapProps, EthiopiaRegionId, EthiopiaServiceRowsIncrementalConfig } from "../types";
2
3
  export interface EthiopiaMapSurfaceProps {
3
4
  className?: string;
5
+ style?: CSSProperties;
4
6
  svgText?: string;
5
7
  svgUrl?: string;
6
8
  terrainImageUrl?: string;
9
+ inMapStatsClassName?: string;
10
+ inMapStatsStyle?: CSSProperties;
11
+ inMapStatsListClassName?: string;
12
+ inMapStatsListStyle?: CSSProperties;
13
+ inMapStatsEmptyText?: string;
14
+ incrementalRows?: EthiopiaServiceRowsIncrementalConfig;
15
+ showLayerControl?: boolean;
16
+ layerControlClassName?: string;
17
+ layerControlLabels?: Partial<Record<"regions" | "terrain", string>>;
7
18
  }
8
- export declare function EthiopiaMapSurface({ className, svgText, svgUrl, terrainImageUrl, }: EthiopiaMapSurfaceProps): import("react").JSX.Element;
9
- export declare function EthiopiaMap({ className, svgText, svgUrl, terrainImageUrl, ...providerProps }: EthiopiaMapProps): import("react").JSX.Element;
19
+ export declare function EthiopiaMapSurface({ className, style, svgText, svgUrl, terrainImageUrl, inMapStatsClassName, inMapStatsStyle, inMapStatsListClassName, inMapStatsListStyle, inMapStatsEmptyText, incrementalRows, showLayerControl, layerControlClassName, layerControlLabels, }: EthiopiaMapSurfaceProps): import("react").JSX.Element;
20
+ export declare function EthiopiaMap({ className, style, svgText, svgUrl, terrainImageUrl, inMapStatsClassName, inMapStatsStyle, inMapStatsListClassName, inMapStatsListStyle, inMapStatsEmptyText, incrementalRows, showLayerControl, layerControlClassName, layerControlLabels, ...providerProps }: EthiopiaMapProps): import("react").JSX.Element;
10
21
  export declare function useSelectedRegionId(): EthiopiaRegionId | null;
@@ -1,5 +1,11 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { EthiopiaServiceRowsIncrementalConfig } from "../types";
1
3
  export interface EthiopiaRegionServicesCardProps {
2
4
  className?: string;
5
+ style?: CSSProperties;
6
+ listClassName?: string;
7
+ listStyle?: CSSProperties;
3
8
  emptyText?: string;
9
+ incrementalRows?: EthiopiaServiceRowsIncrementalConfig;
4
10
  }
5
- export declare function EthiopiaRegionServicesCard({ className, emptyText, }: EthiopiaRegionServicesCardProps): import("react").JSX.Element;
11
+ export declare function EthiopiaRegionServicesCard({ className, style, listClassName, listStyle, emptyText, incrementalRows, }: EthiopiaRegionServicesCardProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { type CSSProperties } from "react";
2
+ import type { EthiopiaServiceRowsIncrementalConfig, ServiceStat } from "../types";
3
+ type ServicesPanelVariant = "card" | "overlay";
4
+ export interface EthiopiaRegionServicesPanelProps {
5
+ className?: string;
6
+ style?: CSSProperties;
7
+ listClassName?: string;
8
+ listStyle?: CSSProperties;
9
+ title?: string;
10
+ kicker?: string;
11
+ regionName?: string | null;
12
+ regionValue?: number | null;
13
+ services?: readonly ServiceStat[];
14
+ emptyText?: string;
15
+ variant?: ServicesPanelVariant;
16
+ incrementalRows?: EthiopiaServiceRowsIncrementalConfig;
17
+ getBarPercent?: (stat: ServiceStat) => number;
18
+ }
19
+ export declare function EthiopiaRegionServicesPanel({ className, style, listClassName, listStyle, title, kicker, regionName, regionValue, services, emptyText, variant, incrementalRows, getBarPercent, }: EthiopiaRegionServicesPanelProps): import("react").JSX.Element;
20
+ export {};
package/dist/context.d.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  import { type ReactNode } from "react";
2
- import type { EthiopiaMapConfig, EthiopiaMapProviderProps, EthiopiaMapSelection, EthiopiaRegionId, LatLngLocation, RegionDatum, RegionRecord, RegionServiceStats } from "./types";
3
- export interface EthiopiaMapContextValue extends Required<Omit<EthiopiaMapConfig, "regionColors">> {
2
+ import type { EthiopiaMapConfig, EthiopiaMapLayer, EthiopiaMapProviderProps, EthiopiaMapSelection, EthiopiaRegionId, LatLngLocation, RegionDatum, RegionRecord, RegionServiceStats } from "./types";
3
+ export interface EthiopiaMapContextValue extends Required<Omit<EthiopiaMapConfig, "defaultLayer" | "layer" | "onLayerChange" | "regionColors">> {
4
4
  data: RegionRecord<RegionDatum>;
5
5
  serviceStats: RegionServiceStats;
6
6
  locations: readonly LatLngLocation[];
7
7
  regionColors: NonNullable<EthiopiaMapConfig["regionColors"]>;
8
+ layer: EthiopiaMapLayer;
8
9
  selectedRegionId: EthiopiaRegionId | null;
9
10
  selectedRegion: RegionDatum | null;
10
11
  selectRegion: (regionId: EthiopiaRegionId | null) => void;
12
+ setLayer: (layer: EthiopiaMapLayer) => void;
11
13
  }
12
- export declare function EthiopiaMapProvider({ children, data, serviceStats, locations, selectedRegionId, defaultSelectedRegionId, onRegionSelect, theme, layer, viewMode, selectionAnimation, showLabels, showMarkers, showInMapStats, autoTour, autoTourSequence, autoTourIntervalMs, regionColors, }: EthiopiaMapProviderProps): import("react").JSX.Element;
14
+ export declare function EthiopiaMapProvider({ children, data, serviceStats, locations, selectedRegionId, defaultSelectedRegionId, onRegionSelect, theme, layer, defaultLayer, onLayerChange, viewMode, selectionAnimation, showLabels, showMarkers, showInMapStats, autoTour, autoTourSequence, autoTourIntervalMs, regionColors, }: EthiopiaMapProviderProps): import("react").JSX.Element;
13
15
  export declare function useEthiopiaMap(): EthiopiaMapContextValue;
14
16
  export declare function EthiopiaMapRoot({ children, ...providerProps }: Omit<EthiopiaMapProviderProps, "children"> & {
15
17
  children: ReactNode;
@@ -1 +1 @@
1
- .ethiopia-map-surface{--map-stroke: rgba(42, 73, 63, .42);--preview-base: #f7faf6;--region-shadow-rest: rgba(0, 0, 0, .3);--region-shadow-wall: rgba(0, 0, 0, .6);--region-shadow-mid: rgba(0, 0, 0, .42);--region-shadow-ambient: rgba(0, 0, 0, .3);--region-shadow-glow: rgba(23, 107, 84, .45);position:relative;min-height:520px;overflow:hidden;border:1px solid rgba(40,72,62,.22);border-radius:8px;background:linear-gradient(rgba(23,107,84,.06) 1px,transparent 1px),linear-gradient(90deg,rgba(23,107,84,.06) 1px,transparent 1px),#eef5ef;background-size:32px 32px}.ethiopia-map-surface.dark-mode{--map-stroke: rgba(238, 248, 244, .72);--preview-base: #102520;--region-shadow-rest: rgba(218, 229, 224, .28);--region-shadow-wall: rgba(218, 229, 224, .42);--region-shadow-mid: rgba(206, 221, 214, .3);--region-shadow-ambient: rgba(220, 234, 228, .22);--region-shadow-glow: rgba(222, 238, 231, .34);border-color:#c2ddd33d;background:linear-gradient(rgba(173,214,198,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(173,214,198,.07) 1px,transparent 1px),#15201c}.ethiopia-map-surface.bars-mode{background:linear-gradient(rgba(23,107,84,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(23,107,84,.05) 1px,transparent 1px),#f7faf7}.ethiopia-map-surface.dark-mode.bars-mode{background:#15201c}.ethiopia-map-svg-host{position:absolute;top:0;right:0;bottom:0;left:0}.ethiopia-map-surface svg{display:block;width:100%;height:100%}.ethiopia-map-watermark{position:absolute;top:20px;left:20px;z-index:5;width:auto;max-height:64px;object-fit:contain;opacity:.82;pointer-events:none;-webkit-user-select:none;user-select:none}.ethiopia-map-surface .map-region{transition:fill .18s ease,stroke .18s ease,opacity .18s ease,filter .18s ease,transform .26s ease;transform-box:fill-box;transform-origin:center}.ethiopia-map-surface.zooming .map-region{transition:none!important}.ethiopia-map-surface.zooming .region-outline-layer{opacity:0}.ethiopia-map-surface.zooming .map-region.pulse,.ethiopia-map-surface.zooming .map-region.lifted,.ethiopia-map-surface.zooming .map-region.outline-only{animation-play-state:paused;filter:none!important}.ethiopia-map-surface.zooming text,.ethiopia-map-surface.zooming tspan{opacity:0!important;transition:none!important}.ethiopia-map-surface .map-region:hover,.ethiopia-map-surface .map-region.selected{stroke:#fff!important}.ethiopia-map-surface text,.ethiopia-map-surface tspan{pointer-events:none}.ethiopia-map-surface.dark-mode .map-region{stroke:#e2efe9a3!important}.ethiopia-map-surface .map-region.pulse,.ethiopia-map-surface .map-region.lifted{transform-box:fill-box;transform-origin:center bottom;will-change:transform,filter}.ethiopia-map-surface .map-region.pulse{animation:regionFloat 3s cubic-bezier(.34,1.4,.64,1) infinite}.ethiopia-map-surface .map-region.lifted{animation:regionLiftHold .62s cubic-bezier(.34,1.4,.64,1) forwards}.ethiopia-map-surface .map-region.outline-only{fill:#ffffff0f!important;filter:drop-shadow(0 0 10px rgba(23,107,84,.28))}.ethiopia-map-surface .region-outline-layer{pointer-events:none}.ethiopia-map-surface .selected-region-outline,.ethiopia-map-surface .hover-region-outline{filter:drop-shadow(0 0 8px rgba(23,107,84,.36))}.ethiopia-map-surface.terrain-mode .map-region{fill:#ffffff08!important;stroke:#1b231fb8!important;stroke-width:1.5px!important}.ethiopia-map-surface.terrain-mode .map-region.selected{fill:#ffffff09!important}.ethiopia-map-surface.focus-mode .map-region:not(.selected){opacity:.3!important;fill:var(--preview-base)!important;stroke:var(--map-stroke)!important;filter:none}.ethiopia-map-surface.light-mode.focus-mode .map-region:not(.selected){fill:#fff!important}.ethiopia-map-surface.terrain-mode.focus-mode .map-region:not(.selected){fill:#ffffff06!important;opacity:.24!important;stroke:#1b231f75!important}.ethiopia-map-surface.focus-mode .map-region.selected{opacity:1!important;stroke:#fff!important}.ethiopia-map-surface.focus-mode text,.ethiopia-map-surface.focus-mode tspan{opacity:.24!important}.ethiopia-map-panel{border:1px solid rgba(40,72,62,.18);border-radius:8px;background:#ffffffb8;padding:14px;color:#17231f}.ethiopia-map-panel h2,.ethiopia-map-panel p{margin:0}.ethiopia-map-kicker{color:#60736b;font-size:12px;font-weight:800;text-transform:uppercase}.ethiopia-map-panel-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.ethiopia-map-legend-list,.ethiopia-service-list,.ethiopia-location-list{display:grid;gap:8px}.ethiopia-map-legend-list{max-height:300px;overflow:auto;padding-right:4px}.ethiopia-region-services .ethiopia-service-list{max-height:320px;overflow:auto;padding-right:4px}.ethiopia-map-legend-row{display:grid;grid-template-columns:12px minmax(0,1fr) 42px;gap:9px;align-items:center;border:1px solid rgba(40,72,62,.16);border-radius:6px;background:#ffffffa3;padding:8px;color:inherit;cursor:pointer;text-align:left}.ethiopia-map-legend-row.active{border-color:#176b546b}.ethiopia-map-swatch{width:12px;height:26px;border-radius:4px}.ethiopia-map-legend-title-line,.ethiopia-service-copy,.ethiopia-selected-region-stats{display:flex;align-items:baseline;justify-content:space-between;gap:10px}.ethiopia-map-legend-bar{display:block;height:5px;overflow:hidden;border-radius:999px;background:#14221d14}.ethiopia-map-legend-bar-fill{display:block;height:100%}.ethiopia-selected-region-preview,.ethiopia-empty-state{display:grid;place-items:center;border:1px solid rgba(40,72,62,.14);border-radius:8px;color:#60736b}.ethiopia-selected-region-preview{--ethiopia-preview-base: #f7faf6;--ethiopia-preview-stroke: rgba(42, 73, 63, .16);--region-shadow-rest: rgba(0, 0, 0, .3);--region-shadow-wall: rgba(0, 0, 0, .6);--region-shadow-mid: rgba(0, 0, 0, .42);--region-shadow-ambient: rgba(0, 0, 0, .3);--region-shadow-glow: rgba(23, 107, 84, .45);height:190px;margin:14px 0 0;background:#f7faf6;overflow:hidden}.ethiopia-empty-state{min-height:104px}.dark-mode .ethiopia-selected-region-preview,.dark-theme .ethiopia-selected-region-preview{--ethiopia-preview-base: #102520;--ethiopia-preview-stroke: rgba(255, 255, 255, .12);--region-shadow-rest: rgba(218, 229, 224, .28);--region-shadow-wall: rgba(218, 229, 224, .42);--region-shadow-mid: rgba(206, 221, 214, .3);--region-shadow-ambient: rgba(220, 234, 228, .22);--region-shadow-glow: rgba(222, 238, 231, .34);background:#102520}.ethiopia-selected-region-preview-canvas{width:100%;height:100%}.ethiopia-selected-region-preview-canvas svg{display:block;width:100%;height:100%}.ethiopia-selected-region-preview .map-region{transform-box:fill-box;transform-origin:center}.ethiopia-selected-region-preview .map-region.selected{stroke:#fff!important}.ethiopia-selected-region-preview .map-region.pulse,.ethiopia-selected-region-preview .map-region.lifted{transform-box:fill-box;transform-origin:center bottom;will-change:transform,filter}.ethiopia-selected-region-preview .map-region.pulse{animation:regionFloat 3s cubic-bezier(.34,1.4,.64,1) infinite}.ethiopia-selected-region-preview .map-region.lifted{animation:regionLiftHold .62s cubic-bezier(.34,1.4,.64,1) forwards}.ethiopia-selected-region-preview .map-region.outline-only{fill:#ffffff0d!important;filter:drop-shadow(0 0 8px rgba(23,107,84,.22))}.ethiopia-selected-region-preview>.ethiopia-empty-state{min-height:0;border:0;background:transparent}.ethiopia-selected-region-stats{margin-top:12px}.ethiopia-selected-region-stats div{min-width:0;flex:1;border:1px solid rgba(40,72,62,.14);border-radius:6px;padding:9px}.ethiopia-selected-region-stats span,.ethiopia-location-item span{display:block;color:#60736b;font-size:12px;font-weight:800;text-transform:uppercase}.ethiopia-service-row,.ethiopia-location-item{border:1px solid rgba(40,72,62,.14);border-radius:6px;padding:9px}.ethiopia-service-comparison{margin-top:8px;font-size:12px;font-weight:800;text-transform:uppercase}.ethiopia-service-comparison.positive{color:#176b54}.ethiopia-service-comparison.negative{color:#9f2f2f}.map-stat-overlay{position:absolute;box-sizing:border-box;right:12px;top:14px;z-index:8;width:min(336px,calc(100% - 28px));padding:12px;border:1px solid rgba(38,72,60,.18);border-radius:8px;background:#ffffffbd;box-shadow:0 1px #ffffff94 inset,0 22px 70px #1c2d2724;color:#17231f;opacity:0;pointer-events:none;transform:translateY(-12px) scale(.98);transition:opacity .22s ease,transform .26s cubic-bezier(.22,1,.36,1)}@supports (backdrop-filter: blur(18px)){.map-stat-overlay{-webkit-backdrop-filter:blur(18px) saturate(1.18);backdrop-filter:blur(18px) saturate(1.18)}}.map-stat-overlay.visible{opacity:1;pointer-events:auto;transform:translateY(0) scale(1)}.dark-mode .map-stat-overlay{border-color:#d8f4e824;background:#0d1c19cc;color:#edf7f2}.map-stat-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:10px}.map-stat-heading h2{margin:0;font-size:22px;line-height:1.05}.map-stat-index{display:grid;place-items:center;min-width:40px;height:40px;border:1px solid rgba(23,107,84,.28);border-radius:8px;background:#176b541a;color:#176b54;font-size:17px;font-weight:900}.map-stat-list{display:grid;gap:7px;max-height:min(60vh,560px);overflow:auto;padding-right:5px}.map-stat-empty{padding:18px 12px;border:1px solid rgba(40,72,62,.14);border-radius:8px;background:#ffffff6b;color:#60736b;font-size:14px;line-height:1.45;text-align:center}.map-stat-row{padding:8px;border:1px solid rgba(40,72,62,.14);border-radius:8px;background:#ffffff7a;opacity:0;transform:translate(16px);animation:statSlideIn .52s cubic-bezier(.22,1,.36,1) forwards;animation-delay:var(--delay)}.dark-mode .map-stat-row{border-color:#e8f4ef24;background:#ffffff0f}.map-stat-copy{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:7px}.map-stat-copy span{color:#60736b;font-size:11px;font-weight:800;text-transform:uppercase}.dark-mode .map-stat-copy span{color:#9eb8ae}.map-stat-copy strong{color:inherit;font-size:15px;font-weight:900}.map-stat-comparison{display:inline-flex;align-items:center;max-width:100%;gap:6px;margin-bottom:9px;padding:5px 8px;border:1px solid rgba(40,72,62,.14);border-radius:8px;background:#ffffff80;color:#60736b}.dark-mode .map-stat-comparison{border-color:#e8f4ef24;background:#ffffff0f}.map-stat-comparison span{font-size:9px;font-weight:900;text-transform:uppercase}.map-stat-comparison strong{color:inherit;font-size:11px;font-weight:900;white-space:nowrap}.map-stat-comparison.positive{border-color:#176b543d;background:#176b541a;color:#176b54}.map-stat-comparison.negative{border-color:#b041373d;background:#b041371a;color:#9d342c}.map-stat-comparison.neutral{border-color:#626f693d;background:#626f691a;color:#626f69}.dark-mode .map-stat-comparison.positive{color:#8be0bf}.dark-mode .map-stat-comparison.negative{color:#ffaaa0}.dark-mode .map-stat-comparison.neutral{color:#b8c6bf}.map-stat-bar{height:8px;overflow:hidden;border:1px solid rgba(40,72,62,.14);border-radius:999px;background:#274b3f14}.map-stat-bar i{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#176b549e,#176b54);transform-origin:left;animation:statBarGrow .72s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--delay) + .12s)}.bar-morph-track{fill:#15282214}.bar-morph-bar{filter:drop-shadow(0 8px 12px rgba(23,107,84,.14))}.bar-row{opacity:1;transition:opacity .36s ease}.bar-row.visible{opacity:1!important}.bar-region-ghost{filter:drop-shadow(0 8px 18px rgba(28,45,39,.2))}.bar-morph-label,.bar-morph-value{fill:#17231f;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:24px;font-weight:800}.bar-morph-value{font-size:22px;text-anchor:end}.dark-mode .bar-morph-track{fill:#e8f4ef1f}.dark-mode .bar-morph-label,.dark-mode .bar-morph-value{fill:#edf7f2}@keyframes regionFloat{0%{transform:translateZ(0) scaleY(1);filter:drop-shadow(0 1px 2px var(--region-shadow-rest)) drop-shadow(0 0 6px var(--region-shadow-glow))}45%{transform:translate3d(-3px,-18px,0) scaleY(1.018);filter:drop-shadow(4px 22px 0 var(--region-shadow-wall)) drop-shadow(3px 18px 2px var(--region-shadow-mid)) drop-shadow(1px 8px 12px var(--region-shadow-ambient)) drop-shadow(0 0 14px var(--region-shadow-glow))}62%{transform:translate3d(-4px,-22px,0) scaleY(1.022);filter:drop-shadow(5px 26px 0 var(--region-shadow-wall)) drop-shadow(4px 20px 3px var(--region-shadow-mid)) drop-shadow(2px 10px 14px var(--region-shadow-ambient)) drop-shadow(0 0 18px var(--region-shadow-glow))}82%{transform:translate3d(-1px,-6px,0) scaleY(1.006);filter:drop-shadow(2px 8px 0 var(--region-shadow-mid)) drop-shadow(1px 6px 8px var(--region-shadow-ambient)) drop-shadow(0 0 10px var(--region-shadow-glow))}to{transform:translateZ(0) scaleY(1);filter:drop-shadow(0 1px 2px var(--region-shadow-rest)) drop-shadow(0 0 6px var(--region-shadow-glow))}}@keyframes regionLiftHold{0%{transform:translateZ(0) scaleY(1);filter:drop-shadow(0 1px 2px var(--region-shadow-rest)) drop-shadow(0 0 6px var(--region-shadow-glow))}70%{transform:translate3d(-4px,-22px,0) scaleY(1.022);filter:drop-shadow(5px 26px 0 var(--region-shadow-wall)) drop-shadow(4px 20px 3px var(--region-shadow-mid)) drop-shadow(2px 10px 14px var(--region-shadow-ambient)) drop-shadow(0 0 18px var(--region-shadow-glow))}to{transform:translate3d(-4px,-22px,0) scaleY(1.022);filter:drop-shadow(5px 26px 0 var(--region-shadow-wall)) drop-shadow(4px 20px 3px var(--region-shadow-mid)) drop-shadow(2px 10px 14px var(--region-shadow-ambient)) drop-shadow(0 0 18px var(--region-shadow-glow))}}@keyframes statSlideIn{to{opacity:1;transform:translate(0)}}@keyframes statBarGrow{0%{transform:scaleX(0)}to{transform:scaleX(1)}}.react-demo-shell{display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:18px;min-height:100vh;padding:18px;background:#e8eee8;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.react-demo-shell.dark-theme{background:#111c18}.react-demo-map,.react-demo-sidebar{display:grid;gap:14px;align-content:start}.react-demo-sidebar{max-height:calc(100vh - 36px);overflow:auto;padding-right:2px}.react-demo-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}.react-demo-map h1{margin:0;color:#17231f;font-size:44px;line-height:.95}.dark-theme .react-demo-map h1{color:#edf7f2}.react-demo-mode-switcher,.react-demo-selection-switcher{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;max-width:520px}.react-demo-selection-switcher{justify-self:end;max-width:none}.react-demo-mode-switcher button,.react-demo-selection-switcher button{border:1px solid rgba(38,83,69,.22);border-radius:6px;background:#ffffffc7;color:#17231f;cursor:pointer;font:inherit;font-size:13px;font-weight:800;padding:9px 11px}.react-demo-mode-switcher button.active,.react-demo-selection-switcher button.active{border-color:#176b54;background:#176b54;color:#fff}.dark-theme .react-demo-mode-switcher button,.dark-theme .react-demo-selection-switcher button{border-color:#e8f4ef33;background:#ffffff14;color:#edf7f2}.dark-theme .react-demo-mode-switcher button.active,.dark-theme .react-demo-selection-switcher button.active{border-color:#6dc7a8;background:#6dc7a8;color:#10201a}.react-demo-map .ethiopia-map-surface{height:calc(100vh - 128px)}.dark-theme .ethiopia-map-panel{border-color:#e8f4ef29;background:#ffffff14;color:#edf7f2}.dark-theme .ethiopia-map-kicker,.dark-theme .ethiopia-selected-region-stats span,.dark-theme .ethiopia-location-item span{color:#9eb8ae}.dark-theme .ethiopia-map-legend-row,.dark-theme .ethiopia-service-row,.dark-theme .ethiopia-location-item,.dark-theme .ethiopia-selected-region-stats div,.dark-theme .ethiopia-selected-region-preview,.dark-theme .ethiopia-empty-state{border-color:#e8f4ef24;background:#ffffff0f}@media(max-width:900px){.react-demo-shell{grid-template-columns:1fr}.react-demo-header{display:grid}.react-demo-mode-switcher{justify-content:flex-start}.react-demo-selection-switcher{justify-self:start}}
1
+ .ethiopia-map-surface{--map-stroke: rgba(42, 73, 63, .42);--preview-base: #f7faf6;--region-shadow-rest: rgba(0, 0, 0, .3);--region-shadow-wall: rgba(0, 0, 0, .6);--region-shadow-mid: rgba(0, 0, 0, .42);--region-shadow-ambient: rgba(0, 0, 0, .3);--region-shadow-glow: rgba(23, 107, 84, .45);position:relative;min-height:520px;overflow:hidden;border:1px solid rgba(40,72,62,.22);border-radius:8px;background:linear-gradient(rgba(23,107,84,.06) 1px,transparent 1px),linear-gradient(90deg,rgba(23,107,84,.06) 1px,transparent 1px),#eef5ef;background-size:32px 32px}.ethiopia-map-surface.dark-mode{--map-stroke: rgba(238, 248, 244, .72);--preview-base: #102520;--region-shadow-rest: rgba(218, 229, 224, .28);--region-shadow-wall: rgba(218, 229, 224, .42);--region-shadow-mid: rgba(206, 221, 214, .3);--region-shadow-ambient: rgba(220, 234, 228, .22);--region-shadow-glow: rgba(222, 238, 231, .34);border-color:#c2ddd33d;background:linear-gradient(rgba(173,214,198,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(173,214,198,.07) 1px,transparent 1px),#15201c}.ethiopia-map-surface.bars-mode{background:linear-gradient(rgba(23,107,84,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(23,107,84,.05) 1px,transparent 1px),#f7faf7}.ethiopia-map-surface.dark-mode.bars-mode{background:#15201c}.ethiopia-map-svg-host{position:absolute;top:0;right:0;bottom:0;left:0}.ethiopia-map-surface svg{display:block;width:100%;height:100%}.ethiopia-map-watermark{position:absolute;top:20px;left:20px;z-index:5;width:auto;max-height:64px;object-fit:contain;opacity:.82;pointer-events:none;-webkit-user-select:none;user-select:none}.ethiopia-map-layer-toggle{position:absolute;left:18px;bottom:18px;z-index:6;display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:6px 10px;border:1px solid rgba(181,202,194,.82);border-radius:999px;background:#ffffffe0;color:#176b54;box-shadow:0 8px 18px #1c2d2721;cursor:pointer;font:inherit;font-size:12px;font-weight:800;line-height:1;transition:background .16s ease,border-color .16s ease,box-shadow .16s ease,transform .16s ease}.ethiopia-map-layer-toggle:hover{transform:translateY(-1px);border-color:#176b545c;background:#fffffff5;box-shadow:0 12px 22px #1c2d272b}.ethiopia-map-layer-toggle:focus-visible{outline:3px solid rgba(23,107,84,.2);outline-offset:2px}.ethiopia-map-layer-toggle__icon{width:14px;height:14px;border-radius:4px;border:1px solid rgba(23,107,84,.46);background:linear-gradient(135deg,transparent 45%,rgba(255,255,255,.78) 46% 55%,transparent 56%),linear-gradient(135deg,#6fb99d,#d8c07e);box-shadow:inset 0 0 0 1px #ffffff70}.ethiopia-map-layer-toggle.is-terrain .ethiopia-map-layer-toggle__icon{background:linear-gradient(135deg,transparent 45%,rgba(255,255,255,.72) 46% 55%,transparent 56%),linear-gradient(135deg,#315b44,#caa66b 58%,#eee3b6)}.ethiopia-map-surface .map-region{transition:fill .18s ease,stroke .18s ease,opacity .18s ease,filter .18s ease,transform .26s ease;transform-box:fill-box;transform-origin:center}.ethiopia-map-surface.zooming .map-region{transition:none!important}.ethiopia-map-surface.zooming .region-outline-layer{opacity:0}.ethiopia-map-surface.zooming .map-region.pulse,.ethiopia-map-surface.zooming .map-region.lifted,.ethiopia-map-surface.zooming .map-region.outline-only{animation-play-state:paused;filter:none!important}.ethiopia-map-surface .map-region:hover,.ethiopia-map-surface .map-region.selected{stroke:#fff!important}.ethiopia-map-surface text,.ethiopia-map-surface tspan{pointer-events:none}.ethiopia-map-surface.dark-mode .map-region{stroke:#e2efe9a3!important}.ethiopia-map-surface .map-region.pulse,.ethiopia-map-surface .map-region.lifted{transform-box:fill-box;transform-origin:center bottom;will-change:transform,filter}.ethiopia-map-surface .map-region.pulse{animation:regionFloat 3s cubic-bezier(.34,1.4,.64,1) infinite}.ethiopia-map-surface .map-region.lifted{animation:regionLiftHold .62s cubic-bezier(.34,1.4,.64,1) forwards}.ethiopia-map-surface .map-region.outline-only{fill:#ffffff0f!important;filter:drop-shadow(0 0 10px rgba(23,107,84,.28))}.ethiopia-map-surface .region-outline-layer{pointer-events:none}.ethiopia-map-surface .selected-region-outline,.ethiopia-map-surface .hover-region-outline{filter:drop-shadow(0 0 8px rgba(23,107,84,.36))}.ethiopia-map-surface.terrain-mode .map-region{fill:#ffffff08!important;stroke:#1b231fb8!important;stroke-width:1.5px!important}.ethiopia-map-surface.terrain-mode .map-region.selected{fill:#ffffff09!important}.ethiopia-map-surface.focus-mode .map-region:not(.selected){opacity:.3!important;fill:var(--preview-base)!important;stroke:var(--map-stroke)!important;filter:none}.ethiopia-map-surface.light-mode.focus-mode .map-region:not(.selected){fill:#fff!important}.ethiopia-map-surface.terrain-mode.focus-mode .map-region:not(.selected){fill:#ffffff06!important;opacity:.24!important;stroke:#1b231f75!important}.ethiopia-map-surface.light-mode.terrain-mode.focus-mode .map-region:not(.selected){fill:#ffffff06!important;opacity:.24!important;stroke:#1b231f75!important}.ethiopia-map-surface.focus-mode .map-region.selected{opacity:1!important;stroke:#fff!important}.ethiopia-map-surface.focus-mode text,.ethiopia-map-surface.focus-mode tspan{opacity:.24!important}.ethiopia-map-panel{border:1px solid rgba(40,72,62,.18);border-radius:8px;background:#ffffffb8;padding:14px;color:#17231f}.ethiopia-map-panel h2,.ethiopia-map-panel p{margin:0}.ethiopia-map-kicker{color:#60736b;font-size:12px;font-weight:800;text-transform:uppercase}.ethiopia-map-panel-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.ethiopia-map-legend-list,.ethiopia-service-list,.ethiopia-location-list{display:grid;gap:8px}.ethiopia-service-list,.map-stat-list{scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:rgba(23,107,84,.42) rgba(23,107,84,.08)}.ethiopia-service-list::-webkit-scrollbar,.map-stat-list::-webkit-scrollbar{width:8px}.ethiopia-service-list::-webkit-scrollbar-track,.map-stat-list::-webkit-scrollbar-track{background:#176b5414;border-radius:999px}.ethiopia-service-list::-webkit-scrollbar-thumb,.map-stat-list::-webkit-scrollbar-thumb{background:#176b546b;border-radius:999px}.ethiopia-map-legend-list{max-height:300px;overflow:auto;padding-right:4px}.ethiopia-region-services .ethiopia-service-list{max-height:320px;overflow:auto;padding-right:4px}.ethiopia-map-legend-row{display:grid;grid-template-columns:12px minmax(0,1fr) 42px;gap:9px;align-items:center;border:1px solid rgba(40,72,62,.16);border-radius:6px;background:#ffffffa3;padding:8px;color:inherit;cursor:pointer;text-align:left}.ethiopia-map-legend-row.active{border-color:#176b546b}.ethiopia-map-swatch{width:12px;height:26px;border-radius:4px}.ethiopia-map-legend-title-line,.ethiopia-service-copy,.ethiopia-selected-region-stats{display:flex;align-items:baseline;justify-content:space-between;gap:10px}.ethiopia-map-legend-bar{display:block;height:5px;overflow:hidden;border-radius:999px;background:#14221d14}.ethiopia-map-legend-bar-fill{display:block;height:100%}.ethiopia-selected-region-preview,.ethiopia-empty-state{display:grid;place-items:center;border:1px solid rgba(40,72,62,.14);border-radius:8px;color:#60736b}.ethiopia-selected-region-preview{--ethiopia-preview-base: #f7faf6;--ethiopia-preview-stroke: rgba(42, 73, 63, .16);--region-shadow-rest: rgba(0, 0, 0, .3);--region-shadow-wall: rgba(0, 0, 0, .6);--region-shadow-mid: rgba(0, 0, 0, .42);--region-shadow-ambient: rgba(0, 0, 0, .3);--region-shadow-glow: rgba(23, 107, 84, .45);height:190px;margin:14px 0 0;background:#f7faf6;overflow:hidden}.ethiopia-empty-state{min-height:104px}.dark-mode .ethiopia-selected-region-preview,.dark-theme .ethiopia-selected-region-preview{--ethiopia-preview-base: #102520;--ethiopia-preview-stroke: rgba(255, 255, 255, .12);--region-shadow-rest: rgba(218, 229, 224, .28);--region-shadow-wall: rgba(218, 229, 224, .42);--region-shadow-mid: rgba(206, 221, 214, .3);--region-shadow-ambient: rgba(220, 234, 228, .22);--region-shadow-glow: rgba(222, 238, 231, .34);background:#102520}.ethiopia-selected-region-preview-canvas{width:100%;height:100%}.ethiopia-selected-region-preview-canvas svg{display:block;width:100%;height:100%}.ethiopia-selected-region-preview .map-region{transform-box:fill-box;transform-origin:center}.ethiopia-selected-region-preview .map-region.selected{stroke:#fff!important}.ethiopia-selected-region-preview .map-region.pulse,.ethiopia-selected-region-preview .map-region.lifted{transform-box:fill-box;transform-origin:center bottom;will-change:transform,filter}.ethiopia-selected-region-preview .map-region.pulse{animation:regionFloat 3s cubic-bezier(.34,1.4,.64,1) infinite}.ethiopia-selected-region-preview .map-region.lifted{animation:regionLiftHold .62s cubic-bezier(.34,1.4,.64,1) forwards}.ethiopia-selected-region-preview .map-region.outline-only{fill:#ffffff0d!important;filter:drop-shadow(0 0 8px rgba(23,107,84,.22))}.ethiopia-selected-region-preview>.ethiopia-empty-state{min-height:0;border:0;background:transparent}.ethiopia-selected-region-stats{margin-top:12px}.ethiopia-selected-region-stats div{min-width:0;flex:1;border:1px solid rgba(40,72,62,.14);border-radius:6px;padding:9px}.ethiopia-selected-region-stats span,.ethiopia-location-item span{display:block;color:#60736b;font-size:12px;font-weight:800;text-transform:uppercase}.ethiopia-service-row,.ethiopia-location-item{border:1px solid rgba(40,72,62,.14);border-radius:6px;padding:9px}.ethiopia-service-comparison{margin-top:8px;font-size:12px;font-weight:800;text-transform:uppercase}.ethiopia-service-comparison.positive{color:#176b54}.ethiopia-service-comparison.negative{color:#9f2f2f}.ethiopia-service-show-more,.map-stat-show-more{width:100%;border:1px solid rgba(23,107,84,.22);border-radius:6px;background:#176b5414;color:#176b54;cursor:pointer;font:inherit;font-size:12px;font-weight:900;padding:8px 10px;text-transform:uppercase}.ethiopia-service-show-more:hover,.map-stat-show-more:hover{background:#176b5421}.ethiopia-service-scroll-sentinel,.map-stat-scroll-sentinel{display:grid;place-items:center;width:100%}.ethiopia-service-scroll-sentinel.visible,.map-stat-scroll-sentinel.visible{min-height:26px;border:1px dashed rgba(23,107,84,.22);border-radius:6px;background:#176b540d;color:#176b54;font-size:11px;font-weight:900;letter-spacing:0;text-transform:uppercase}.ethiopia-service-scroll-sentinel.hidden,.map-stat-scroll-sentinel.hidden{min-height:1px;border:0;background:transparent;color:transparent}.dark-theme .ethiopia-service-scroll-sentinel.visible,.dark-mode .map-stat-scroll-sentinel.visible{border-color:#8be0bf38;background:#8be0bf12;color:#8be0bf}.dark-theme .ethiopia-service-show-more,.dark-mode .map-stat-show-more{border-color:#8be0bf33;background:#8be0bf1a;color:#8be0bf}.dark-theme .ethiopia-service-show-more:hover,.dark-mode .map-stat-show-more:hover{background:#8be0bf29}.map-stat-overlay{position:absolute;box-sizing:border-box;right:12px;top:14px;z-index:8;width:min(336px,calc(100% - 28px));max-height:calc(100% - 28px);display:flex;flex-direction:column;padding:12px;border:1px solid rgba(38,72,60,.18);border-radius:8px;background:#ffffffbd;box-shadow:0 1px #ffffff94 inset,0 22px 70px #1c2d2724;color:#17231f;opacity:0;pointer-events:none;transform:translateY(-12px) scale(.98);transition:opacity .22s ease,transform .26s cubic-bezier(.22,1,.36,1)}@supports (backdrop-filter: blur(18px)){.map-stat-overlay{-webkit-backdrop-filter:blur(18px) saturate(1.18);backdrop-filter:blur(18px) saturate(1.18)}}.map-stat-overlay.visible{opacity:1;pointer-events:auto;transform:translateY(0) scale(1)}.dark-mode .map-stat-overlay{border-color:#d8f4e824;background:#0d1c19cc;color:#edf7f2}.map-stat-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:10px}.map-stat-heading h2{margin:0;font-size:22px;line-height:1.05}.map-stat-index{display:grid;place-items:center;min-width:40px;height:40px;border:1px solid rgba(23,107,84,.28);border-radius:8px;background:#176b541a;color:#176b54;font-size:17px;font-weight:900}.map-stat-list{display:grid;gap:7px;flex:1;min-height:0;max-height:none;overflow:auto;padding-right:5px}.map-stat-empty{padding:18px 12px;border:1px solid rgba(40,72,62,.14);border-radius:8px;background:#ffffff6b;color:#60736b;font-size:14px;line-height:1.45;text-align:center}.map-stat-row{--delay: 0ms;--row-animation-duration: .52s;padding:8px;border:1px solid rgba(40,72,62,.14);border-radius:8px;background:#ffffff7a;opacity:0;transform:translate(16px);animation:statSlideIn var(--row-animation-duration) cubic-bezier(.22,1,.36,1) forwards;animation-delay:var(--delay)}.dark-mode .map-stat-row{border-color:#e8f4ef24;background:#ffffff0f}.map-stat-copy{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:7px}.map-stat-copy span{color:#60736b;font-size:11px;font-weight:800;text-transform:uppercase}.dark-mode .map-stat-copy span{color:#9eb8ae}.map-stat-copy strong{color:inherit;font-size:15px;font-weight:900}.map-stat-comparison{display:inline-flex;align-items:center;max-width:100%;gap:6px;margin-bottom:9px;padding:5px 8px;border:1px solid rgba(40,72,62,.14);border-radius:8px;background:#ffffff80;color:#60736b}.dark-mode .map-stat-comparison{border-color:#e8f4ef24;background:#ffffff0f}.map-stat-comparison span{font-size:9px;font-weight:900;text-transform:uppercase}.map-stat-comparison strong{color:inherit;font-size:11px;font-weight:900;white-space:nowrap}.map-stat-comparison.positive{border-color:#176b543d;background:#176b541a;color:#176b54}.map-stat-comparison.negative{border-color:#b041373d;background:#b041371a;color:#9d342c}.map-stat-comparison.neutral{border-color:#626f693d;background:#626f691a;color:#626f69}.dark-mode .map-stat-comparison.positive{color:#8be0bf}.dark-mode .map-stat-comparison.negative{color:#ffaaa0}.dark-mode .map-stat-comparison.neutral{color:#b8c6bf}.map-stat-bar{height:8px;overflow:hidden;border:1px solid rgba(40,72,62,.14);border-radius:999px;background:#274b3f14}.map-stat-bar i{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#176b549e,#176b54);transform-origin:left;animation:statBarGrow var(--row-animation-duration) cubic-bezier(.22,1,.36,1) both;animation-delay:var(--delay)}.bar-morph-track{fill:#15282214}.bar-morph-bar{filter:drop-shadow(0 8px 12px rgba(23,107,84,.14))}.bar-row{opacity:1;transition:opacity .36s ease}.bar-row.visible{opacity:1!important}.bar-region-ghost{filter:drop-shadow(0 8px 18px rgba(28,45,39,.2))}.bar-morph-label,.bar-morph-value{fill:#17231f;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:24px;font-weight:800}.bar-morph-value{font-size:22px;text-anchor:end}.dark-mode .bar-morph-track{fill:#e8f4ef1f}.dark-mode .bar-morph-label,.dark-mode .bar-morph-value{fill:#edf7f2}@keyframes regionFloat{0%{transform:translateZ(0) scaleY(1);filter:drop-shadow(0 1px 2px var(--region-shadow-rest)) drop-shadow(0 0 6px var(--region-shadow-glow))}45%{transform:translate3d(-3px,-18px,0) scaleY(1.018);filter:drop-shadow(4px 22px 0 var(--region-shadow-wall)) drop-shadow(3px 18px 2px var(--region-shadow-mid)) drop-shadow(1px 8px 12px var(--region-shadow-ambient)) drop-shadow(0 0 14px var(--region-shadow-glow))}62%{transform:translate3d(-4px,-22px,0) scaleY(1.022);filter:drop-shadow(5px 26px 0 var(--region-shadow-wall)) drop-shadow(4px 20px 3px var(--region-shadow-mid)) drop-shadow(2px 10px 14px var(--region-shadow-ambient)) drop-shadow(0 0 18px var(--region-shadow-glow))}82%{transform:translate3d(-1px,-6px,0) scaleY(1.006);filter:drop-shadow(2px 8px 0 var(--region-shadow-mid)) drop-shadow(1px 6px 8px var(--region-shadow-ambient)) drop-shadow(0 0 10px var(--region-shadow-glow))}to{transform:translateZ(0) scaleY(1);filter:drop-shadow(0 1px 2px var(--region-shadow-rest)) drop-shadow(0 0 6px var(--region-shadow-glow))}}@keyframes regionLiftHold{0%{transform:translateZ(0) scaleY(1);filter:drop-shadow(0 1px 2px var(--region-shadow-rest)) drop-shadow(0 0 6px var(--region-shadow-glow))}70%{transform:translate3d(-4px,-22px,0) scaleY(1.022);filter:drop-shadow(5px 26px 0 var(--region-shadow-wall)) drop-shadow(4px 20px 3px var(--region-shadow-mid)) drop-shadow(2px 10px 14px var(--region-shadow-ambient)) drop-shadow(0 0 18px var(--region-shadow-glow))}to{transform:translate3d(-4px,-22px,0) scaleY(1.022);filter:drop-shadow(5px 26px 0 var(--region-shadow-wall)) drop-shadow(4px 20px 3px var(--region-shadow-mid)) drop-shadow(2px 10px 14px var(--region-shadow-ambient)) drop-shadow(0 0 18px var(--region-shadow-glow))}}@keyframes statSlideIn{to{opacity:1;transform:translate(0)}}@keyframes statBarGrow{0%{transform:scaleX(0)}to{transform:scaleX(1)}}.react-demo-shell{display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:18px;min-height:100vh;padding:18px;background:#e8eee8;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.react-demo-shell.dark-theme{background:#111c18}.react-demo-map,.react-demo-sidebar{display:grid;gap:14px;align-content:start}.react-demo-sidebar{max-height:calc(100vh - 36px);overflow:auto;padding-right:2px}.react-demo-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}.react-demo-map h1{margin:0;color:#17231f;font-size:44px;line-height:.95}.dark-theme .react-demo-map h1{color:#edf7f2}.react-demo-mode-switcher,.react-demo-selection-switcher{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;max-width:520px}.react-demo-selection-switcher{justify-self:end;max-width:none}.react-demo-mode-switcher button,.react-demo-selection-switcher button{border:1px solid rgba(38,83,69,.22);border-radius:6px;background:#ffffffc7;color:#17231f;cursor:pointer;font:inherit;font-size:13px;font-weight:800;padding:9px 11px}.react-demo-mode-switcher button.active,.react-demo-selection-switcher button.active{border-color:#176b54;background:#176b54;color:#fff}.dark-theme .react-demo-mode-switcher button,.dark-theme .react-demo-selection-switcher button{border-color:#e8f4ef33;background:#ffffff14;color:#edf7f2}.dark-theme .react-demo-mode-switcher button.active,.dark-theme .react-demo-selection-switcher button.active{border-color:#6dc7a8;background:#6dc7a8;color:#10201a}.react-demo-map .ethiopia-map-surface{height:calc(100vh - 128px)}.dark-theme .ethiopia-map-panel{border-color:#e8f4ef29;background:#ffffff14;color:#edf7f2}.dark-theme .ethiopia-map-kicker,.dark-theme .ethiopia-selected-region-stats span,.dark-theme .ethiopia-location-item span{color:#9eb8ae}.dark-theme .ethiopia-map-legend-row,.dark-theme .ethiopia-service-row,.dark-theme .ethiopia-location-item,.dark-theme .ethiopia-selected-region-stats div,.dark-theme .ethiopia-selected-region-preview,.dark-theme .ethiopia-empty-state{border-color:#e8f4ef24;background:#ffffff0f}@media(max-width:900px){.react-demo-shell{grid-template-columns:1fr}.react-demo-header{display:grid}.react-demo-mode-switcher{justify-content:flex-start}.react-demo-selection-switcher{justify-self:start}}