@mailwoman/react 9.4.0 → 10.1.0

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.
Files changed (370) hide show
  1. package/README.md +17 -16
  2. package/fonts.css +97 -22
  3. package/lib/common/About.tsx +87 -71
  4. package/lib/common/ClientOnly.tsx +2 -14
  5. package/lib/common/CopyButton.tsx +3 -1
  6. package/lib/common/LoadingIndicator.tsx +4 -2
  7. package/lib/common/PresetChips.tsx +9 -8
  8. package/lib/common/confidence-tiers.ts +7 -6
  9. package/lib/common/cx.ts +3 -1
  10. package/lib/common/text-tokens.ts +7 -4
  11. package/lib/common/useBuildInfo.ts +54 -0
  12. package/lib/common/useClipboard.ts +2 -11
  13. package/lib/common.ts +22 -0
  14. package/lib/index.ts +27 -112
  15. package/lib/map/AppIdentity.tsx +85 -0
  16. package/lib/map/BackendControl.tsx +16 -13
  17. package/lib/map/CompareToggle.tsx +11 -33
  18. package/lib/map/DebugInfo.tsx +88 -0
  19. package/lib/map/Geocoder.tsx +92 -58
  20. package/lib/map/GeocoderControls.tsx +582 -127
  21. package/lib/map/GraticuleLayer.tsx +54 -0
  22. package/lib/map/MapCanvas.tsx +23 -12
  23. package/lib/map/MapChipRow.tsx +73 -0
  24. package/lib/map/MapCompass.tsx +68 -0
  25. package/lib/map/MapControlStack.tsx +97 -0
  26. package/lib/map/MapFooter.tsx +101 -0
  27. package/lib/map/MapProgressBar.tsx +55 -0
  28. package/lib/map/MapSearchBar.tsx +82 -0
  29. package/lib/map/MapSheet.tsx +71 -0
  30. package/lib/map/OverlayLayers.tsx +1 -1
  31. package/lib/map/PlaceAutocomplete.tsx +10 -21
  32. package/lib/map/PlaceMarker.tsx +4 -2
  33. package/lib/map/ResolvedPlaceLayers.tsx +5 -14
  34. package/lib/map/ResultCamera.tsx +8 -31
  35. package/lib/map/ResultOverlay.tsx +2 -12
  36. package/lib/map/ResultPanel.tsx +1 -20
  37. package/lib/map/VersionPicker.tsx +10 -19
  38. package/lib/map/fake-runtime.ts +10 -12
  39. package/lib/map/geometry.ts +14 -47
  40. package/lib/map/graticule.ts +80 -0
  41. package/lib/map/map-debug.ts +1 -1
  42. package/lib/map/place-render.ts +28 -60
  43. package/lib/map/types.ts +65 -97
  44. package/lib/map/useCompareState.ts +5 -4
  45. package/lib/map/useGeocode.ts +7 -5
  46. package/lib/map/useMapBearing.ts +56 -0
  47. package/lib/map/useMapLabelPick.ts +114 -0
  48. package/lib/map/useMapPlaceRender.ts +2 -6
  49. package/lib/map/usePlaceAutocomplete.ts +44 -50
  50. package/lib/map.ts +95 -0
  51. package/lib/pipeline/CandidatePicker.tsx +13 -5
  52. package/lib/pipeline/ConfidenceCell.tsx +1 -1
  53. package/lib/pipeline/FailureDiagnostic.tsx +8 -2
  54. package/lib/pipeline/QueryForm.tsx +5 -2
  55. package/lib/pipeline/ResolvedPlace.tsx +1 -1
  56. package/lib/pipeline/SpanHighlight.tsx +7 -5
  57. package/lib/pipeline/TimingPanel.tsx +1 -1
  58. package/lib/pipeline/TreeView.tsx +1 -1
  59. package/lib/pipeline/copy.ts +22 -25
  60. package/lib/pipeline/presets.ts +15 -13
  61. package/lib/pipeline/types.ts +21 -14
  62. package/lib/pipeline/useParsePipeline.ts +38 -24
  63. package/lib/pipeline.ts +36 -0
  64. package/lib/poi/AbstainPanel.tsx +1 -1
  65. package/lib/poi/LiveResultsBlock.tsx +2 -1
  66. package/lib/poi/POIExplorer.tsx +24 -15
  67. package/lib/poi/QueryInput.tsx +1 -1
  68. package/lib/poi/SubjectPanel.tsx +4 -3
  69. package/lib/poi/runtime.ts +5 -16
  70. package/lib/poi/types.ts +39 -27
  71. package/lib/poi/usePOISearch.ts +39 -44
  72. package/lib/poi.ts +39 -0
  73. package/lib/runtime/useReleaseRuntime.ts +125 -74
  74. package/lib/runtime.ts +16 -0
  75. package/lib/search/SearchModal.tsx +317 -0
  76. package/lib/search/search.css +144 -0
  77. package/lib/search/types.ts +67 -0
  78. package/lib/search.ts +11 -0
  79. package/out/common/About.d.ts +13 -4
  80. package/out/common/About.d.ts.map +1 -1
  81. package/out/common/About.js +5 -2
  82. package/out/common/About.js.map +1 -1
  83. package/out/common/ClientOnly.d.ts +2 -11
  84. package/out/common/ClientOnly.d.ts.map +1 -1
  85. package/out/common/ClientOnly.js +1 -13
  86. package/out/common/ClientOnly.js.map +1 -1
  87. package/out/common/CopyButton.d.ts +3 -1
  88. package/out/common/CopyButton.d.ts.map +1 -1
  89. package/out/common/CopyButton.js.map +1 -1
  90. package/out/common/LoadingIndicator.d.ts +4 -2
  91. package/out/common/LoadingIndicator.d.ts.map +1 -1
  92. package/out/common/LoadingIndicator.js.map +1 -1
  93. package/out/common/PresetChips.d.ts +9 -8
  94. package/out/common/PresetChips.d.ts.map +1 -1
  95. package/out/common/PresetChips.js.map +1 -1
  96. package/out/common/confidence-tiers.d.ts +7 -6
  97. package/out/common/confidence-tiers.d.ts.map +1 -1
  98. package/out/common/confidence-tiers.js +7 -6
  99. package/out/common/confidence-tiers.js.map +1 -1
  100. package/out/common/cx.d.ts +3 -1
  101. package/out/common/cx.d.ts.map +1 -1
  102. package/out/common/cx.js +3 -1
  103. package/out/common/cx.js.map +1 -1
  104. package/out/common/text-tokens.d.ts +4 -3
  105. package/out/common/text-tokens.d.ts.map +1 -1
  106. package/out/common/text-tokens.js +6 -3
  107. package/out/common/text-tokens.js.map +1 -1
  108. package/out/common/useBuildInfo.d.ts +27 -0
  109. package/out/common/useBuildInfo.d.ts.map +1 -0
  110. package/out/common/useBuildInfo.js +32 -0
  111. package/out/common/useBuildInfo.js.map +1 -0
  112. package/out/common/useClipboard.d.ts +1 -3
  113. package/out/common/useClipboard.d.ts.map +1 -1
  114. package/out/common/useClipboard.js +2 -11
  115. package/out/common/useClipboard.js.map +1 -1
  116. package/out/common.d.ts +22 -0
  117. package/out/common.d.ts.map +1 -0
  118. package/out/common.js +15 -0
  119. package/out/common.js.map +1 -0
  120. package/out/index.d.ts +26 -71
  121. package/out/index.d.ts.map +1 -1
  122. package/out/index.js +21 -47
  123. package/out/index.js.map +1 -1
  124. package/out/map/AppIdentity.d.ts +42 -0
  125. package/out/map/AppIdentity.d.ts.map +1 -0
  126. package/out/map/AppIdentity.js +16 -0
  127. package/out/map/AppIdentity.js.map +1 -0
  128. package/out/map/BackendControl.d.ts +9 -10
  129. package/out/map/BackendControl.d.ts.map +1 -1
  130. package/out/map/BackendControl.js +2 -2
  131. package/out/map/BackendControl.js.map +1 -1
  132. package/out/map/CompareToggle.d.ts +8 -31
  133. package/out/map/CompareToggle.d.ts.map +1 -1
  134. package/out/map/CompareToggle.js +5 -2
  135. package/out/map/CompareToggle.js.map +1 -1
  136. package/out/map/DebugInfo.d.ts +34 -0
  137. package/out/map/DebugInfo.d.ts.map +1 -0
  138. package/out/map/DebugInfo.js +39 -0
  139. package/out/map/DebugInfo.js.map +1 -0
  140. package/out/map/Geocoder.d.ts +47 -26
  141. package/out/map/Geocoder.d.ts.map +1 -1
  142. package/out/map/Geocoder.js +34 -50
  143. package/out/map/Geocoder.js.map +1 -1
  144. package/out/map/GeocoderControls.d.ts +23 -33
  145. package/out/map/GeocoderControls.d.ts.map +1 -1
  146. package/out/map/GeocoderControls.js +277 -16
  147. package/out/map/GeocoderControls.js.map +1 -1
  148. package/out/map/GraticuleLayer.d.ts +28 -0
  149. package/out/map/GraticuleLayer.d.ts.map +1 -0
  150. package/out/map/GraticuleLayer.js +25 -0
  151. package/out/map/GraticuleLayer.js.map +1 -0
  152. package/out/map/MapCanvas.d.ts +23 -12
  153. package/out/map/MapCanvas.d.ts.map +1 -1
  154. package/out/map/MapCanvas.js +5 -2
  155. package/out/map/MapCanvas.js.map +1 -1
  156. package/out/map/MapChipRow.d.ts +43 -0
  157. package/out/map/MapChipRow.d.ts.map +1 -0
  158. package/out/map/MapChipRow.js +12 -0
  159. package/out/map/MapChipRow.js.map +1 -0
  160. package/out/map/MapCompass.d.ts +33 -0
  161. package/out/map/MapCompass.d.ts.map +1 -0
  162. package/out/map/MapCompass.js +15 -0
  163. package/out/map/MapCompass.js.map +1 -0
  164. package/out/map/MapControlStack.d.ts +56 -0
  165. package/out/map/MapControlStack.d.ts.map +1 -0
  166. package/out/map/MapControlStack.js +18 -0
  167. package/out/map/MapControlStack.js.map +1 -0
  168. package/out/map/MapFooter.d.ts +27 -0
  169. package/out/map/MapFooter.d.ts.map +1 -0
  170. package/out/map/MapFooter.js +37 -0
  171. package/out/map/MapFooter.js.map +1 -0
  172. package/out/map/MapProgressBar.d.ts +31 -0
  173. package/out/map/MapProgressBar.d.ts.map +1 -0
  174. package/out/map/MapProgressBar.js +8 -0
  175. package/out/map/MapProgressBar.js.map +1 -0
  176. package/out/map/MapSearchBar.d.ts +48 -0
  177. package/out/map/MapSearchBar.d.ts.map +1 -0
  178. package/out/map/MapSearchBar.js +12 -0
  179. package/out/map/MapSearchBar.js.map +1 -0
  180. package/out/map/MapSheet.d.ts +35 -0
  181. package/out/map/MapSheet.d.ts.map +1 -0
  182. package/out/map/MapSheet.js +29 -0
  183. package/out/map/MapSheet.js.map +1 -0
  184. package/out/map/OverlayLayers.d.ts +1 -1
  185. package/out/map/OverlayLayers.js +2 -2
  186. package/out/map/PlaceAutocomplete.d.ts +10 -21
  187. package/out/map/PlaceAutocomplete.d.ts.map +1 -1
  188. package/out/map/PlaceAutocomplete.js +2 -1
  189. package/out/map/PlaceAutocomplete.js.map +1 -1
  190. package/out/map/PlaceMarker.d.ts +4 -2
  191. package/out/map/PlaceMarker.d.ts.map +1 -1
  192. package/out/map/PlaceMarker.js +3 -1
  193. package/out/map/PlaceMarker.js.map +1 -1
  194. package/out/map/ResolvedPlaceLayers.d.ts +5 -14
  195. package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
  196. package/out/map/ResolvedPlaceLayers.js +2 -2
  197. package/out/map/ResolvedPlaceLayers.js.map +1 -1
  198. package/out/map/ResultCamera.d.ts +8 -31
  199. package/out/map/ResultCamera.d.ts.map +1 -1
  200. package/out/map/ResultCamera.js +3 -28
  201. package/out/map/ResultCamera.js.map +1 -1
  202. package/out/map/ResultOverlay.d.ts +2 -12
  203. package/out/map/ResultOverlay.d.ts.map +1 -1
  204. package/out/map/ResultOverlay.js +1 -2
  205. package/out/map/ResultOverlay.js.map +1 -1
  206. package/out/map/ResultPanel.d.ts +1 -20
  207. package/out/map/ResultPanel.d.ts.map +1 -1
  208. package/out/map/ResultPanel.js +1 -1
  209. package/out/map/ResultPanel.js.map +1 -1
  210. package/out/map/VersionPicker.d.ts +7 -19
  211. package/out/map/VersionPicker.d.ts.map +1 -1
  212. package/out/map/VersionPicker.js +5 -2
  213. package/out/map/VersionPicker.js.map +1 -1
  214. package/out/map/fake-runtime.d.ts +9 -11
  215. package/out/map/fake-runtime.d.ts.map +1 -1
  216. package/out/map/fake-runtime.js +9 -12
  217. package/out/map/fake-runtime.js.map +1 -1
  218. package/out/map/geometry.d.ts +13 -34
  219. package/out/map/geometry.d.ts.map +1 -1
  220. package/out/map/geometry.js +9 -34
  221. package/out/map/geometry.js.map +1 -1
  222. package/out/map/graticule.d.ts +17 -0
  223. package/out/map/graticule.d.ts.map +1 -0
  224. package/out/map/graticule.js +67 -0
  225. package/out/map/graticule.js.map +1 -0
  226. package/out/map/map-debug.d.ts +1 -1
  227. package/out/map/map-debug.d.ts.map +1 -1
  228. package/out/map/map-debug.js +1 -1
  229. package/out/map/map-debug.js.map +1 -1
  230. package/out/map/place-render.d.ts +23 -43
  231. package/out/map/place-render.d.ts.map +1 -1
  232. package/out/map/place-render.js +4 -39
  233. package/out/map/place-render.js.map +1 -1
  234. package/out/map/types.d.ts +67 -95
  235. package/out/map/types.d.ts.map +1 -1
  236. package/out/map/types.js +1 -10
  237. package/out/map/types.js.map +1 -1
  238. package/out/map/useCompareState.d.ts +5 -4
  239. package/out/map/useCompareState.d.ts.map +1 -1
  240. package/out/map/useCompareState.js +3 -3
  241. package/out/map/useCompareState.js.map +1 -1
  242. package/out/map/useGeocode.d.ts +5 -3
  243. package/out/map/useGeocode.d.ts.map +1 -1
  244. package/out/map/useGeocode.js +4 -4
  245. package/out/map/useGeocode.js.map +1 -1
  246. package/out/map/useMapBearing.d.ts +28 -0
  247. package/out/map/useMapBearing.d.ts.map +1 -0
  248. package/out/map/useMapBearing.js +30 -0
  249. package/out/map/useMapBearing.js.map +1 -0
  250. package/out/map/useMapLabelPick.d.ts +13 -0
  251. package/out/map/useMapLabelPick.d.ts.map +1 -0
  252. package/out/map/useMapLabelPick.js +95 -0
  253. package/out/map/useMapLabelPick.js.map +1 -0
  254. package/out/map/useMapPlaceRender.d.ts +2 -6
  255. package/out/map/useMapPlaceRender.d.ts.map +1 -1
  256. package/out/map/useMapPlaceRender.js +2 -6
  257. package/out/map/useMapPlaceRender.js.map +1 -1
  258. package/out/map/usePlaceAutocomplete.d.ts +30 -28
  259. package/out/map/usePlaceAutocomplete.d.ts.map +1 -1
  260. package/out/map/usePlaceAutocomplete.js +6 -33
  261. package/out/map/usePlaceAutocomplete.js.map +1 -1
  262. package/out/map.d.ts +69 -0
  263. package/out/map.d.ts.map +1 -0
  264. package/out/map.js +46 -0
  265. package/out/map.js.map +1 -0
  266. package/out/pipeline/CandidatePicker.d.ts +12 -2
  267. package/out/pipeline/CandidatePicker.d.ts.map +1 -1
  268. package/out/pipeline/CandidatePicker.js +2 -2
  269. package/out/pipeline/CandidatePicker.js.map +1 -1
  270. package/out/pipeline/ConfidenceCell.d.ts +1 -1
  271. package/out/pipeline/ConfidenceCell.js +1 -1
  272. package/out/pipeline/FailureDiagnostic.d.ts +8 -2
  273. package/out/pipeline/FailureDiagnostic.d.ts.map +1 -1
  274. package/out/pipeline/FailureDiagnostic.js +3 -0
  275. package/out/pipeline/FailureDiagnostic.js.map +1 -1
  276. package/out/pipeline/PipelineExplorer.js +1 -1
  277. package/out/pipeline/QueryForm.d.ts +5 -2
  278. package/out/pipeline/QueryForm.d.ts.map +1 -1
  279. package/out/pipeline/QueryForm.js +1 -1
  280. package/out/pipeline/QueryForm.js.map +1 -1
  281. package/out/pipeline/ResolvedPlace.js +1 -1
  282. package/out/pipeline/SpanHighlight.d.ts +4 -3
  283. package/out/pipeline/SpanHighlight.d.ts.map +1 -1
  284. package/out/pipeline/SpanHighlight.js +3 -2
  285. package/out/pipeline/SpanHighlight.js.map +1 -1
  286. package/out/pipeline/TimingPanel.d.ts +1 -1
  287. package/out/pipeline/TimingPanel.js +1 -1
  288. package/out/pipeline/TreeView.d.ts +1 -1
  289. package/out/pipeline/copy.d.ts +1 -1
  290. package/out/pipeline/copy.d.ts.map +1 -1
  291. package/out/pipeline/copy.js +4 -3
  292. package/out/pipeline/copy.js.map +1 -1
  293. package/out/pipeline/presets.d.ts +5 -4
  294. package/out/pipeline/presets.d.ts.map +1 -1
  295. package/out/pipeline/presets.js +15 -13
  296. package/out/pipeline/presets.js.map +1 -1
  297. package/out/pipeline/types.d.ts +21 -14
  298. package/out/pipeline/types.d.ts.map +1 -1
  299. package/out/pipeline/types.js +2 -4
  300. package/out/pipeline/types.js.map +1 -1
  301. package/out/pipeline/useParsePipeline.d.ts +15 -7
  302. package/out/pipeline/useParsePipeline.d.ts.map +1 -1
  303. package/out/pipeline/useParsePipeline.js +10 -7
  304. package/out/pipeline/useParsePipeline.js.map +1 -1
  305. package/out/pipeline.d.ts +33 -0
  306. package/out/pipeline.d.ts.map +1 -0
  307. package/out/pipeline.js +21 -0
  308. package/out/pipeline.js.map +1 -0
  309. package/out/poi/AbstainPanel.d.ts +1 -1
  310. package/out/poi/LiveResultsBlock.d.ts +2 -1
  311. package/out/poi/LiveResultsBlock.d.ts.map +1 -1
  312. package/out/poi/LiveResultsBlock.js +1 -1
  313. package/out/poi/LiveResultsBlock.js.map +1 -1
  314. package/out/poi/POIExplorer.d.ts +22 -12
  315. package/out/poi/POIExplorer.d.ts.map +1 -1
  316. package/out/poi/POIExplorer.js +10 -9
  317. package/out/poi/POIExplorer.js.map +1 -1
  318. package/out/poi/QueryInput.d.ts +1 -1
  319. package/out/poi/SubjectPanel.d.ts +2 -2
  320. package/out/poi/SubjectPanel.d.ts.map +1 -1
  321. package/out/poi/SubjectPanel.js +3 -2
  322. package/out/poi/SubjectPanel.js.map +1 -1
  323. package/out/poi/runtime.d.ts +5 -14
  324. package/out/poi/runtime.d.ts.map +1 -1
  325. package/out/poi/runtime.js +5 -16
  326. package/out/poi/runtime.js.map +1 -1
  327. package/out/poi/types.d.ts +39 -27
  328. package/out/poi/types.d.ts.map +1 -1
  329. package/out/poi/types.js +2 -4
  330. package/out/poi/types.js.map +1 -1
  331. package/out/poi/usePOISearch.d.ts +28 -17
  332. package/out/poi/usePOISearch.d.ts.map +1 -1
  333. package/out/poi/usePOISearch.js +6 -32
  334. package/out/poi/usePOISearch.js.map +1 -1
  335. package/out/poi.d.ts +23 -0
  336. package/out/poi.d.ts.map +1 -0
  337. package/out/poi.js +15 -0
  338. package/out/poi.js.map +1 -0
  339. package/out/runtime/useReleaseRuntime.d.ts +64 -65
  340. package/out/runtime/useReleaseRuntime.d.ts.map +1 -1
  341. package/out/runtime/useReleaseRuntime.js +31 -26
  342. package/out/runtime/useReleaseRuntime.js.map +1 -1
  343. package/out/runtime.d.ts +9 -0
  344. package/out/runtime.d.ts.map +1 -0
  345. package/out/runtime.js +8 -0
  346. package/out/runtime.js.map +1 -0
  347. package/out/search/SearchModal.d.ts +33 -0
  348. package/out/search/SearchModal.d.ts.map +1 -0
  349. package/out/search/SearchModal.js +187 -0
  350. package/out/search/SearchModal.js.map +1 -0
  351. package/out/search/types.d.ts +64 -0
  352. package/out/search/types.d.ts.map +1 -0
  353. package/out/search/types.js +13 -0
  354. package/out/search/types.js.map +1 -0
  355. package/out/search.d.ts +11 -0
  356. package/out/search.d.ts.map +1 -0
  357. package/out/search.js +9 -0
  358. package/out/search.js.map +1 -0
  359. package/package.json +132 -133
  360. package/styles.css +1340 -202
  361. package/tokens/index.css +26 -7
  362. package/tokens/tokens.json +121 -6
  363. package/tokens/tokens.resolver.json +1 -1
  364. package/lib/map/index.ts +0 -76
  365. package/out/map/index.d.ts +0 -52
  366. package/out/map/index.d.ts.map +0 -1
  367. package/out/map/index.js +0 -36
  368. package/out/map/index.js.map +0 -1
  369. package/styleframe.config.ts +0 -321
  370. package/vitest.node.config.ts +0 -20
package/styles.css CHANGED
@@ -1,7 +1,7 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
  * @mailwoman/react — component styles.
3
3
  *
4
- * Plain, `mw-`-prefixed classes. Every colour, family and radius here names a
4
+ * Plain, `mw-`-prefixed classes. Every color, family and radius here names a
5
5
  * SEMANTIC token from `tokens.css`, which this file requires a host to load —
6
6
  * `@mailwoman/react/tokens.css`, and `fonts.css` beside it for the brand face.
7
7
  * There are no inline fallbacks, on purpose: a fallback is what let eight
@@ -29,7 +29,42 @@
29
29
  */
30
30
  @import url("./tokens/index.css") layer(tokens);
31
31
 
32
+ /* The docs search modal keeps its rules beside its component. */
33
+ @import url("./lib/search/search.css") layer(components);
34
+
35
+ @layer reset {
36
+ /*
37
+ * Every box is measured by its border. The default is `content-box`, where `width`, `height` and `max-height`
38
+ * describe the content ALONE and any padding and border stand outside them — so a sheet capped at 52% of the
39
+ * viewport shipped 34px taller than its cap and ran off the bottom of the screen, and every rule that pairs a size
40
+ * with padding is one edit away from the same defect.
41
+ *
42
+ * A rule that wants the other measure states `box-sizing: content-box` for itself; the footer strip does, because
43
+ * its height names one line of type and its safe-area padding has to sit outside that.
44
+ *
45
+ * It is in the `reset` layer, so a host's own rules and every layer below win over it without out-specifying it.
46
+ */
47
+ *,
48
+ *::before,
49
+ *::after {
50
+ box-sizing: border-box;
51
+ }
52
+ }
53
+
32
54
  @layer base {
55
+ /*
56
+ * The theme attribute has to reach the USER AGENT, not only our own rules. `color-scheme` is what paints a
57
+ * scrollbar, a focus ring, a `<select>` popup and — the one this app depends on — the native clear button inside
58
+ * `input[type="search"]`, which renders as a dark cross on a dark field until the document declares its scheme.
59
+ */
60
+ :root[data-theme="dark"] {
61
+ color-scheme: dark;
62
+ }
63
+
64
+ :root[data-theme="light"] {
65
+ color-scheme: light;
66
+ }
67
+
33
68
  /*
34
69
  * Form controls do not inherit the document's font. Left alone, a `<button>` renders in the user agent's default
35
70
  * — measured as Arial on the deployed Earth app, beside a body in system-ui and labels in ui-monospace, which is
@@ -43,12 +78,33 @@
43
78
  font-size: inherit;
44
79
  line-height: inherit;
45
80
  }
81
+
82
+ /*
83
+ * A button carries no color of its own: unset, it paints the user agent's `buttontext`, which is near-black and
84
+ * stays near-black inside a dark sheet. Every button in this stylesheet that sets a background but no color read
85
+ * that way — the candidate list was the visible one.
86
+ */
87
+ button {
88
+ color: inherit;
89
+ }
90
+
91
+ /*
92
+ * A default face for a host that sets none. Wrapped in `:where()` so it carries zero specificity: Docusaurus's own
93
+ * `body` rule wins on the docs site and keeps that page's typography, while Storybook and a bare React host — which
94
+ * declare nothing — stop falling back to the user agent's serif.
95
+ */
96
+ :where(body) {
97
+ font-family: var(--font-family-text);
98
+ font-size: var(--font-size-body);
99
+ line-height: var(--line-height-body);
100
+ color: var(--color-text-primary);
101
+ }
46
102
  }
47
103
 
48
104
  @layer components {
49
105
  .mw-poi-explorer,
50
106
  .mw-pipeline-explorer {
51
- max-width: 720px;
107
+ max-width: 45rem;
52
108
  font-size: 0.95rem;
53
109
  }
54
110
 
@@ -57,7 +113,7 @@
57
113
  .mw-field {
58
114
  display: flex;
59
115
  flex-direction: column;
60
- gap: 0.5rem;
116
+ gap: var(--space-3);
61
117
  }
62
118
 
63
119
  .mw-field label {
@@ -66,10 +122,10 @@
66
122
  }
67
123
 
68
124
  .mw-field input {
69
- padding: 0.5rem 0.75rem;
125
+ padding: var(--space-3) var(--space-4);
70
126
  font: inherit;
71
127
  border: 1px solid var(--control-border);
72
- border-radius: 6px;
128
+ border-radius: var(--radius-control);
73
129
  background: var(--color-background-raised);
74
130
  color: var(--color-text-primary);
75
131
  }
@@ -79,16 +135,16 @@
79
135
  }
80
136
 
81
137
  .mw-field button[type="submit"] {
82
- padding: 0.5rem 0.75rem;
138
+ padding: var(--space-3) var(--space-4);
83
139
  background: var(--color-accent);
84
140
  color: var(--color-accent-contrast);
85
141
  border: 0;
86
- border-radius: 6px;
142
+ border-radius: var(--radius-control);
87
143
  font-weight: 600;
88
144
  cursor: pointer;
89
145
  display: inline-flex;
90
146
  align-items: center;
91
- gap: 0.4rem;
147
+ gap: var(--space-2);
92
148
  align-self: flex-start;
93
149
  }
94
150
 
@@ -101,27 +157,48 @@
101
157
 
102
158
  /* #region Preset chips */
103
159
 
160
+ /*
161
+ * One line that scrolls, never a wrap. Twelve examples wrapped down five ragged rows (3/3/2/1/1/1) and took a
162
+ * third of the panel; a scrolling row costs a fixed height whatever the number of examples. The scrollbar is
163
+ * hidden because the row sits inside a floating panel where a track reads as chrome, and every chip stays
164
+ * reachable by keyboard.
165
+ */
104
166
  .mw-presets {
105
- margin-top: 0.75rem;
167
+ margin-top: var(--space-4);
106
168
  display: flex;
107
- flex-wrap: wrap;
108
- gap: 0.4rem;
169
+ flex-wrap: nowrap;
170
+ overflow-x: auto;
171
+ scrollbar-width: none;
172
+ gap: var(--space-2);
109
173
  align-items: baseline;
174
+ padding-bottom: var(--space-0);
175
+ }
176
+
177
+ .mw-presets::-webkit-scrollbar {
178
+ display: none;
179
+ }
180
+
181
+ .mw-presets > * {
182
+ flex: 0 0 auto;
183
+ }
184
+
185
+ .mw-chip {
186
+ white-space: nowrap;
110
187
  }
111
188
 
112
189
  .mw-presets__label {
113
190
  font-size: 0.85rem;
114
191
  color: var(--color-text-secondary);
115
- margin-right: 0.25rem;
192
+ margin-right: var(--space-1);
116
193
  }
117
194
 
118
195
  .mw-chip {
119
196
  font-size: 0.8rem;
120
- padding: 0.25rem 0.55rem;
197
+ padding: var(--space-1) var(--space-3);
121
198
  background: var(--control-background);
122
199
  color: var(--color-text-primary);
123
200
  border: 1px solid var(--control-border);
124
- border-radius: 4px;
201
+ border-radius: var(--radius-tight);
125
202
  font-weight: 500;
126
203
  cursor: pointer;
127
204
  font-family: inherit;
@@ -143,11 +220,11 @@
143
220
 
144
221
  .mw-btn {
145
222
  font-size: 0.8rem;
146
- padding: 0.25rem 0.55rem;
223
+ padding: var(--space-1) var(--space-3);
147
224
  background: var(--control-background);
148
225
  color: var(--color-text-primary);
149
226
  border: 1px solid var(--control-border);
150
- border-radius: 4px;
227
+ border-radius: var(--radius-tight);
151
228
  cursor: pointer;
152
229
  font-family: inherit;
153
230
  }
@@ -166,14 +243,14 @@
166
243
  /* #region Result panel + errors */
167
244
 
168
245
  .mw-result {
169
- margin-top: 1.5rem;
246
+ margin-top: var(--space-6);
170
247
  }
171
248
 
172
249
  .mw-result__header {
173
250
  display: flex;
174
251
  justify-content: space-between;
175
252
  align-items: center;
176
- margin-bottom: 0.75rem;
253
+ margin-bottom: var(--space-4);
177
254
  }
178
255
 
179
256
  .mw-result__header h2 {
@@ -185,7 +262,7 @@
185
262
  display: flex;
186
263
  justify-content: space-between;
187
264
  align-items: center;
188
- margin-bottom: 0.4rem;
265
+ margin-bottom: var(--space-2);
189
266
  }
190
267
 
191
268
  .mw-panel__header h3 {
@@ -209,7 +286,7 @@
209
286
  /* #region KindBadge */
210
287
 
211
288
  .mw-kind {
212
- margin-bottom: 0.5rem;
289
+ margin-bottom: var(--space-3);
213
290
  font-size: 0.9rem;
214
291
  }
215
292
 
@@ -223,7 +300,7 @@
223
300
  }
224
301
 
225
302
  .mw-kind__alternatives {
226
- margin: 0.25rem 0 0 1rem;
303
+ margin: var(--space-1) 0 0 var(--space-5);
227
304
  padding: 0;
228
305
  list-style: disc;
229
306
  }
@@ -235,14 +312,14 @@
235
312
  .mw-subject__row {
236
313
  display: flex;
237
314
  align-items: center;
238
- gap: 0.5rem;
239
- margin-bottom: 0.5rem;
315
+ gap: var(--space-3);
316
+ margin-bottom: var(--space-3);
240
317
  }
241
318
 
242
319
  .mw-subject__chip {
243
320
  display: inline-block;
244
- padding: 0.2rem 0.6rem;
245
- border-radius: 999px;
321
+ padding: var(--space-1) var(--space-3);
322
+ border-radius: var(--radius-pill);
246
323
  background: var(--color-accent-background);
247
324
  color: var(--color-accent);
248
325
  font-weight: 600;
@@ -251,8 +328,8 @@
251
328
 
252
329
  .mw-subject__badge {
253
330
  display: inline-block;
254
- padding: 0.2rem 0.5rem;
255
- border-radius: 999px;
331
+ padding: var(--space-1) var(--space-3);
332
+ border-radius: var(--radius-pill);
256
333
  background: var(--color-state-warning-background);
257
334
  color: var(--color-state-warning);
258
335
  font-size: 0.75rem;
@@ -268,8 +345,8 @@
268
345
 
269
346
  .mw-subject__qid {
270
347
  display: inline-block;
271
- padding: 0.2rem 0.5rem;
272
- border-radius: 999px;
348
+ padding: var(--space-1) var(--space-3);
349
+ border-radius: var(--radius-pill);
273
350
  background: var(--color-background-sunken);
274
351
  color: var(--color-text-primary);
275
352
  font-family: var(--font-family-code);
@@ -283,7 +360,7 @@
283
360
  }
284
361
 
285
362
  .mw-subject__note {
286
- margin: 0 0 0.75rem;
363
+ margin: 0 0 var(--space-4);
287
364
  font-size: 0.85rem;
288
365
  opacity: 0.8;
289
366
  }
@@ -291,8 +368,8 @@
291
368
  .mw-subject__detail {
292
369
  display: grid;
293
370
  grid-template-columns: auto 1fr;
294
- gap: 0.25rem 0.75rem;
295
- margin: 0 0 0.75rem;
371
+ gap: var(--space-1) var(--space-4);
372
+ margin: 0 0 var(--space-4);
296
373
  font-size: 0.9rem;
297
374
  }
298
375
 
@@ -310,13 +387,13 @@
310
387
  /* #region OverpassQL block */
311
388
 
312
389
  .mw-overpass {
313
- margin-top: 0.75rem;
390
+ margin-top: var(--space-4);
314
391
  }
315
392
 
316
393
  .mw-overpass__code {
317
394
  margin: 0;
318
- padding: 0.75rem;
319
- border-radius: 6px;
395
+ padding: var(--space-4);
396
+ border-radius: var(--radius-control);
320
397
  background: var(--control-background);
321
398
  font-family: var(--font-family-code);
322
399
  font-size: 0.85rem;
@@ -329,11 +406,11 @@
329
406
  /* #region Live results */
330
407
 
331
408
  .mw-live {
332
- margin-top: 1rem;
409
+ margin-top: var(--space-5);
333
410
  }
334
411
 
335
412
  .mw-live__caption {
336
- margin: 0 0 0.4rem;
413
+ margin: 0 0 var(--space-2);
337
414
  font-size: 0.85rem;
338
415
  color: var(--color-text-secondary);
339
416
  }
@@ -344,16 +421,16 @@
344
421
  padding: 0;
345
422
  display: flex;
346
423
  flex-direction: column;
347
- gap: 0.35rem;
424
+ gap: var(--space-2);
348
425
  }
349
426
 
350
427
  .mw-live__results li {
351
428
  display: flex;
352
429
  justify-content: space-between;
353
430
  align-items: baseline;
354
- gap: 0.75rem;
355
- padding: 0.4rem 0.6rem;
356
- border-radius: 6px;
431
+ gap: var(--space-4);
432
+ padding: var(--space-2) var(--space-3);
433
+ border-radius: var(--radius-control);
357
434
  background: var(--control-background);
358
435
  font-size: 0.9rem;
359
436
  }
@@ -375,13 +452,13 @@
375
452
  .mw-components {
376
453
  width: 100%;
377
454
  border-collapse: collapse;
378
- margin: 0.75rem 0;
455
+ margin: var(--space-4) 0;
379
456
  font-size: 0.9rem;
380
457
  }
381
458
 
382
459
  .mw-components th,
383
460
  .mw-components td {
384
- padding: 0.4rem 0.6rem;
461
+ padding: var(--space-2) var(--space-3);
385
462
  text-align: left;
386
463
  border-bottom: 1px solid var(--color-separator);
387
464
  }
@@ -404,27 +481,27 @@
404
481
  .mw-conf {
405
482
  display: flex;
406
483
  align-items: center;
407
- gap: 0.35rem;
408
- min-width: 80px;
484
+ gap: var(--space-2);
485
+ min-width: 5rem;
409
486
  }
410
487
 
411
488
  .mw-conf__bar {
412
- height: 6px;
413
- border-radius: 3px;
414
- min-width: 4px;
489
+ height: 0.375rem;
490
+ border-radius: var(--radius-pill);
491
+ min-width: 0.25rem;
415
492
  transition: width 0.2s ease;
416
493
  }
417
494
 
418
495
  .mw-conf__bar--high {
419
- background: #22c55e;
496
+ background: var(--color-confidence-high);
420
497
  }
421
498
 
422
499
  .mw-conf__bar--mid {
423
- background: #f59e0b;
500
+ background: var(--color-confidence-mid);
424
501
  }
425
502
 
426
503
  .mw-conf__bar--low {
427
- background: #ef4444;
504
+ background: var(--color-confidence-low);
428
505
  }
429
506
 
430
507
  .mw-conf__value {
@@ -437,22 +514,22 @@
437
514
  /* #region Resolved place */
438
515
 
439
516
  .mw-resolved {
440
- margin-top: 1rem;
441
- padding: 0.75rem;
517
+ margin-top: var(--space-5);
518
+ padding: var(--space-4);
442
519
  border: 1px solid var(--color-separator);
443
- border-radius: 6px;
520
+ border-radius: var(--radius-control);
444
521
  background: var(--color-background-canvas);
445
522
  }
446
523
 
447
524
  .mw-resolved h2 {
448
- margin: 0 0 0.5rem;
525
+ margin: 0 0 var(--space-3);
449
526
  font-size: 1rem;
450
527
  }
451
528
 
452
529
  .mw-resolved dl {
453
530
  display: grid;
454
531
  grid-template-columns: auto 1fr;
455
- gap: 0.25rem 0.75rem;
532
+ gap: var(--space-1) var(--space-4);
456
533
  margin: 0;
457
534
  }
458
535
 
@@ -466,9 +543,9 @@
466
543
  }
467
544
 
468
545
  .mw-resolved__dual {
469
- margin: 0.5rem 0 0;
470
- padding: 0.4rem 0.6rem;
471
- border-radius: 6px;
546
+ margin: var(--space-3) 0 0;
547
+ padding: var(--space-2) var(--space-3);
548
+ border-radius: var(--radius-control);
472
549
  background: var(--color-state-info-background);
473
550
  font-size: 0.9rem;
474
551
  }
@@ -478,12 +555,12 @@
478
555
  /* #region Candidate picker */
479
556
 
480
557
  .mw-candidates {
481
- margin-top: 1rem;
558
+ margin-top: var(--space-5);
482
559
  }
483
560
 
484
561
  .mw-candidates h2 {
485
562
  font-size: 1rem;
486
- margin: 0 0 0.5rem;
563
+ margin: 0 0 var(--space-3);
487
564
  }
488
565
 
489
566
  .mw-candidates__list {
@@ -492,32 +569,44 @@
492
569
  padding: 0;
493
570
  display: flex;
494
571
  flex-direction: column;
495
- gap: 0.35rem;
572
+ gap: var(--space-2);
496
573
  }
497
574
 
498
575
  .mw-candidates__btn {
499
576
  display: flex;
500
577
  align-items: baseline;
501
- gap: 0.6rem;
578
+ gap: var(--space-3);
502
579
  width: 100%;
503
580
  text-align: left;
504
- padding: 0.4rem 0.6rem;
581
+ padding: var(--space-2) var(--space-3);
505
582
  border: 1px solid var(--color-separator);
506
- border-radius: 6px;
507
- background: transparent;
583
+ border-radius: var(--radius-control);
584
+ background: var(--control-background);
585
+ color: var(--color-text-primary);
508
586
  cursor: pointer;
509
587
  font: inherit;
510
588
  }
511
589
 
590
+ .mw-candidates__btn:hover:not(:disabled) {
591
+ background: var(--control-background-pressed);
592
+ border-color: var(--control-border);
593
+ }
594
+
595
+ .mw-candidates__btn:focus-visible {
596
+ outline: 2px solid var(--color-selection);
597
+ outline-offset: 1px;
598
+ }
599
+
512
600
  .mw-candidates__btn--active {
513
601
  border-color: var(--color-accent);
514
602
  background: var(--color-accent-background);
515
603
  }
516
604
 
605
+ /* Tertiary, not translucent: an opacity here also fades the text under it toward the pane behind the sheet. */
517
606
  .mw-candidates__rank {
518
607
  font-family: var(--font-family-code);
519
- font-size: 0.8rem;
520
- opacity: 0.6;
608
+ font-size: var(--font-size-caption);
609
+ color: var(--color-text-tertiary);
521
610
  }
522
611
 
523
612
  .mw-candidates__name {
@@ -539,8 +628,8 @@
539
628
  flex-direction: column;
540
629
  align-items: center;
541
630
  justify-content: center;
542
- gap: 0.75rem;
543
- padding: 1.5rem;
631
+ gap: var(--space-4);
632
+ padding: var(--space-6);
544
633
  }
545
634
 
546
635
  .mw-loading__label {
@@ -553,14 +642,14 @@
553
642
  .mw-pulse {
554
643
  display: flex;
555
644
  flex-direction: column;
556
- gap: 0.5rem;
645
+ gap: var(--space-3);
557
646
  width: 100%;
558
- max-width: 320px;
647
+ max-width: 20rem;
559
648
  }
560
649
 
561
650
  .mw-pulse__bar {
562
651
  height: 0.75rem;
563
- border-radius: 4px;
652
+ border-radius: var(--radius-pill);
564
653
  background: var(--color-background-sunken);
565
654
  animation: mw-pulse-fade 1.4s ease-in-out infinite;
566
655
  }
@@ -627,9 +716,9 @@
627
716
  .mw-staged {
628
717
  display: flex;
629
718
  flex-direction: column;
630
- gap: 0.5rem;
719
+ gap: var(--space-3);
631
720
  width: 100%;
632
- max-width: 360px;
721
+ max-width: 22.5rem;
633
722
  list-style: none;
634
723
  margin: 0;
635
724
  padding: 0;
@@ -638,9 +727,9 @@
638
727
  .mw-staged__step {
639
728
  display: flex;
640
729
  align-items: center;
641
- gap: 0.6rem;
642
- padding: 0.4rem 0.6rem;
643
- border-radius: 4px;
730
+ gap: var(--space-3);
731
+ padding: var(--space-2) var(--space-3);
732
+ border-radius: var(--radius-tight);
644
733
  font-size: 0.85rem;
645
734
  transition:
646
735
  background 0.3s,
@@ -715,12 +804,12 @@
715
804
  /*
716
805
  * The <MapCanvas> wrapper. Sizing is the host's call (pass `style`/`className`), but a min-height keeps
717
806
  * the map from collapsing to zero when a consumer forgets. The inner <Map> fills 100% of this box.
718
- * The host must ALSO import `maplibre-gl/dist/maplibre-gl.css` for the map's own control/canvas styles.
807
+ * The host must also import `maplibre-gl/dist/maplibre-gl.css` for the map's own control/canvas styles.
719
808
  */
720
809
  .mw-demo-map {
721
810
  position: relative;
722
811
  width: 100%;
723
- min-height: 320px;
812
+ min-height: 20rem;
724
813
  overflow: hidden;
725
814
  }
726
815
 
@@ -733,7 +822,7 @@
733
822
  position: relative;
734
823
  width: 100%;
735
824
  height: 100%;
736
- min-height: 480px;
825
+ min-height: 30rem;
737
826
  }
738
827
 
739
828
  .mw-geocoder-demo__map {
@@ -741,52 +830,35 @@
741
830
  inset: 0;
742
831
  }
743
832
 
744
- .mw-demo-controls {
745
- position: absolute;
746
- top: 1rem;
747
- left: 1rem;
748
- z-index: 2;
749
- width: min(360px, calc(100% - 2rem));
750
- max-height: calc(100% - 2rem);
751
- overflow-y: auto;
752
- padding: 1rem;
753
- border-radius: 10px;
754
- background: color-mix(in srgb, var(--color-background-raised) 92%, transparent);
755
- box-shadow: 0 2px 16px rgba(0, 0, 0, 0.18);
756
- font-size: 0.95rem;
757
- }
758
-
759
- @media (max-width: 600px) {
760
- .mw-demo-controls {
761
- top: auto;
762
- bottom: 1rem;
763
- left: 1rem;
764
- right: 1rem;
765
- width: auto;
766
- }
767
- }
768
-
769
833
  /* #endregion */
770
834
 
771
835
  /* #region Demo control field (version / compare-version selects) */
772
836
 
773
837
  .mw-demo-control {
774
- margin-bottom: 0.75rem;
838
+ display: grid;
839
+ gap: var(--space-1);
840
+ width: 100%;
775
841
  }
776
842
 
777
843
  .mw-demo-control__label {
778
844
  display: block;
779
- margin-bottom: 0.25rem;
780
- font-weight: 600;
781
- font-size: 0.9rem;
845
+ font-weight: var(--font-weight-medium);
846
+ font-size: var(--font-size-footnote);
847
+ color: var(--color-text-secondary);
782
848
  }
783
849
 
784
850
  .mw-demo-control__select {
785
851
  width: 100%;
786
- padding: 0.4rem;
852
+ /*
853
+ * The release labels are long ("v9.1.0 — the suffix-boundary cure (model v4.4.0)") and a native select clips
854
+ * the selected option at the field's edge with no ellipsis, so the string ended mid-word and its closing
855
+ * parenthesis never rendered. The full text rides the element's `title` (see VersionPicker).
856
+ */
857
+ text-overflow: ellipsis;
858
+ padding: var(--space-2);
787
859
  font: inherit;
788
860
  border: 1px solid var(--control-border);
789
- border-radius: 6px;
861
+ border-radius: var(--radius-control);
790
862
  background: var(--color-background-raised);
791
863
  color: var(--color-text-primary);
792
864
  }
@@ -799,59 +871,76 @@
799
871
 
800
872
  /* #region Backend indicator + WASM toggle / compare toggle */
801
873
 
874
+ /* Stacked, because both controls now sit in a sheet row where a label sits over its control, not beside it. */
802
875
  .mw-demo-backend,
803
876
  .mw-demo-compare {
804
- display: flex;
805
- align-items: center;
806
- gap: 0.75rem;
807
- flex-wrap: wrap;
808
- margin-bottom: 0.75rem;
809
- font-size: 0.85rem;
810
- }
811
-
812
- .mw-demo-compare {
813
- display: block;
877
+ display: grid;
878
+ gap: var(--space-2);
879
+ justify-items: start;
880
+ font-size: var(--font-size-footnote);
814
881
  }
815
882
 
816
883
  .mw-demo-backend__active {
817
- opacity: 0.7;
884
+ color: var(--color-text-tertiary);
885
+ font-size: var(--font-size-caption);
818
886
  }
819
887
 
820
888
  .mw-demo-backend__toggle,
821
889
  .mw-demo-compare__toggle {
822
890
  display: inline-flex;
823
891
  align-items: center;
824
- gap: 0.25rem;
892
+ gap: var(--space-3);
825
893
  cursor: pointer;
826
- opacity: 0.8;
827
- }
828
-
829
- .mw-demo-compare__toggle {
830
- margin-bottom: 0.5rem;
831
894
  }
832
895
 
833
896
  .mw-demo-compare__status {
834
- margin: 0.25rem 0 0;
897
+ margin: var(--space-1) 0 0;
835
898
  font-size: 0.8rem;
836
899
  color: var(--color-text-secondary);
837
900
  }
838
901
 
902
+ /* The debug record: label/value pairs a reporter reads off, sized to the sheet rather than the page. The value
903
+ column wraps because a user-agent string is long and truncating the one field that identifies the browser
904
+ would defeat the row. */
905
+ .mw-debug-info {
906
+ display: grid;
907
+ gap: var(--space-1);
908
+ width: 100%;
909
+ font-size: var(--font-size-caption);
910
+ }
911
+
912
+ .mw-debug-info__row {
913
+ display: grid;
914
+ grid-template-columns: minmax(6rem, auto) 1fr;
915
+ gap: var(--space-3);
916
+ align-items: baseline;
917
+ }
918
+
919
+ .mw-debug-info__label {
920
+ color: var(--color-text-tertiary);
921
+ }
922
+
923
+ .mw-debug-info__value {
924
+ overflow-wrap: anywhere;
925
+ color: var(--color-text-secondary);
926
+ }
927
+
839
928
  /* #endregion */
840
929
 
841
930
  /* #region Place autocomplete listbox (reuses .mw-chip) */
842
931
 
843
932
  .mw-demo-suggest {
844
- margin-top: 0.75rem;
933
+ margin-top: var(--space-4);
845
934
  display: flex;
846
935
  flex-wrap: wrap;
847
- gap: 0.4rem;
936
+ gap: var(--space-2);
848
937
  align-items: baseline;
849
938
  }
850
939
 
851
940
  .mw-demo-suggest__label {
852
941
  font-size: 0.85rem;
853
942
  color: var(--color-text-secondary);
854
- margin-right: 0.25rem;
943
+ margin-right: var(--space-1);
855
944
  }
856
945
 
857
946
  .mw-chip--active {
@@ -864,36 +953,36 @@
864
953
  /* #region Span ribbon (SpanHighlight) — each tagged span is a column: the text on top, tinted by tier, the tag beneath */
865
954
 
866
955
  .mw-spans {
867
- margin: 0.5rem 0 1rem 0;
956
+ margin: var(--space-3) 0 var(--space-5) 0;
868
957
  }
869
958
 
870
959
  .mw-spans__legend {
871
960
  display: flex;
872
961
  align-items: center;
873
- gap: 0.35rem;
962
+ gap: var(--space-2);
874
963
  font-size: 0.7rem;
875
964
  text-transform: uppercase;
876
965
  letter-spacing: 0.04em;
877
966
  color: var(--color-text-secondary);
878
- margin-bottom: 0.5rem;
967
+ margin-bottom: var(--space-3);
879
968
  }
880
969
 
881
970
  .mw-spans__swatch {
882
971
  display: inline-block;
883
972
  width: 0.8rem;
884
973
  height: 0.8rem;
885
- border-radius: 2px;
886
- margin-left: 0.4rem;
974
+ border-radius: var(--radius-tick);
975
+ margin-left: var(--space-2);
887
976
  }
888
977
 
889
978
  .mw-spans__track {
890
979
  display: flex;
891
980
  flex-wrap: wrap;
892
981
  align-items: flex-end;
893
- row-gap: 1.5rem;
894
- padding: 0.5rem 0.6rem;
982
+ row-gap: var(--space-6);
983
+ padding: var(--space-3) var(--space-3);
895
984
  border: 1px solid var(--color-separator);
896
- border-radius: 6px;
985
+ border-radius: var(--radius-control);
897
986
  background: var(--color-background-raised);
898
987
  font-family: var(--font-family-code);
899
988
  font-size: 0.9rem;
@@ -907,13 +996,20 @@
907
996
  }
908
997
 
909
998
  .mw-spans__text {
910
- padding: 0.05rem 0.1rem;
999
+ padding: var(--space-0) var(--space-0);
911
1000
  border-bottom: 2px solid transparent;
912
1001
  white-space: pre;
913
1002
  }
914
1003
 
1004
+ /*
1005
+ * Each segment is a column sized to max(text, tag), so a long tag under a short span widens the segment — which is
1006
+ * why `350 5th Ave` renders spread out. What it did not have was any space BETWEEN neighboring tags, so the row
1007
+ * read as one run-on word: `house_numberstreetstreet_suffix locality regionpostcode`. The padding separates the
1008
+ * tags and, because the segment sizes to the tag, spreads the text above them to match.
1009
+ */
915
1010
  .mw-spans__tag {
916
- margin-top: 0.15rem;
1011
+ margin-top: var(--space-0);
1012
+ padding-inline: var(--space-1);
917
1013
  font-size: 0.6rem;
918
1014
  line-height: 1;
919
1015
  text-align: center;
@@ -929,40 +1025,44 @@
929
1025
  color: var(--color-text-tertiary);
930
1026
  }
931
1027
 
932
- /* The tier tints match .mw-conf__bar--high/--mid/--low. */
1028
+ /*
1029
+ * The tier tints match `.mw-conf__bar--high/--mid/--low` — which the sentence that stood here also claimed, while
1030
+ * the two painted different greens. Both now read the confidence tokens, so the claim is carried by the values
1031
+ * rather than by the comment.
1032
+ */
933
1033
  .mw-spans__seg--high .mw-spans__text {
934
- background: rgba(26, 168, 77, 0.18);
935
- border-bottom-color: #1aa84d;
1034
+ background: var(--color-confidence-high-tint);
1035
+ border-bottom-color: var(--color-confidence-high);
936
1036
  }
937
1037
 
938
1038
  .mw-spans__seg--mid .mw-spans__text {
939
- background: rgba(230, 168, 0, 0.18);
940
- border-bottom-color: #e6a800;
1039
+ background: var(--color-confidence-mid-tint);
1040
+ border-bottom-color: var(--color-confidence-mid);
941
1041
  }
942
1042
 
943
1043
  .mw-spans__seg--low .mw-spans__text {
944
- background: rgba(216, 80, 74, 0.18);
945
- border-bottom-color: #d8504a;
1044
+ background: var(--color-confidence-low-tint);
1045
+ border-bottom-color: var(--color-confidence-low);
946
1046
  }
947
1047
 
948
1048
  .mw-spans__swatch--high {
949
- background: #1aa84d;
1049
+ background: var(--color-confidence-high);
950
1050
  }
951
1051
 
952
1052
  .mw-spans__swatch--mid {
953
- background: #e6a800;
1053
+ background: var(--color-confidence-mid);
954
1054
  }
955
1055
 
956
1056
  .mw-spans__swatch--low {
957
- background: #d8504a;
1057
+ background: var(--color-confidence-low);
958
1058
  }
959
1059
 
960
1060
  /* #endregion */
961
1061
 
962
- /* #region Stage timing (TimingPanel) — a stacked bar by wall-clock, stage hues, never the confidence colours */
1062
+ /* #region Stage timing (TimingPanel) — a stacked bar by wall-clock, stage hues, never the confidence colors */
963
1063
 
964
1064
  .mw-timing {
965
- margin: 1rem 0;
1065
+ margin: var(--space-5) 0;
966
1066
  font-size: 0.8rem;
967
1067
  }
968
1068
 
@@ -973,7 +1073,7 @@
973
1073
  text-transform: uppercase;
974
1074
  letter-spacing: 0.05em;
975
1075
  color: var(--color-text-secondary);
976
- margin-bottom: 0.35rem;
1076
+ margin-bottom: var(--space-2);
977
1077
  }
978
1078
 
979
1079
  .mw-timing__total {
@@ -987,7 +1087,7 @@
987
1087
  display: flex;
988
1088
  width: 100%;
989
1089
  height: 0.7rem;
990
- border-radius: 4px;
1090
+ border-radius: var(--radius-tight);
991
1091
  overflow: hidden;
992
1092
  background: var(--color-background-sunken);
993
1093
  }
@@ -999,15 +1099,15 @@
999
1099
  .mw-timing__legend {
1000
1100
  display: flex;
1001
1101
  flex-wrap: wrap;
1002
- gap: 0.25rem 1rem;
1003
- margin-top: 0.4rem;
1102
+ gap: var(--space-1) var(--space-5);
1103
+ margin-top: var(--space-2);
1004
1104
  color: var(--color-text-secondary);
1005
1105
  }
1006
1106
 
1007
1107
  .mw-timing__item {
1008
1108
  display: inline-flex;
1009
1109
  align-items: center;
1010
- gap: 0.35rem;
1110
+ gap: var(--space-2);
1011
1111
  }
1012
1112
 
1013
1113
  .mw-timing__ms {
@@ -1019,19 +1119,19 @@
1019
1119
  display: inline-block;
1020
1120
  width: 0.7rem;
1021
1121
  height: 0.7rem;
1022
- border-radius: 2px;
1122
+ border-radius: var(--radius-tick);
1023
1123
  }
1024
1124
 
1025
1125
  .mw-timing__seg--shape {
1026
- background: #3578e5;
1126
+ background: var(--color-stage-shape);
1027
1127
  }
1028
1128
 
1029
1129
  .mw-timing__seg--classify {
1030
- background: #8b5cf6;
1130
+ background: var(--color-stage-classify);
1031
1131
  }
1032
1132
 
1033
1133
  .mw-timing__seg--resolve {
1034
- background: #14b8a6;
1134
+ background: var(--color-stage-resolve);
1035
1135
  }
1036
1136
 
1037
1137
  /* #endregion */
@@ -1039,36 +1139,36 @@
1039
1139
  /* #region Containment tree (TreeView) — nested lists with a guide rail per level */
1040
1140
 
1041
1141
  .mw-tree {
1042
- margin: 0.5rem 0 0 0;
1142
+ margin: var(--space-3) 0 0 0;
1043
1143
  }
1044
1144
 
1045
1145
  .mw-tree__children {
1046
1146
  list-style: none;
1047
1147
  margin: 0;
1048
- padding-left: 1.1rem;
1148
+ padding-left: var(--space-5);
1049
1149
  }
1050
1150
 
1051
1151
  /* Nested levels get a guide rail; the outermost list sits flush. */
1052
1152
  .mw-tree__children .mw-tree__children {
1053
1153
  border-left: 1px solid var(--control-border);
1054
- margin-left: 0.3rem;
1154
+ margin-left: var(--space-1);
1055
1155
  }
1056
1156
 
1057
1157
  .mw-tree__node {
1058
- margin: 0.15rem 0;
1158
+ margin: var(--space-0) 0;
1059
1159
  }
1060
1160
 
1061
1161
  .mw-tree__row {
1062
1162
  display: inline-flex;
1063
1163
  align-items: baseline;
1064
- gap: 0.5rem;
1164
+ gap: var(--space-3);
1065
1165
  font-size: 0.85rem;
1066
1166
  }
1067
1167
 
1068
1168
  .mw-tree__tag {
1069
1169
  font-weight: 600;
1070
- padding: 0 0.3rem;
1071
- border-radius: 3px;
1170
+ padding: 0 var(--space-1);
1171
+ border-radius: var(--radius-tick);
1072
1172
  border-bottom: 2px solid transparent;
1073
1173
  }
1074
1174
 
@@ -1083,18 +1183,18 @@
1083
1183
  }
1084
1184
 
1085
1185
  .mw-tree__tag--high {
1086
- background: rgba(26, 168, 77, 0.18);
1087
- border-bottom-color: #1aa84d;
1186
+ background: var(--color-confidence-high-tint);
1187
+ border-bottom-color: var(--color-confidence-high);
1088
1188
  }
1089
1189
 
1090
1190
  .mw-tree__tag--mid {
1091
- background: rgba(230, 168, 0, 0.18);
1092
- border-bottom-color: #e6a800;
1191
+ background: var(--color-confidence-mid-tint);
1192
+ border-bottom-color: var(--color-confidence-mid);
1093
1193
  }
1094
1194
 
1095
1195
  .mw-tree__tag--low {
1096
- background: rgba(216, 80, 74, 0.18);
1097
- border-bottom-color: #d8504a;
1196
+ background: var(--color-confidence-low-tint);
1197
+ border-bottom-color: var(--color-confidence-low);
1098
1198
  }
1099
1199
 
1100
1200
  /* #endregion */
@@ -1102,16 +1202,16 @@
1102
1202
  /* #region No-hit diagnostic (FailureDiagnostic) — a structured panel, tinted informational rather than red */
1103
1203
 
1104
1204
  .mw-failure {
1105
- margin-top: 1rem;
1106
- padding: 0.75rem 1rem;
1205
+ margin-top: var(--space-5);
1206
+ padding: var(--space-4) var(--space-5);
1107
1207
  background: var(--color-state-warning-background);
1108
1208
  border-left: 3px solid var(--color-state-warning);
1109
- border-radius: 4px;
1209
+ border-radius: var(--radius-tight);
1110
1210
  }
1111
1211
 
1112
1212
  .mw-failure h2 {
1113
1213
  font-size: 1rem;
1114
- margin: 0 0 0.5rem 0;
1214
+ margin: 0 0 var(--space-3) 0;
1115
1215
  text-transform: uppercase;
1116
1216
  letter-spacing: 0.05em;
1117
1217
  color: var(--color-state-warning);
@@ -1119,13 +1219,13 @@
1119
1219
 
1120
1220
  .mw-failure ul {
1121
1221
  margin: 0;
1122
- padding-left: 1.25rem;
1222
+ padding-left: var(--space-5);
1123
1223
  font-size: 0.875rem;
1124
1224
  color: var(--color-text-primary);
1125
1225
  }
1126
1226
 
1127
1227
  .mw-failure li + li {
1128
- margin-top: 0.35rem;
1228
+ margin-top: var(--space-2);
1129
1229
  }
1130
1230
 
1131
1231
  /* #endregion */
@@ -1133,9 +1233,9 @@
1133
1233
  /* #region About box (About) — the collapsible product explainer */
1134
1234
 
1135
1235
  .mw-about {
1136
- margin-bottom: 1rem;
1236
+ margin-bottom: var(--space-5);
1137
1237
  border: 1px solid var(--control-border);
1138
- border-radius: 8px;
1238
+ border-radius: var(--radius-control);
1139
1239
  background: var(--color-background-raised);
1140
1240
  font-size: 0.9rem;
1141
1241
  }
@@ -1145,14 +1245,14 @@
1145
1245
  }
1146
1246
 
1147
1247
  .mw-about__summary {
1148
- padding: 0.65rem 0.75rem;
1248
+ padding: var(--space-4) var(--space-4);
1149
1249
  cursor: pointer;
1150
1250
  user-select: none;
1151
1251
  font-weight: 600;
1152
1252
  display: flex;
1153
1253
  align-items: center;
1154
- gap: 0.35rem;
1155
- border-radius: 8px;
1254
+ gap: var(--space-2);
1255
+ border-radius: var(--radius-control);
1156
1256
  }
1157
1257
 
1158
1258
  .mw-about__summary:hover {
@@ -1165,11 +1265,11 @@
1165
1265
  }
1166
1266
 
1167
1267
  .mw-about__body {
1168
- padding: 0 0.75rem 0.75rem;
1268
+ padding: 0 var(--space-4) var(--space-4);
1169
1269
  }
1170
1270
 
1171
1271
  .mw-about__section {
1172
- margin-top: 0.75rem;
1272
+ margin-top: var(--space-4);
1173
1273
  }
1174
1274
 
1175
1275
  .mw-about__section:first-child {
@@ -1179,7 +1279,7 @@
1179
1279
  .mw-about__heading {
1180
1280
  font-size: 0.9rem;
1181
1281
  font-weight: 700;
1182
- margin: 0 0 0.3rem;
1282
+ margin: 0 0 var(--space-1);
1183
1283
  color: var(--color-text-primary);
1184
1284
  }
1185
1285
 
@@ -1191,43 +1291,1081 @@
1191
1291
 
1192
1292
  .mw-about__section code {
1193
1293
  font-size: 0.85rem;
1194
- padding: 0.1rem 0.3rem;
1294
+ padding: var(--space-0) var(--space-1);
1195
1295
  background: var(--color-background-sunken);
1196
- border-radius: 3px;
1296
+ border-radius: var(--radius-tick);
1197
1297
  }
1198
1298
 
1199
1299
  .mw-about__legend {
1200
1300
  list-style: none;
1201
- margin: 0.5rem 0 0;
1301
+ margin: var(--space-3) 0 0;
1202
1302
  padding: 0;
1203
1303
  display: flex;
1204
1304
  flex-direction: column;
1205
- gap: 0.35rem;
1305
+ gap: var(--space-2);
1206
1306
  }
1207
1307
 
1208
1308
  .mw-about__item {
1209
1309
  display: flex;
1210
1310
  align-items: center;
1211
- gap: 0.4rem;
1311
+ gap: var(--space-2);
1212
1312
  font-size: 0.85rem;
1213
1313
  }
1214
1314
 
1215
1315
  .mw-about__swatch {
1216
1316
  flex-shrink: 0;
1217
- width: 10px;
1218
- height: 10px;
1219
- border-radius: 2px;
1317
+ width: 0.625rem;
1318
+ height: 0.625rem;
1319
+ border-radius: var(--radius-tick);
1220
1320
  }
1221
1321
 
1222
1322
  .mw-about__swatch--high {
1223
- background: #22c55e;
1323
+ background: var(--color-confidence-high);
1224
1324
  }
1225
1325
 
1226
1326
  .mw-about__swatch--mid {
1227
- background: #f59e0b;
1327
+ background: var(--color-confidence-mid);
1228
1328
  }
1229
1329
 
1230
1330
  .mw-about__swatch--low {
1231
- background: #ef4444;
1331
+ background: var(--color-confidence-low);
1332
+ }
1333
+
1334
+ /* #endregion */
1335
+
1336
+ /* #region Developer disclosure */
1337
+
1338
+ /*
1339
+ * The expert set lives here, below the result rather than above the address field. A hairline and a quieter
1340
+ * summary keep it legible without competing with the query, which is what the panel is for.
1341
+ */
1342
+ .mw-demo-developer {
1343
+ margin-top: var(--space-5);
1344
+ padding-top: var(--space-4);
1345
+ border-top: 1px solid var(--color-separator);
1346
+ }
1347
+
1348
+ .mw-demo-developer > summary {
1349
+ cursor: pointer;
1350
+ user-select: none;
1351
+ font-size: var(--font-size-footnote);
1352
+ font-weight: var(--font-weight-medium);
1353
+ color: var(--color-text-secondary);
1354
+ letter-spacing: 0.02em;
1355
+ text-transform: uppercase;
1356
+ }
1357
+
1358
+ .mw-demo-developer > summary:hover {
1359
+ color: var(--color-text-primary);
1360
+ }
1361
+
1362
+ .mw-demo-developer[open] > summary {
1363
+ margin-bottom: var(--space-4);
1364
+ }
1365
+
1366
+ /* #endregion */
1367
+
1368
+ /* #region Chrome layout — where the floating pieces sit over a map */
1369
+
1370
+ /*
1371
+ * the panel: search, examples and result on one surface.
1372
+ *
1373
+ * It replaces a floating pill plus a separate bottom sheet. Two surfaces meant the search field shared its row
1374
+ * with the control rail — which covered its right end at any width where the two met — and meant a phone could
1375
+ * open a result it had no way back from. One surface has one set of edges to reason about.
1376
+ *
1377
+ * Desktop: a column down the left over a full-bleed map, sized to its content rather than stretched to the
1378
+ * viewport, because with no result there is nothing to fill a full-height sidebar with.
1379
+ * Phone (below): a bottom drawer, the search riding at its top.
1380
+ */
1381
+ .mw-map-panel {
1382
+ position: absolute;
1383
+ top: calc(1rem + var(--safe-area-top));
1384
+ left: calc(1rem + var(--safe-area-left));
1385
+ z-index: 3;
1386
+ display: grid;
1387
+ gap: var(--space-3);
1388
+ align-content: start;
1389
+ width: min(24rem, calc(100vw - 6rem));
1390
+ /* Stops short of the footer strip, so a long result never runs under the credits. */
1391
+ max-height: calc(100% - var(--map-footer-height) - var(--safe-area-top) - var(--safe-area-bottom) - 2rem);
1392
+ /* no TOP PADDING: the grip bar carries it, so the bar's paint can reach the panel's top edge. */
1393
+ padding: 0 var(--space-4) var(--space-4);
1394
+ border-radius: var(--radius-sheet);
1395
+ overflow: auto;
1396
+ overscroll-behavior: contain;
1397
+ /*
1398
+ * The MATERIAL is not stated here. It is composed once, with every other glass surface, in the material region
1399
+ * below — which is also the list the two fallbacks repeat. Written out at the component this panel had the
1400
+ * vendor pair the wrong way round (so Firefox lost the blur), a hardcoded `blur(20px)` with no saturation, and
1401
+ * no entry in either fallback, so a viewer asking for reduced transparency still got translucent glass. Only
1402
+ * the shadow is local, because the panel's is the sheet's rather than the pill's.
1403
+ */
1404
+ box-shadow: var(--material-glass-shadow);
1405
+ }
1406
+
1407
+ /*
1408
+ * The header: the grab bar and the search field as one sticky block.
1409
+ *
1410
+ * The field is in here rather than down in the scroll because scrolling a long result used to carry it off the
1411
+ * top of the panel, and the field is what a visitor reaches for next. The reference map apps pin exactly this
1412
+ * much and let everything under it move.
1413
+ *
1414
+ * It OWNS the panel's top gutter rather than sitting inside it: the panel carries no top padding, so the header's
1415
+ * paint reaches the panel's top edge. Opaque, because the panel's material is glass and a result read straight
1416
+ * through a translucent pinned header.
1417
+ *
1418
+ * no NEGATIVE TOP MARGIN to reach that edge. In a grid the track is sized from the margin box while the item is
1419
+ * laid out at the track's start, so a bar pulled up by 12px painted 12px past its own row, onto the field below.
1420
+ */
1421
+ .mw-map-panel__header {
1422
+ position: sticky;
1423
+ top: 0;
1424
+ z-index: 2;
1425
+ display: grid;
1426
+ gap: var(--space-3);
1427
+ margin-inline: calc(-1 * var(--space-4));
1428
+ padding: var(--space-4) var(--space-4) var(--space-3);
1429
+ background: var(--color-background-raised);
1430
+ border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
1431
+ /* A hairline under the pinned block, so a result scrolling beneath it has an edge to disappear at. */
1432
+ box-shadow: 0 1px 0 var(--color-separator);
1433
+ }
1434
+
1435
+ /*
1436
+ * The grab bar's own row, inside the header.
1437
+ *
1438
+ * no MINIMUM on the desktop column: the row holds a handle that only the drawer offers and a close that only a
1439
+ * result offers, so on a desktop with nothing found it holds nothing — and a minimum drew that nothing as a band
1440
+ * of empty glass above the search field. Its height is whatever it is carrying; the drawer rule below puts a
1441
+ * minimum back, where the handle is the thing being reached for.
1442
+ */
1443
+ .mw-map-panel__grip {
1444
+ display: flex;
1445
+ align-items: center;
1446
+ justify-content: center;
1447
+ min-height: 0;
1448
+ }
1449
+
1450
+ .mw-map-panel__result {
1451
+ display: grid;
1452
+ gap: var(--space-3);
1453
+ }
1454
+
1455
+ /* The form around the field carries no margin of its own; the header's grid sets the spacing. */
1456
+ .mw-map-panel__search {
1457
+ margin: 0;
1458
+ }
1459
+
1460
+ /*
1461
+ * A sheet: the glass surface a result or a panel arrives on. The bottom one rises over the map's lower edge with
1462
+ * a drag handle, the side one sits under the control column it belongs to.
1463
+ */
1464
+ .mw-map-sheet {
1465
+ position: absolute;
1466
+ z-index: 3;
1467
+ /*
1468
+ * `max-height` measures the CONTENT box by default, so a sheet with 1rem of padding stood 34px taller than its
1469
+ * cap and ran past the bottom of the screen. Every height here is meant as the whole box.
1470
+ */
1471
+ box-sizing: border-box;
1472
+ box-shadow: var(--material-glass-shadow);
1473
+ overflow: auto;
1474
+ overscroll-behavior: contain;
1475
+ }
1476
+
1477
+ /*
1478
+ * BESIDE the control column, not below it. Opening below meant the sheet's top depended on how many controls the
1479
+ * capsule held, and every app that added one covered the button that opened it. Beside, it clears the column
1480
+ * whatever it holds: 2.5rem of control plus its inset and a gap.
1481
+ */
1482
+ .mw-map-sheet--side {
1483
+ top: calc(1rem + var(--safe-area-top));
1484
+ right: calc(4rem + var(--safe-area-right));
1485
+ width: min(24rem, calc(100vw - 6rem));
1486
+ /*
1487
+ * Measured against the VIEWPORT, not the containing block: the map wrapper is taller than the window on a page
1488
+ * that scrolls, and a percentage there let the sheet run past the bottom of the screen.
1489
+ */
1490
+ max-height: calc(100dvh - 2rem - var(--map-footer-height) - var(--safe-area-top) - var(--safe-area-bottom));
1491
+ padding: var(--space-5);
1492
+ border-radius: var(--radius-panel);
1493
+ /*
1494
+ * the TOP OF the MAP'S LADDER: panel 3 (5 as a drawer), control rail 4, side sheet 6. A side sheet is a
1495
+ * different task and covers the answer rather than sharing an edge with it. It was 5 — a tie with the drawer,
1496
+ * resolved by nothing but the order the two happen to render in, and one JSX reorder from inverting.
1497
+ */
1498
+ z-index: 6;
1499
+ }
1500
+
1501
+ /*
1502
+ * A sheet's own furniture: a title, then rows separated by hairlines, then the small print. Without these a sheet
1503
+ * is a stack of raw form controls behind glass, which reads as a debug panel rather than as part of the app.
1504
+ */
1505
+ /*
1506
+ * The title and its close button ride together and stay put while the body scrolls. The close is not decoration:
1507
+ * on a phone the sheet is the whole panel and covers the control that opened it, so this is the only way out.
1508
+ */
1509
+ .mw-map-sheet__header {
1510
+ position: sticky;
1511
+ top: 0;
1512
+ z-index: 1;
1513
+ display: flex;
1514
+ align-items: center;
1515
+ justify-content: space-between;
1516
+ gap: var(--space-5);
1517
+ margin: calc(-1 * var(--space-5)) calc(-1 * var(--space-5)) var(--space-4);
1518
+ padding: var(--space-4) var(--space-5);
1519
+ background: var(--material-glass-fallback-background);
1520
+ border-bottom: 1px solid var(--color-separator);
1521
+ }
1522
+
1523
+ .mw-map-sheet__title {
1524
+ margin: 0;
1525
+ font-family: var(--font-family-display);
1526
+ font-size: var(--font-size-headline);
1527
+ font-weight: var(--font-weight-semibold);
1528
+ color: var(--color-text-primary);
1529
+ }
1530
+
1531
+ .mw-map-sheet__close {
1532
+ display: flex;
1533
+ align-items: center;
1534
+ justify-content: center;
1535
+ flex: 0 0 auto;
1536
+ width: 1.75rem;
1537
+ height: 1.75rem;
1538
+ font-size: 1.15rem;
1539
+ line-height: 1;
1540
+ color: var(--color-text-secondary);
1541
+ background: transparent;
1542
+ border: 0;
1543
+ border-radius: var(--radius-pill);
1544
+ cursor: pointer;
1545
+ }
1546
+
1547
+ .mw-map-sheet__close:hover {
1548
+ color: var(--color-text-primary);
1549
+ background: var(--control-background);
1550
+ }
1551
+
1552
+ .mw-map-sheet__close:focus-visible {
1553
+ outline: 2px solid var(--color-selection);
1554
+ outline-offset: 1px;
1555
+ }
1556
+
1557
+ /*
1558
+ * The close sits at the right of the grip bar.
1559
+ *
1560
+ * IN FLOW on the desktop column, where the bar has no handle to center and therefore no height of its own. Taken
1561
+ * out of flow there it reserved nothing, and a 44px button hanging from a 0px bar came down across the right end
1562
+ * of the search field below it — the overlap this whole panel was meant to end. In flow it carries the bar, and
1563
+ * the field starts under it.
1564
+ *
1565
+ * The phone rule below puts it back to absolute, where the handle is the bar and has to stay centered on the sheet
1566
+ * rather than on the space the close leaves, and where the target grows to a full 44px because the drawer is the
1567
+ * whole panel and this is the only way out of it.
1568
+ */
1569
+ .mw-map-sheet__close--floating {
1570
+ margin-left: auto;
1571
+ right: 0.25rem;
1572
+ }
1573
+
1574
+ .mw-map-sheet__row {
1575
+ display: grid;
1576
+ gap: var(--space-1);
1577
+ padding: var(--space-4) 0;
1578
+ border-top: 1px solid var(--color-separator);
1579
+ }
1580
+
1581
+ .mw-map-sheet__row:first-of-type {
1582
+ border-top: 0;
1583
+ padding-top: 0;
1584
+ }
1585
+
1586
+ .mw-map-sheet__label {
1587
+ font-size: var(--font-size-footnote);
1588
+ font-weight: var(--font-weight-medium);
1589
+ color: var(--color-text-secondary);
1590
+ }
1591
+
1592
+ .mw-map-sheet__hint {
1593
+ margin: 0;
1594
+ font-size: var(--font-size-caption);
1595
+ color: var(--color-text-tertiary);
1596
+ }
1597
+
1598
+ /* An intro paragraph is not a row, so it carries its own space before the first hairline. */
1599
+ .mw-map-sheet > .mw-map-sheet__hint {
1600
+ margin-bottom: var(--space-4);
1601
+ }
1602
+
1603
+ /* A value in a row reads at the row's size; the browser's default `code` is a step larger than the label above it. */
1604
+ .mw-map-sheet__row code {
1605
+ font-family: var(--font-family-code);
1606
+ font-size: var(--font-size-footnote);
1607
+ color: var(--color-text-primary);
1608
+ }
1609
+
1610
+ /* A checkbox and its words are one target, and the words sit beside the box rather than under it. */
1611
+ .mw-map-sheet__check {
1612
+ display: flex;
1613
+ align-items: baseline;
1614
+ gap: var(--space-3);
1615
+ cursor: pointer;
1616
+ font-size: var(--font-size-footnote);
1617
+ color: var(--color-text-primary);
1618
+ }
1619
+
1620
+ .mw-map-sheet__check input[type="checkbox"] {
1621
+ margin: 0;
1622
+ accent-color: var(--color-accent);
1623
+ cursor: pointer;
1624
+ }
1625
+
1626
+ /* Every form control in a sheet takes the app's type and the control palette, never the browser's defaults. */
1627
+ .mw-map-sheet select,
1628
+ .mw-map-sheet input[type="text"],
1629
+ .mw-map-sheet input[type="search"] {
1630
+ width: 100%;
1631
+ box-sizing: border-box;
1632
+ padding: var(--space-2) var(--space-3);
1633
+ font: inherit;
1634
+ font-size: var(--font-size-footnote);
1635
+ color: var(--control-foreground);
1636
+ background: var(--control-background);
1637
+ border: 1px solid var(--control-border);
1638
+ border-radius: var(--radius-control);
1639
+ }
1640
+
1641
+ .mw-map-sheet select:focus-visible,
1642
+ .mw-map-sheet input:focus-visible {
1643
+ outline: 2px solid var(--color-selection);
1644
+ outline-offset: 1px;
1645
+ }
1646
+
1647
+ /*
1648
+ * The grab affordance, and now a real control: drag to resize between two detents, drag down past the smaller one
1649
+ * to dismiss, tap or Enter to toggle. It was decorative, which is worse than absent — a handle that looks draggable
1650
+ * and is not reads as a broken sheet rather than a static one.
1651
+ *
1652
+ * The pill is drawn as a pseudo-element so the button itself can be a 44px-tall target without a 44px-tall pill.
1653
+ * `touch-action: none` stops the browser claiming the gesture as a scroll before the pointer handlers see it.
1654
+ */
1655
+ /* Desktop has no drawer to resize, so the handle is not offered there; the phone rule below restores it. */
1656
+ .mw-map-panel .mw-map-sheet__handle {
1657
+ display: none;
1658
+ }
1659
+
1660
+ .mw-map-sheet__handle {
1661
+ display: grid;
1662
+ place-items: center;
1663
+ width: 3rem;
1664
+ height: 2.5rem;
1665
+ padding: 0;
1666
+ border: 0;
1667
+ background: transparent;
1668
+ color: inherit;
1669
+ cursor: grab;
1670
+ touch-action: none;
1671
+ }
1672
+
1673
+ .mw-map-sheet__handle::before {
1674
+ content: "";
1675
+ width: 2.25rem;
1676
+ height: 0.25rem;
1677
+ border-radius: var(--radius-pill);
1678
+ background: var(--color-separator);
1679
+ }
1680
+
1681
+ .mw-map-sheet__handle:hover::before {
1682
+ background: var(--color-text-tertiary);
1683
+ }
1684
+
1685
+ .mw-map-sheet__handle:active {
1686
+ cursor: grabbing;
1687
+ }
1688
+
1689
+ .mw-map-sheet__handle:focus-visible {
1690
+ outline: 2px solid var(--color-selection);
1691
+ outline-offset: -2px;
1692
+ border-radius: var(--radius-pill);
1693
+ }
1694
+
1695
+ .mw-map-sheet__actions {
1696
+ margin-top: var(--space-4);
1697
+ display: flex;
1698
+ justify-content: flex-end;
1232
1699
  }
1700
+
1701
+ /*
1702
+ * On a phone the chrome spans the width and the sheet becomes a true bottom sheet against the edge, which is what
1703
+ * leaves the map its full height.
1704
+ */
1705
+ /*
1706
+ * The phone layout. The map keeps the whole screen and every floating piece is measured against the footer strip,
1707
+ * whose height is `--map-footer-height` plus the safe-area inset: a sheet that ends at the screen's edge hides its
1708
+ * own last rows behind the credits.
1709
+ *
1710
+ * A RESULT is a bottom sheet, because it answers the query in the field above it and both stay in view. A SIDE
1711
+ * sheet is the whole panel, because it is a different task — layers, settings, the explainer — and a phone has no
1712
+ * room to hold it beside anything. That also settles the overlap: the panel covers the result rather than sharing
1713
+ * an edge with it.
1714
+ */
1715
+ @media (max-width: 600px) {
1716
+ /*
1717
+ * The column spans the width, so it has to stop short of the control capsule in the same corner — 2.5rem of
1718
+ * control plus its 1rem inset plus a 0.5rem gap. Without this the capsule sits on the search pill's clear
1719
+ * button, which is the one control at that end of the pill.
1720
+ */
1721
+ /*
1722
+ * The drawer. Anchored to the bottom edge above the footer, full width, and detented — the search rides at its
1723
+ * top so reaching it never means getting a result out of the way first.
1724
+ */
1725
+ .mw-map-panel {
1726
+ top: auto;
1727
+ left: 0;
1728
+ right: 0;
1729
+ bottom: calc(var(--map-footer-height) + var(--safe-area-bottom));
1730
+ width: auto;
1731
+ /*
1732
+ * above the control rail (`z-index: 4`), which the desktop column never reaches. Pulled to its tall detent
1733
+ * the drawer's top edge crosses the rail's corner, and drawn under it the rail sat on the drawer's own
1734
+ * close. A sheet raised over a map covers what it covers; it does not share a corner with it.
1735
+ */
1736
+ z-index: 5;
1737
+ /*
1738
+ * `none`, not `contain`. A pull at the top of the scroll is a drag on the drawer here (see
1739
+ * `onPanelPointerMove`), and the browser's own rubber band fought it: the content bounced while the drawer
1740
+ * stayed put, which reads as the gesture being heard and refused.
1741
+ */
1742
+ overscroll-behavior: none;
1743
+ max-height: 55dvh;
1744
+ border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
1745
+ border-inline: 0;
1746
+ border-bottom: 0;
1747
+ }
1748
+
1749
+ .mw-map-panel .mw-map-sheet__handle {
1750
+ display: grid;
1751
+ }
1752
+
1753
+ /* The header is the grab target, so the browser must not claim the gesture as a scroll before it is read. */
1754
+ .mw-map-panel__header {
1755
+ touch-action: none;
1756
+ }
1757
+
1758
+ /*
1759
+ * The control rail is hidden while a result is open. It shares the corner the drawer's tall detent reaches,
1760
+ * and a control stranded above an open drawer is out of reach of the thumb holding the phone. Closing the
1761
+ * result brings it back — which is the same gesture that gives the map back.
1762
+ */
1763
+ /* Doubled class: the base `.mw-map-control-stack` sets `display: flex` further down the sheet, and a single
1764
+ * class here loses to it on source order alone. */
1765
+ .mw-map-control-stack.mw-map-control-stack--drawer-open {
1766
+ display: none;
1767
+ }
1768
+
1769
+ /*
1770
+ * The drawer's bar is the grab target, so it keeps a full row — but only while it holds one. With no result the
1771
+ * bar is empty on a phone too, and an empty band over the search field is the gap this rule would otherwise put
1772
+ * back. `:has()` is the test; where it is unsupported the bar collapses, which is the harmless half.
1773
+ */
1774
+ .mw-map-panel__grip:has(button) {
1775
+ min-height: 2.25rem;
1776
+ }
1777
+
1778
+ .mw-map-sheet__close--floating {
1779
+ position: absolute;
1780
+ margin-left: 0;
1781
+ width: 2.75rem;
1782
+ height: 2.75rem;
1783
+ }
1784
+
1785
+ .mw-map-sheet--side {
1786
+ top: 0;
1787
+ left: 0;
1788
+ right: 0;
1789
+ bottom: calc(var(--map-footer-height) + var(--safe-area-bottom));
1790
+ width: auto;
1791
+ max-width: none;
1792
+ max-height: none;
1793
+ border-radius: 0;
1794
+ border: 0;
1795
+ padding: calc(var(--space-5) + var(--safe-area-top)) var(--space-5) var(--space-5);
1796
+ }
1797
+
1798
+ /* The chips take the full width on a phone, so the fade has less to hide behind. */
1799
+ .mw-map-chiprow {
1800
+ -webkit-mask-image: linear-gradient(to right, black calc(100% - 1.25rem), transparent);
1801
+ mask-image: linear-gradient(to right, black calc(100% - 1.25rem), transparent);
1802
+ }
1803
+
1804
+ /* The header spans the panel's full width once the sheet loses its own inset. */
1805
+ .mw-map-sheet--side .mw-map-sheet__header {
1806
+ margin: calc(-1 * (var(--space-5) + var(--safe-area-top))) calc(-1 * var(--space-5)) var(--space-4);
1807
+ padding-top: calc(var(--space-4) + var(--safe-area-top));
1808
+ }
1809
+ }
1810
+
1811
+ /* #endregion */
1812
+
1813
+ /* #region Map chrome */
1814
+
1815
+ /*
1816
+ * The floating controls over a map: a search pill on top, example chips beneath it, a control column down one
1817
+ * edge, a footer along the bottom. Each is a glass material composed from tokens, and each carries the opaque
1818
+ * fallback below — no control depends on `backdrop-filter` to stay readable.
1819
+ */
1820
+
1821
+ /*
1822
+ * every glass surface is listed here, and the two fallback blocks at the end of this region repeat exactly this
1823
+ * list. Composing the material at each component instead left the sheets and the sources popover out of both
1824
+ * fallbacks, so a viewer who asked for reduced transparency still got a translucent panel.
1825
+ */
1826
+ .mw-map-searchbar,
1827
+ .mw-map-chip,
1828
+ .mw-map-control-group,
1829
+ .mw-map-footer,
1830
+ .mw-map-footer__popover,
1831
+ .mw-map-sheet,
1832
+ .mw-map-panel {
1833
+ background: var(--material-glass-background);
1834
+ border: 1px solid var(--material-glass-border);
1835
+ /*
1836
+ * PREFIXED first, STANDARD last. The minifier collapses two declarations carrying the same value and keeps the
1837
+ * last one; written the other way round the shipped rule had `-webkit-backdrop-filter` alone, so every glass
1838
+ * surface lost its blur on Firefox, which implements the standard property and not the alias.
1839
+ */
1840
+ -webkit-backdrop-filter: blur(var(--material-glass-blur)) saturate(var(--material-glass-saturation));
1841
+ backdrop-filter: blur(var(--material-glass-blur)) saturate(var(--material-glass-saturation));
1842
+ color: var(--color-text-primary);
1843
+ }
1844
+
1845
+ .mw-map-searchbar {
1846
+ display: flex;
1847
+ align-items: center;
1848
+ gap: var(--space-3);
1849
+ padding: var(--space-2) var(--space-4);
1850
+ border-radius: var(--radius-pill);
1851
+ box-shadow: var(--material-glass-shadow);
1852
+ }
1853
+
1854
+ .mw-map-searchbar__field {
1855
+ flex: 1;
1856
+ min-width: 0;
1857
+ }
1858
+
1859
+ /*
1860
+ * Busy is a hairline along the pill's lower edge, inside its radius. It is drawn on a pseudo-element so it costs
1861
+ * no layout — a spinner in a slot changed the pill's height on every submit, which moved the field under the
1862
+ * pointer.
1863
+ */
1864
+ .mw-map-searchbar {
1865
+ position: relative;
1866
+ overflow: hidden;
1867
+ }
1868
+
1869
+ .mw-map-searchbar--busy::after {
1870
+ content: "";
1871
+ position: absolute;
1872
+ left: 0;
1873
+ right: 0;
1874
+ bottom: 0;
1875
+ height: 2px;
1876
+ background: var(--color-accent);
1877
+ transform-origin: left center;
1878
+ animation: mw-map-searchbar-sweep 1.1s var(--easing-standard) infinite;
1879
+ }
1880
+
1881
+ @keyframes mw-map-searchbar-sweep {
1882
+ 0% {
1883
+ transform: scaleX(0);
1884
+ transform-origin: left center;
1885
+ }
1886
+
1887
+ 50% {
1888
+ transform: scaleX(1);
1889
+ transform-origin: left center;
1890
+ }
1891
+
1892
+ 51% {
1893
+ transform: scaleX(1);
1894
+ transform-origin: right center;
1895
+ }
1896
+
1897
+ 100% {
1898
+ transform: scaleX(0);
1899
+ transform-origin: right center;
1900
+ }
1901
+ }
1902
+
1903
+ /*
1904
+ * The native clear button of `input[type="search"]`. Its COLOR is the user agent's, decided by the document's
1905
+ * `color-scheme` — nothing here repaints it. Only the two things the scheme does not say are stated: that it is a
1906
+ * pointer target, and that it sits back until the field is under the pointer or holding focus.
1907
+ */
1908
+ .mw-map-searchbar input[type="search"]::-webkit-search-cancel-button {
1909
+ cursor: pointer;
1910
+ opacity: 0.55;
1911
+ }
1912
+
1913
+ .mw-map-searchbar input[type="search"]:hover::-webkit-search-cancel-button,
1914
+ .mw-map-searchbar input[type="search"]:focus::-webkit-search-cancel-button {
1915
+ opacity: 1;
1916
+ }
1917
+
1918
+ /* The host's input carries no chrome of its own — the pill is the chrome. */
1919
+ .mw-map-searchbar__field input {
1920
+ width: 100%;
1921
+ border: 0;
1922
+ outline: 0;
1923
+ background: transparent;
1924
+ color: inherit;
1925
+ font: inherit;
1926
+ padding: var(--space-1) 0;
1927
+ }
1928
+
1929
+ .mw-map-searchbar:focus-within {
1930
+ outline: 2px solid var(--color-selection);
1931
+ outline-offset: 2px;
1932
+ }
1933
+
1934
+ /* The glyph face draws its control marks small, so a leading or trailing mark is sized up to the field's own weight. */
1935
+ .mw-map-searchbar__slot {
1936
+ display: inline-flex;
1937
+ align-items: center;
1938
+ color: var(--color-text-secondary);
1939
+ font-family: var(--font-family-glyph);
1940
+ font-size: 1.15rem;
1941
+ line-height: 1;
1942
+ }
1943
+
1944
+ /* A drawn mark, not a typed one — see `SearchGlyph`. `block` keeps the line box off its baseline. */
1945
+ .mw-map-searchbar__glyph {
1946
+ display: block;
1947
+ }
1948
+
1949
+ /*
1950
+ * One line that scrolls, never a wrap. The scrollbar is hidden because the row sits over a map where a track
1951
+ * would read as chrome; the row stays reachable by keyboard through its buttons.
1952
+ */
1953
+ .mw-map-chiprow {
1954
+ display: flex;
1955
+ gap: var(--space-2);
1956
+ overflow-x: auto;
1957
+ scrollbar-width: none;
1958
+ padding: var(--space-0);
1959
+ scroll-padding-inline: 0.15rem;
1960
+ /* With the track hidden, a chip clipped at the edge reads as broken; the fade is what says the row scrolls. */
1961
+ -webkit-mask-image: linear-gradient(to right, black calc(100% - 2rem), transparent);
1962
+ mask-image: linear-gradient(to right, black calc(100% - 2rem), transparent);
1963
+ }
1964
+
1965
+ .mw-map-chiprow::-webkit-scrollbar {
1966
+ display: none;
1967
+ }
1968
+
1969
+ .mw-map-chip {
1970
+ flex: 0 0 auto;
1971
+ display: inline-flex;
1972
+ align-items: center;
1973
+ gap: var(--space-2);
1974
+ padding: var(--space-2) var(--space-4);
1975
+ border-radius: var(--radius-pill);
1976
+ font-size: var(--font-size-footnote);
1977
+ font-weight: var(--font-weight-medium);
1978
+ white-space: nowrap;
1979
+ cursor: pointer;
1980
+ }
1981
+
1982
+ .mw-map-chip:hover:not(:disabled) {
1983
+ background: var(--material-glass-fallback-background);
1984
+ border-color: var(--control-border);
1985
+ }
1986
+
1987
+ .mw-map-chip:focus-visible {
1988
+ outline: 2px solid var(--color-selection);
1989
+ outline-offset: 1px;
1990
+ }
1991
+
1992
+ /* A chip that carries a state reports it through `aria-pressed`, so the accent follows the attribute, not a class. */
1993
+ .mw-map-chip--active,
1994
+ .mw-map-chip[aria-pressed="true"] {
1995
+ border-color: var(--color-accent);
1996
+ color: var(--color-accent);
1997
+ }
1998
+
1999
+ .mw-map-chip:disabled {
2000
+ opacity: 0.5;
2001
+ cursor: not-allowed;
2002
+ }
2003
+
2004
+ .mw-map-chip__icon {
2005
+ display: inline-flex;
2006
+ font-family: var(--font-family-glyph);
2007
+ }
2008
+
2009
+ .mw-map-control-stack {
2010
+ position: absolute;
2011
+ top: calc(1rem + var(--safe-area-top));
2012
+ display: flex;
2013
+ flex-direction: column;
2014
+ gap: var(--space-3);
2015
+ z-index: 4;
2016
+ }
2017
+
2018
+ /*
2019
+ * The compass capsule collapses to nothing while the compass is hidden, so the capsules below it do not sit under
2020
+ * a gap the map shows through. `visibility` alone kept the box.
2021
+ */
2022
+ .mw-map-control-group--compass:has(.mw-map-compass--north) {
2023
+ box-shadow: none;
2024
+ border-color: transparent;
2025
+ background: transparent;
2026
+ -webkit-backdrop-filter: none;
2027
+ backdrop-filter: none;
2028
+ }
2029
+
2030
+ .mw-map-control-stack--right {
2031
+ right: calc(1rem + var(--safe-area-right));
2032
+ }
2033
+
2034
+ .mw-map-control-stack--left {
2035
+ left: calc(1rem + var(--safe-area-left));
2036
+ }
2037
+
2038
+ /* A capsule: related controls read as one object, divided by hairlines rather than gaps. */
2039
+ .mw-map-control-group {
2040
+ display: flex;
2041
+ flex-direction: column;
2042
+ border-radius: var(--radius-control);
2043
+ overflow: hidden;
2044
+ box-shadow: var(--material-glass-shadow);
2045
+ }
2046
+
2047
+ .mw-map-control-group > * + * {
2048
+ border-top: 1px solid var(--material-glass-border);
2049
+ }
2050
+
2051
+ .mw-map-control {
2052
+ display: flex;
2053
+ align-items: center;
2054
+ justify-content: center;
2055
+ /* 2.5rem = 40px, under the 44px minimum for a touch target, and these three are the only way into the about,
2056
+ layers and developer sheets. */
2057
+ width: 2.75rem;
2058
+ height: 2.75rem;
2059
+ border: 0;
2060
+ background: transparent;
2061
+ color: var(--color-text-primary);
2062
+ font-family: var(--font-family-glyph);
2063
+ cursor: pointer;
2064
+ }
2065
+
2066
+ /*
2067
+ * This rail was the one control group in the app with no focus ring — every sibling has one (.mw-map-chip,
2068
+ * .mw-map-compass, .mw-map-sheet__close, .mw-map-footer__attribution-button). An `outline` alone would not have
2069
+ * shown anyway: the parent capsule clips with `overflow: hidden`, which hides an outline drawn outside the button's
2070
+ * box. An inset ring is drawn inside it, so the capsule cannot clip it.
2071
+ */
2072
+ .mw-map-control:focus-visible {
2073
+ outline: none;
2074
+ box-shadow: inset 0 0 0 2px var(--color-selection);
2075
+ }
2076
+
2077
+ .mw-map-control:hover:not(:disabled) {
2078
+ background: var(--control-background);
2079
+ }
2080
+
2081
+ .mw-map-control:active:not(:disabled) {
2082
+ background: var(--control-background-pressed);
2083
+ }
2084
+
2085
+ .mw-map-control--active {
2086
+ color: var(--color-accent);
2087
+ }
2088
+
2089
+ .mw-map-control:disabled {
2090
+ opacity: 0.4;
2091
+ cursor: not-allowed;
2092
+ }
2093
+
2094
+ /*
2095
+ * The compass keeps its box while invisible so the capsules above it do not shift as the map rotates.
2096
+ * `visibility` rather than `display` is what lets the opacity transition run at all.
2097
+ */
2098
+ /*
2099
+ * The compass sits inside a control capsule like the other controls, so it carries no material of its own — the
2100
+ * capsule supplies the glass, and a second background here would read as a button on a button.
2101
+ */
2102
+ .mw-map-compass {
2103
+ display: flex;
2104
+ align-items: center;
2105
+ justify-content: center;
2106
+ width: 2.5rem;
2107
+ height: 2.5rem;
2108
+ border: 0;
2109
+ background: transparent;
2110
+ color: var(--color-text-primary);
2111
+ cursor: pointer;
2112
+ opacity: 1;
2113
+ visibility: visible;
2114
+ transition:
2115
+ opacity var(--duration-standard) var(--easing-standard),
2116
+ visibility 0s linear 0s;
2117
+ }
2118
+
2119
+ .mw-map-compass:hover:not(:disabled) {
2120
+ background: var(--control-background);
2121
+ }
2122
+
2123
+ .mw-map-compass:active:not(:disabled) {
2124
+ background: var(--control-background-pressed);
2125
+ }
2126
+
2127
+ .mw-map-compass:focus-visible {
2128
+ outline: 2px solid var(--color-selection);
2129
+ outline-offset: -2px;
2130
+ }
2131
+
2132
+ /* The dial: a ring, not a disc — the map has to stay readable through the middle of the control. */
2133
+ .mw-map-compass__dial {
2134
+ fill: none;
2135
+ stroke: var(--color-separator);
2136
+ stroke-width: 1.5;
2137
+ }
2138
+
2139
+ /* Red north, pale south. Two tones are what say which end points north without the reader knowing a convention. */
2140
+ .mw-map-compass__north {
2141
+ fill: var(--color-state-danger);
2142
+ }
2143
+
2144
+ .mw-map-compass__south {
2145
+ fill: var(--color-text-tertiary);
2146
+ }
2147
+
2148
+ /*
2149
+ * Invisible, and out of the way: `visibility: hidden` keeps the element in flow, so without the pointer rule a
2150
+ * compass nobody can see still swallows every click on the map beneath it.
2151
+ */
2152
+ .mw-map-compass--north {
2153
+ opacity: 0;
2154
+ visibility: hidden;
2155
+ pointer-events: none;
2156
+ transition:
2157
+ opacity var(--duration-standard) var(--easing-standard),
2158
+ visibility 0s linear var(--duration-standard);
2159
+ }
2160
+
2161
+ /*
2162
+ * One line, always. Wrapping was the alternative and it grew the strip to three lines on a phone, where it then
2163
+ * covered the bottom sheet's last rows — the credits are the least important thing on the screen and must never
2164
+ * take height from the thing a visitor asked for. Long credits scroll instead.
2165
+ */
2166
+ /*
2167
+ * the STRIP ITSELF does not CLIP. A non-visible overflow on one axis makes the other clip as well, so an
2168
+ * `overflow-x` here erased the sources popover outright — it is positioned above the strip, entirely outside the
2169
+ * box that would have been doing the clipping. The scroll belongs to the identity region, which is the part
2170
+ * carrying text long enough to need one.
2171
+ */
2172
+ .mw-map-footer {
2173
+ display: flex;
2174
+ align-items: center;
2175
+ justify-content: space-between;
2176
+ gap: var(--space-5);
2177
+ flex-wrap: nowrap;
2178
+ white-space: nowrap;
2179
+ box-sizing: content-box;
2180
+ height: var(--map-footer-height);
2181
+ padding: 0 var(--space-4);
2182
+ padding-bottom: var(--safe-area-bottom);
2183
+ border-radius: 0;
2184
+ border-inline: 0;
2185
+ border-bottom: 0;
2186
+ font-size: var(--font-size-caption);
2187
+ color: var(--color-text-secondary);
2188
+ }
2189
+
2190
+ /* Identity, then whatever is loading. The status is where the loader says what; the bar on top says how far. */
2191
+ .mw-map-footer__identity {
2192
+ flex: 1 1 auto;
2193
+ min-width: 0;
2194
+ display: flex;
2195
+ align-items: baseline;
2196
+ gap: var(--space-3);
2197
+ overflow-x: auto;
2198
+ scrollbar-width: none;
2199
+ }
2200
+
2201
+ .mw-map-footer__identity::-webkit-scrollbar {
2202
+ display: none;
2203
+ }
2204
+
2205
+ .mw-map-footer__status {
2206
+ min-width: 0;
2207
+ overflow: hidden;
2208
+ text-overflow: ellipsis;
2209
+ color: var(--color-text-tertiary);
2210
+ }
2211
+
2212
+ .mw-map-footer__sources {
2213
+ position: relative;
2214
+ flex: 0 0 auto;
2215
+ }
2216
+
2217
+ .mw-map-footer__attribution-button {
2218
+ padding: var(--space-0) var(--space-3);
2219
+ font: inherit;
2220
+ color: var(--color-text-secondary);
2221
+ background: transparent;
2222
+ border: 1px solid transparent;
2223
+ border-radius: var(--radius-pill);
2224
+ cursor: pointer;
2225
+ }
2226
+
2227
+ .mw-map-footer__attribution-button:hover,
2228
+ .mw-map-footer__attribution-button[aria-expanded="true"] {
2229
+ color: var(--color-text-primary);
2230
+ background: var(--control-background);
2231
+ border-color: var(--control-border);
2232
+ }
2233
+
2234
+ .mw-map-footer__attribution-button:focus-visible {
2235
+ outline: 2px solid var(--color-selection);
2236
+ outline-offset: 1px;
2237
+ }
2238
+
2239
+ /*
2240
+ * One credit line. `MapFooter` has always rendered this class and `styles.css` never defined it, so the credits
2241
+ * inherited whatever the popover gave them — which was fine while they were bare strings and not once they became
2242
+ * links that have to look like links.
2243
+ */
2244
+ .mw-map-footer__credit {
2245
+ line-height: 1.5;
2246
+ }
2247
+
2248
+ .mw-map-footer__credit a {
2249
+ color: inherit;
2250
+ text-decoration: underline;
2251
+ text-underline-offset: 2px;
2252
+ }
2253
+
2254
+ .mw-map-footer__credit a:hover {
2255
+ color: var(--color-text-primary);
2256
+ }
2257
+
2258
+ .mw-map-footer__credit a:focus-visible {
2259
+ outline: 2px solid var(--color-selection);
2260
+ outline-offset: 2px;
2261
+ }
2262
+
2263
+ /* The credits open UPWARD from the strip, because the strip is already against the bottom of the viewport. */
2264
+ .mw-map-footer__popover {
2265
+ position: absolute;
2266
+ right: 0;
2267
+ bottom: calc(100% + 0.4rem);
2268
+ z-index: 1;
2269
+ display: grid;
2270
+ gap: var(--space-1);
2271
+ min-width: max-content;
2272
+ max-width: min(22rem, calc(100vw - 2rem));
2273
+ padding: var(--space-3) var(--space-4);
2274
+ white-space: normal;
2275
+ text-align: right;
2276
+ box-shadow: var(--material-glass-shadow);
2277
+ border-radius: var(--radius-panel);
2278
+ color: var(--color-text-secondary);
2279
+ }
2280
+
2281
+ /* #region Loading */
2282
+
2283
+ /*
2284
+ * The bundle-load bar, pinned across the top of the viewport. It reports at the page's edge because loading the
2285
+ * model is a page-level event: in the search field it resized the control being typed into, and in the result
2286
+ * sheet it held open a panel that had no answer in it yet.
2287
+ */
2288
+ .mw-map-progress {
2289
+ position: fixed;
2290
+ top: 0;
2291
+ left: 0;
2292
+ right: 0;
2293
+ z-index: 10;
2294
+ height: 3px;
2295
+ overflow: hidden;
2296
+ background: var(--control-background);
2297
+ opacity: 1;
2298
+ transition: opacity var(--duration-standard) var(--easing-standard);
2299
+ }
2300
+
2301
+ .mw-map-progress--idle {
2302
+ opacity: 0;
2303
+ pointer-events: none;
2304
+ }
2305
+
2306
+ .mw-map-progress__fill {
2307
+ height: 100%;
2308
+ background: var(--color-accent);
2309
+ transform: scaleX(0);
2310
+ transform-origin: left center;
2311
+ transition: transform var(--duration-standard) var(--easing-decelerate);
2312
+ }
2313
+
2314
+ /* Work whose length is unknown sweeps rather than filling, so the bar never invents a percentage. */
2315
+ .mw-map-progress__fill--indeterminate {
2316
+ width: 35%;
2317
+ transform: none;
2318
+ animation: mw-map-progress-sweep 1.4s var(--easing-standard) infinite;
2319
+ }
2320
+
2321
+ @keyframes mw-map-progress-sweep {
2322
+ from {
2323
+ margin-left: -35%;
2324
+ }
2325
+
2326
+ to {
2327
+ margin-left: 100%;
2328
+ }
2329
+ }
2330
+
2331
+ /* #endregion */
2332
+
2333
+ @media (prefers-reduced-motion: reduce) {
2334
+ .mw-map-compass,
2335
+ .mw-map-compass--north {
2336
+ transition: none;
2337
+ }
2338
+ }
2339
+
2340
+ /*
2341
+ * Where the blur cannot run, or the viewer has asked for less transparency, every material becomes opaque.
2342
+ * Text contrast holds in all three states; the glass is never what makes a control legible.
2343
+ */
2344
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
2345
+ .mw-map-searchbar,
2346
+ .mw-map-chip,
2347
+ .mw-map-control-group,
2348
+ .mw-map-footer,
2349
+ .mw-map-footer__popover,
2350
+ .mw-map-sheet,
2351
+ .mw-map-panel {
2352
+ background: var(--material-glass-fallback-background);
2353
+ }
2354
+ }
2355
+
2356
+ @media (prefers-reduced-transparency: reduce) {
2357
+ .mw-map-searchbar,
2358
+ .mw-map-chip,
2359
+ .mw-map-control-group,
2360
+ .mw-map-footer,
2361
+ .mw-map-footer__popover,
2362
+ .mw-map-sheet,
2363
+ .mw-map-panel {
2364
+ background: var(--material-glass-fallback-background);
2365
+ -webkit-backdrop-filter: none;
2366
+ backdrop-filter: none;
2367
+ }
2368
+ }
2369
+
2370
+ /* #endregion */
1233
2371
  }