@diagrammo/dgmo 0.21.1 → 0.22.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 (73) hide show
  1. package/README.md +16 -6
  2. package/dist/advanced.cjs +2003 -466
  3. package/dist/advanced.d.cts +5714 -0
  4. package/dist/advanced.d.ts +5714 -0
  5. package/dist/advanced.js +1999 -466
  6. package/dist/auto.cjs +2048 -449
  7. package/dist/auto.d.cts +39 -0
  8. package/dist/auto.d.ts +39 -0
  9. package/dist/auto.js +121 -121
  10. package/dist/auto.mjs +2050 -450
  11. package/dist/cli.cjs +170 -170
  12. package/dist/editor.cjs +13 -16
  13. package/dist/editor.js +13 -16
  14. package/dist/highlight.cjs +15 -13
  15. package/dist/highlight.js +15 -13
  16. package/dist/index.cjs +2032 -435
  17. package/dist/index.d.cts +339 -0
  18. package/dist/index.d.ts +339 -0
  19. package/dist/index.js +2034 -436
  20. package/dist/internal.cjs +2003 -466
  21. package/dist/internal.d.cts +5714 -0
  22. package/dist/internal.d.ts +5714 -0
  23. package/dist/internal.js +1999 -466
  24. package/dist/map-data/water-bodies.json +1 -0
  25. package/docs/language-reference.md +20 -9
  26. package/gallery/fixtures/map-categorical-world.dgmo +16 -0
  27. package/gallery/fixtures/map-categorical.dgmo +0 -1
  28. package/gallery/fixtures/map-choropleth.dgmo +0 -1
  29. package/gallery/fixtures/map-coastline.dgmo +7 -0
  30. package/gallery/fixtures/map-colorize.dgmo +11 -0
  31. package/gallery/fixtures/map-direct-color.dgmo +0 -1
  32. package/gallery/fixtures/map-reference-world.dgmo +11 -0
  33. package/gallery/fixtures/map-region-scope.dgmo +0 -3
  34. package/gallery/fixtures/map-route.dgmo +0 -1
  35. package/package.json +1 -1
  36. package/src/advanced.ts +12 -1
  37. package/src/boxes-and-lines/renderer.ts +39 -12
  38. package/src/cli.ts +1 -1
  39. package/src/completion.ts +32 -25
  40. package/src/cycle/renderer.ts +14 -1
  41. package/src/d3.ts +8 -2
  42. package/src/editor/highlight-api.ts +4 -0
  43. package/src/editor/keywords.ts +13 -16
  44. package/src/infra/renderer.ts +35 -7
  45. package/src/map/colorize.ts +54 -0
  46. package/src/map/context-labels.ts +429 -0
  47. package/src/map/data/types.ts +34 -0
  48. package/src/map/data/water-bodies.json +1 -0
  49. package/src/map/dimensions.ts +117 -0
  50. package/src/map/geo-query.ts +21 -3
  51. package/src/map/geo.ts +47 -1
  52. package/src/map/layout.ts +1300 -251
  53. package/src/map/load-data.ts +10 -2
  54. package/src/map/parser.ts +42 -116
  55. package/src/map/renderer.ts +512 -13
  56. package/src/map/resolved-types.ts +16 -2
  57. package/src/map/resolver.ts +208 -59
  58. package/src/map/types.ts +30 -32
  59. package/src/mindmap/renderer.ts +10 -1
  60. package/src/palettes/atlas.ts +77 -0
  61. package/src/palettes/blueprint.ts +73 -0
  62. package/src/palettes/color-utils.ts +58 -1
  63. package/src/palettes/index.ts +12 -3
  64. package/src/palettes/slate.ts +73 -0
  65. package/src/palettes/tidewater.ts +73 -0
  66. package/src/render.ts +8 -1
  67. package/src/tech-radar/renderer.ts +3 -0
  68. package/src/tech-radar/types.ts +3 -0
  69. package/src/utils/d3-types.ts +5 -0
  70. package/src/utils/legend-layout.ts +21 -4
  71. package/src/utils/legend-types.ts +7 -0
  72. package/src/utils/reserved-key-registry.ts +3 -0
  73. package/src/palettes/bold.ts +0 -67
package/dist/advanced.js CHANGED
@@ -371,18 +371,18 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
371
371
  const results = [];
372
372
  for (let i = 0; i < points.length; i++) {
373
373
  const pt = points[i];
374
- const labelWidth = pt.label.length * fontSize * CHAR_WIDTH_RATIO + 8;
374
+ const labelWidth2 = pt.label.length * fontSize * CHAR_WIDTH_RATIO + 8;
375
375
  let best = null;
376
376
  const directions = [
377
377
  {
378
378
  // Above
379
379
  gen: (offset) => {
380
- const lx = pt.cx - labelWidth / 2;
380
+ const lx = pt.cx - labelWidth2 / 2;
381
381
  const ly = pt.cy - offset - labelHeight;
382
- if (ly < chartBounds.top || lx < chartBounds.left || lx + labelWidth > chartBounds.right)
382
+ if (ly < chartBounds.top || lx < chartBounds.left || lx + labelWidth2 > chartBounds.right)
383
383
  return null;
384
384
  return {
385
- rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
385
+ rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
386
386
  textX: pt.cx,
387
387
  textY: ly + labelHeight / 2,
388
388
  anchor: "middle"
@@ -392,12 +392,12 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
392
392
  {
393
393
  // Below
394
394
  gen: (offset) => {
395
- const lx = pt.cx - labelWidth / 2;
395
+ const lx = pt.cx - labelWidth2 / 2;
396
396
  const ly = pt.cy + offset;
397
- if (ly + labelHeight > chartBounds.bottom || lx < chartBounds.left || lx + labelWidth > chartBounds.right)
397
+ if (ly + labelHeight > chartBounds.bottom || lx < chartBounds.left || lx + labelWidth2 > chartBounds.right)
398
398
  return null;
399
399
  return {
400
- rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
400
+ rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
401
401
  textX: pt.cx,
402
402
  textY: ly + labelHeight / 2,
403
403
  anchor: "middle"
@@ -409,10 +409,10 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
409
409
  gen: (offset) => {
410
410
  const lx = pt.cx + offset;
411
411
  const ly = pt.cy - labelHeight / 2;
412
- if (lx + labelWidth > chartBounds.right || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
412
+ if (lx + labelWidth2 > chartBounds.right || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
413
413
  return null;
414
414
  return {
415
- rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
415
+ rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
416
416
  textX: lx,
417
417
  textY: pt.cy,
418
418
  anchor: "start"
@@ -422,13 +422,13 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
422
422
  {
423
423
  // Left
424
424
  gen: (offset) => {
425
- const lx = pt.cx - offset - labelWidth;
425
+ const lx = pt.cx - offset - labelWidth2;
426
426
  const ly = pt.cy - labelHeight / 2;
427
427
  if (lx < chartBounds.left || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
428
428
  return null;
429
429
  return {
430
- rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
431
- textX: lx + labelWidth,
430
+ rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
431
+ textX: lx + labelWidth2,
432
432
  textY: pt.cy,
433
433
  anchor: "end"
434
434
  };
@@ -478,10 +478,10 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
478
478
  }
479
479
  }
480
480
  if (!best) {
481
- const lx = pt.cx - labelWidth / 2;
481
+ const lx = pt.cx - labelWidth2 / 2;
482
482
  const ly = pt.cy - minGap - labelHeight;
483
483
  best = {
484
- rect: { x: lx, y: ly, w: labelWidth, h: labelHeight },
484
+ rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
485
485
  textX: pt.cx,
486
486
  textY: ly + labelHeight / 2,
487
487
  anchor: "middle",
@@ -858,6 +858,9 @@ var init_reserved_key_registry = __esm({
858
858
  "value",
859
859
  "label",
860
860
  "style"
861
+ // `surface:` was removed in the 2026-06-02 defaults-on review — it is no longer
862
+ // a recognized metadata key (the route/edge surface feature was cut; §24B.7).
863
+ // A stray `surface: water` is no longer captured as a reserved key.
861
864
  ]);
862
865
  ORG_REGISTRY = staticRegistry([
863
866
  "color",
@@ -1920,77 +1923,266 @@ function getSegmentColors(palette, count) {
1920
1923
  (_, i) => hslToHex(Math.round((startHue + i * step) % 360), avgS, avgL)
1921
1924
  );
1922
1925
  }
1926
+ function politicalTints(palette, count, isDark) {
1927
+ if (count <= 0) return [];
1928
+ const base = isDark ? palette.surface : palette.bg;
1929
+ const c = palette.colors;
1930
+ const swatches = [
1931
+ .../* @__PURE__ */ new Set([
1932
+ c.green,
1933
+ c.yellow,
1934
+ c.orange,
1935
+ c.purple,
1936
+ c.red,
1937
+ c.teal,
1938
+ c.cyan,
1939
+ c.blue
1940
+ ])
1941
+ ];
1942
+ const bands = isDark ? POLITICAL_TINT_BANDS.dark : POLITICAL_TINT_BANDS.light;
1943
+ const out = [];
1944
+ for (const pct of bands) {
1945
+ if (out.length >= count) break;
1946
+ for (const s of swatches) out.push(mix(s, base, pct));
1947
+ }
1948
+ return out.slice(0, count);
1949
+ }
1950
+ var POLITICAL_TINT_BANDS;
1923
1951
  var init_color_utils = __esm({
1924
1952
  "src/palettes/color-utils.ts"() {
1925
1953
  "use strict";
1954
+ POLITICAL_TINT_BANDS = {
1955
+ light: [32, 48, 64, 80],
1956
+ dark: [44, 58, 72, 86]
1957
+ };
1926
1958
  }
1927
1959
  });
1928
1960
 
1929
- // src/palettes/bold.ts
1930
- var boldPalette;
1931
- var init_bold = __esm({
1932
- "src/palettes/bold.ts"() {
1961
+ // src/palettes/atlas.ts
1962
+ var atlasPalette;
1963
+ var init_atlas = __esm({
1964
+ "src/palettes/atlas.ts"() {
1933
1965
  "use strict";
1934
1966
  init_registry();
1935
- boldPalette = {
1936
- id: "bold",
1937
- name: "Bold",
1967
+ atlasPalette = {
1968
+ id: "atlas",
1969
+ name: "Atlas",
1938
1970
  light: {
1939
- bg: "#ffffff",
1940
- surface: "#f0f0f0",
1941
- overlay: "#f0f0f0",
1942
- border: "#cccccc",
1943
- text: "#000000",
1944
- textMuted: "#666666",
1945
- textOnFillLight: "#ffffff",
1946
- textOnFillDark: "#000000",
1947
- primary: "#0000ff",
1948
- secondary: "#ff00ff",
1949
- accent: "#00cccc",
1950
- destructive: "#ff0000",
1971
+ bg: "#f3ead3",
1972
+ // warm manila / parchment
1973
+ surface: "#ece0c0",
1974
+ // deeper paper (cards, panels)
1975
+ overlay: "#e8dab8",
1976
+ // popovers, dropdowns
1977
+ border: "#bcaa86",
1978
+ // muted sepia rule line
1979
+ text: "#463a26",
1980
+ // aged sepia-brown ink
1981
+ textMuted: "#7a6a4f",
1982
+ // faded annotation ink
1983
+ textOnFillLight: "#f7f1de",
1984
+ // parchment (light text on dark fills)
1985
+ textOnFillDark: "#3a2e1c",
1986
+ // deep ink (dark text on light fills)
1987
+ primary: "#5b7a99",
1988
+ // pull-down map ocean (steel-blue)
1989
+ secondary: "#7e9a6f",
1990
+ // lowland sage / celadon
1991
+ accent: "#b07f7c",
1992
+ // dusty rose
1993
+ destructive: "#b25a45",
1994
+ // brick / terracotta
1951
1995
  colors: {
1952
- red: "#ff0000",
1953
- orange: "#ff8000",
1954
- yellow: "#ffcc00",
1955
- green: "#00cc00",
1956
- blue: "#0000ff",
1957
- purple: "#cc00cc",
1958
- teal: "#008080",
1959
- cyan: "#00cccc",
1960
- gray: "#808080",
1961
- black: "#000000",
1962
- white: "#f0f0f0"
1996
+ red: "#bf6a52",
1997
+ // terracotta brick
1998
+ orange: "#cf9a5c",
1999
+ // map tan / ochre
2000
+ yellow: "#cdb35e",
2001
+ // straw / muted lemon
2002
+ green: "#7e9a6f",
2003
+ // sage / celadon lowland
2004
+ blue: "#5b7a99",
2005
+ // steel-blue ocean
2006
+ purple: "#9a7fa6",
2007
+ // dusty lilac / mauve
2008
+ teal: "#6fa094",
2009
+ // muted seafoam
2010
+ cyan: "#79a7b5",
2011
+ // shallow-water blue
2012
+ gray: "#8a7d68",
2013
+ // warm taupe
2014
+ black: "#463a26",
2015
+ // ink
2016
+ white: "#ece0c0"
2017
+ // paper
1963
2018
  }
1964
2019
  },
1965
2020
  dark: {
1966
- bg: "#000000",
1967
- surface: "#111111",
1968
- overlay: "#1a1a1a",
1969
- border: "#333333",
1970
- text: "#ffffff",
1971
- textMuted: "#aaaaaa",
1972
- textOnFillLight: "#ffffff",
1973
- textOnFillDark: "#000000",
1974
- primary: "#00ccff",
1975
- secondary: "#ff00ff",
1976
- accent: "#ffff00",
1977
- destructive: "#ff0000",
2021
+ bg: "#1e2a33",
2022
+ // deep map ocean (night globe)
2023
+ surface: "#27353f",
2024
+ // raised ocean
2025
+ overlay: "#2e3d48",
2026
+ // popovers, dropdowns
2027
+ border: "#3d4f5c",
2028
+ // depth-contour line
2029
+ text: "#e8dcc0",
2030
+ // parchment ink, inverted
2031
+ textMuted: "#a89a7d",
2032
+ // faded label
2033
+ textOnFillLight: "#f7f1de",
2034
+ // parchment
2035
+ textOnFillDark: "#1a242c",
2036
+ // deep ocean ink
2037
+ primary: "#7ba0bf",
2038
+ // brighter ocean
2039
+ secondary: "#9bb588",
2040
+ // sage, lifted
2041
+ accent: "#cf9a96",
2042
+ // dusty rose, lifted
2043
+ destructive: "#c9745c",
2044
+ // brick, lifted
2045
+ colors: {
2046
+ red: "#cf7a60",
2047
+ // terracotta
2048
+ orange: "#d9a96a",
2049
+ // tan / ochre
2050
+ yellow: "#d8c074",
2051
+ // straw
2052
+ green: "#9bb588",
2053
+ // sage lowland
2054
+ blue: "#7ba0bf",
2055
+ // ocean
2056
+ purple: "#b59ac0",
2057
+ // lilac / mauve
2058
+ teal: "#85b3a6",
2059
+ // seafoam
2060
+ cyan: "#92bccb",
2061
+ // shallow-water blue
2062
+ gray: "#9a8d76",
2063
+ // warm taupe
2064
+ black: "#27353f",
2065
+ // raised ocean
2066
+ white: "#e8dcc0"
2067
+ // parchment
2068
+ }
2069
+ }
2070
+ };
2071
+ registerPalette(atlasPalette);
2072
+ }
2073
+ });
2074
+
2075
+ // src/palettes/blueprint.ts
2076
+ var blueprintPalette;
2077
+ var init_blueprint = __esm({
2078
+ "src/palettes/blueprint.ts"() {
2079
+ "use strict";
2080
+ init_registry();
2081
+ blueprintPalette = {
2082
+ id: "blueprint",
2083
+ name: "Blueprint",
2084
+ light: {
2085
+ bg: "#f4f8fb",
2086
+ // pale drafting white (faint cyan)
2087
+ surface: "#e6eef4",
2088
+ // drafting panel
2089
+ overlay: "#dde9f1",
2090
+ // popovers, dropdowns
2091
+ border: "#aac3d6",
2092
+ // pale blue grid line
2093
+ text: "#123a5e",
2094
+ // blueprint navy ink
2095
+ textMuted: "#4f7390",
2096
+ // faint draft note
2097
+ textOnFillLight: "#f4f8fb",
2098
+ // drafting white
2099
+ textOnFillDark: "#0c2f4d",
2100
+ // deep blueprint navy
2101
+ primary: "#1f5e8c",
2102
+ // blueprint blue
2103
+ secondary: "#5b7d96",
2104
+ // steel
2105
+ accent: "#b08a3e",
2106
+ // draftsman's ochre highlight
2107
+ destructive: "#c0504d",
2108
+ // correction red
1978
2109
  colors: {
1979
- red: "#ff0000",
1980
- orange: "#ff8000",
1981
- yellow: "#ffff00",
1982
- green: "#00ff00",
1983
- blue: "#0066ff",
1984
- purple: "#ff00ff",
1985
- teal: "#00cccc",
1986
- cyan: "#00ffff",
1987
- gray: "#808080",
1988
- black: "#111111",
1989
- white: "#ffffff"
2110
+ red: "#c25a4e",
2111
+ // correction red
2112
+ orange: "#c2823e",
2113
+ // ochre
2114
+ yellow: "#c2a843",
2115
+ // pencil gold
2116
+ green: "#4f8a6b",
2117
+ // drafting green
2118
+ blue: "#1f5e8c",
2119
+ // blueprint blue
2120
+ purple: "#6f5e96",
2121
+ // indigo pencil
2122
+ teal: "#3a8a8a",
2123
+ // teal
2124
+ cyan: "#3f8fb5",
2125
+ // cyan
2126
+ gray: "#7e8e98",
2127
+ // graphite
2128
+ black: "#123a5e",
2129
+ // navy ink
2130
+ white: "#e6eef4"
2131
+ // panel
2132
+ }
2133
+ },
2134
+ dark: {
2135
+ bg: "#103a5e",
2136
+ // deep blueprint blue (cyanotype ground)
2137
+ surface: "#16466e",
2138
+ // raised sheet
2139
+ overlay: "#1c5180",
2140
+ // popovers, dropdowns
2141
+ border: "#3a6f96",
2142
+ // grid line
2143
+ text: "#eaf2f8",
2144
+ // chalk white
2145
+ textMuted: "#9fc0d6",
2146
+ // faint chalk note
2147
+ textOnFillLight: "#eaf2f8",
2148
+ // chalk white
2149
+ textOnFillDark: "#0c2f4d",
2150
+ // deep blueprint navy
2151
+ primary: "#7fb8d8",
2152
+ // chalk cyan
2153
+ secondary: "#9fb8c8",
2154
+ // pale steel
2155
+ accent: "#d8c27a",
2156
+ // chalk amber
2157
+ destructive: "#e08a7a",
2158
+ // chalk correction red
2159
+ colors: {
2160
+ red: "#e0907e",
2161
+ // chalk red
2162
+ orange: "#e0ab78",
2163
+ // chalk amber
2164
+ yellow: "#e3d089",
2165
+ // chalk gold
2166
+ green: "#93c79e",
2167
+ // chalk green
2168
+ blue: "#8ec3e0",
2169
+ // chalk cyan-blue
2170
+ purple: "#b6a6d8",
2171
+ // chalk indigo
2172
+ teal: "#84c7c2",
2173
+ // chalk teal
2174
+ cyan: "#9fd6e0",
2175
+ // chalk cyan
2176
+ gray: "#aebecb",
2177
+ // chalk graphite
2178
+ black: "#16466e",
2179
+ // raised sheet
2180
+ white: "#eaf2f8"
2181
+ // chalk white
1990
2182
  }
1991
2183
  }
1992
2184
  };
1993
- registerPalette(boldPalette);
2185
+ registerPalette(blueprintPalette);
1994
2186
  }
1995
2187
  });
1996
2188
 
@@ -2487,6 +2679,120 @@ var init_rose_pine = __esm({
2487
2679
  }
2488
2680
  });
2489
2681
 
2682
+ // src/palettes/slate.ts
2683
+ var slatePalette;
2684
+ var init_slate = __esm({
2685
+ "src/palettes/slate.ts"() {
2686
+ "use strict";
2687
+ init_registry();
2688
+ slatePalette = {
2689
+ id: "slate",
2690
+ name: "Slate",
2691
+ light: {
2692
+ bg: "#ffffff",
2693
+ // clean slide white
2694
+ surface: "#f3f5f8",
2695
+ // light cool-gray panel
2696
+ overlay: "#eaeef3",
2697
+ // popovers, dropdowns
2698
+ border: "#d4dae1",
2699
+ // hairline rule
2700
+ text: "#1f2933",
2701
+ // near-black slate (softer than pure black)
2702
+ textMuted: "#5b6672",
2703
+ // secondary label
2704
+ textOnFillLight: "#ffffff",
2705
+ // light text on dark fills
2706
+ textOnFillDark: "#1f2933",
2707
+ // dark text on light fills
2708
+ primary: "#3b6ea5",
2709
+ // confident corporate blue
2710
+ secondary: "#5b6672",
2711
+ // slate gray
2712
+ accent: "#3a9188",
2713
+ // muted teal accent
2714
+ destructive: "#c0504d",
2715
+ // brick red
2716
+ colors: {
2717
+ red: "#c0504d",
2718
+ // brick
2719
+ orange: "#cc7a33",
2720
+ // muted amber
2721
+ yellow: "#c9a227",
2722
+ // gold (not neon)
2723
+ green: "#5b9357",
2724
+ // forest / sage
2725
+ blue: "#3b6ea5",
2726
+ // corporate blue
2727
+ purple: "#7d5ba6",
2728
+ // muted violet
2729
+ teal: "#3a9188",
2730
+ // teal
2731
+ cyan: "#4f96c4",
2732
+ // steel cyan
2733
+ gray: "#7e8a97",
2734
+ // cool gray
2735
+ black: "#1f2933",
2736
+ // slate ink
2737
+ white: "#f3f5f8"
2738
+ // panel
2739
+ }
2740
+ },
2741
+ dark: {
2742
+ bg: "#161b22",
2743
+ // deep slate (keynote dark)
2744
+ surface: "#202833",
2745
+ // raised panel
2746
+ overlay: "#29323e",
2747
+ // popovers, dropdowns
2748
+ border: "#38424f",
2749
+ // divider
2750
+ text: "#e6eaef",
2751
+ // off-white
2752
+ textMuted: "#9aa5b1",
2753
+ // secondary label
2754
+ textOnFillLight: "#ffffff",
2755
+ // light text on dark fills
2756
+ textOnFillDark: "#161b22",
2757
+ // dark text on light fills
2758
+ primary: "#5b9bd5",
2759
+ // lifted corporate blue
2760
+ secondary: "#8593a3",
2761
+ // slate gray, lifted
2762
+ accent: "#45b3a3",
2763
+ // teal, lifted
2764
+ destructive: "#e07b6e",
2765
+ // brick, lifted
2766
+ colors: {
2767
+ red: "#e07b6e",
2768
+ // brick
2769
+ orange: "#e0975a",
2770
+ // amber
2771
+ yellow: "#d9bd5a",
2772
+ // gold
2773
+ green: "#74b56e",
2774
+ // forest / sage
2775
+ blue: "#5b9bd5",
2776
+ // corporate blue
2777
+ purple: "#a585c9",
2778
+ // violet
2779
+ teal: "#45b3a3",
2780
+ // teal
2781
+ cyan: "#62b0d9",
2782
+ // steel cyan
2783
+ gray: "#95a1ae",
2784
+ // cool gray
2785
+ black: "#202833",
2786
+ // raised panel
2787
+ white: "#e6eaef"
2788
+ // off-white
2789
+ }
2790
+ }
2791
+ };
2792
+ registerPalette(slatePalette);
2793
+ }
2794
+ });
2795
+
2490
2796
  // src/palettes/solarized.ts
2491
2797
  var solarizedPalette;
2492
2798
  var init_solarized = __esm({
@@ -2582,6 +2888,120 @@ var init_solarized = __esm({
2582
2888
  }
2583
2889
  });
2584
2890
 
2891
+ // src/palettes/tidewater.ts
2892
+ var tidewaterPalette;
2893
+ var init_tidewater = __esm({
2894
+ "src/palettes/tidewater.ts"() {
2895
+ "use strict";
2896
+ init_registry();
2897
+ tidewaterPalette = {
2898
+ id: "tidewater",
2899
+ name: "Tidewater",
2900
+ light: {
2901
+ bg: "#eceff0",
2902
+ // weathered sea-mist paper
2903
+ surface: "#e0e4e3",
2904
+ // worn deck panel
2905
+ overlay: "#dadfdf",
2906
+ // popovers, dropdowns
2907
+ border: "#a9b2b3",
2908
+ // muted slate rule
2909
+ text: "#18313f",
2910
+ // ship's-log navy ink
2911
+ textMuted: "#51636b",
2912
+ // faded log entry
2913
+ textOnFillLight: "#f3f5f3",
2914
+ // weathered white
2915
+ textOnFillDark: "#162c38",
2916
+ // deep navy
2917
+ primary: "#1f4e6b",
2918
+ // deep-sea navy
2919
+ secondary: "#b08a4f",
2920
+ // rope / manila tan
2921
+ accent: "#c69a3e",
2922
+ // brass
2923
+ destructive: "#c1433a",
2924
+ // signal-flag red
2925
+ colors: {
2926
+ red: "#c1433a",
2927
+ // signal-flag red
2928
+ orange: "#cc7a38",
2929
+ // weathered amber
2930
+ yellow: "#d6bf5a",
2931
+ // brass gold
2932
+ green: "#4f8a6b",
2933
+ // sea-glass green
2934
+ blue: "#1f4e6b",
2935
+ // deep-sea navy
2936
+ purple: "#6a5a8c",
2937
+ // twilight harbor
2938
+ teal: "#3d8c8c",
2939
+ // sea-glass teal
2940
+ cyan: "#4f9bb5",
2941
+ // shallow water
2942
+ gray: "#8a8d86",
2943
+ // driftwood gray
2944
+ black: "#18313f",
2945
+ // navy ink
2946
+ white: "#e0e4e3"
2947
+ // deck panel
2948
+ }
2949
+ },
2950
+ dark: {
2951
+ bg: "#0f2230",
2952
+ // night-harbor deep sea
2953
+ surface: "#16303f",
2954
+ // raised hull
2955
+ overlay: "#1d3a4a",
2956
+ // popovers, dropdowns
2957
+ border: "#2c4856",
2958
+ // rigging line
2959
+ text: "#e6ebe8",
2960
+ // weathered white
2961
+ textMuted: "#9aaab0",
2962
+ // faded label
2963
+ textOnFillLight: "#f3f5f3",
2964
+ // weathered white
2965
+ textOnFillDark: "#0f2230",
2966
+ // deep sea
2967
+ primary: "#4f9bc4",
2968
+ // lifted sea blue
2969
+ secondary: "#c9a46a",
2970
+ // rope tan, lifted
2971
+ accent: "#d9b25a",
2972
+ // brass, lifted
2973
+ destructive: "#e06a5e",
2974
+ // signal red, lifted
2975
+ colors: {
2976
+ red: "#e06a5e",
2977
+ // signal-flag red
2978
+ orange: "#df9a52",
2979
+ // amber
2980
+ yellow: "#e0c662",
2981
+ // brass gold
2982
+ green: "#6fb58c",
2983
+ // sea-glass green
2984
+ blue: "#4f9bc4",
2985
+ // sea blue
2986
+ purple: "#9486bf",
2987
+ // twilight harbor
2988
+ teal: "#5cb0ac",
2989
+ // sea-glass teal
2990
+ cyan: "#62b4cf",
2991
+ // shallow water
2992
+ gray: "#9aa39c",
2993
+ // driftwood gray
2994
+ black: "#16303f",
2995
+ // raised hull
2996
+ white: "#e6ebe8"
2997
+ // weathered white
2998
+ }
2999
+ }
3000
+ };
3001
+ registerPalette(tidewaterPalette);
3002
+ }
3003
+ });
3004
+
2585
3005
  // src/palettes/tokyo-night.ts
2586
3006
  var tokyoNightPalette;
2587
3007
  var init_tokyo_night = __esm({
@@ -2857,7 +3277,8 @@ var init_monokai = __esm({
2857
3277
  // src/palettes/index.ts
2858
3278
  var palettes_exports = {};
2859
3279
  __export(palettes_exports, {
2860
- boldPalette: () => boldPalette,
3280
+ atlasPalette: () => atlasPalette,
3281
+ blueprintPalette: () => blueprintPalette,
2861
3282
  catppuccinPalette: () => catppuccinPalette,
2862
3283
  contrastText: () => contrastText,
2863
3284
  draculaPalette: () => draculaPalette,
@@ -2878,7 +3299,9 @@ __export(palettes_exports, {
2878
3299
  rosePinePalette: () => rosePinePalette,
2879
3300
  shade: () => shade,
2880
3301
  shapeFill: () => shapeFill,
3302
+ slatePalette: () => slatePalette,
2881
3303
  solarizedPalette: () => solarizedPalette,
3304
+ tidewaterPalette: () => tidewaterPalette,
2882
3305
  tint: () => tint,
2883
3306
  tokyoNightPalette: () => tokyoNightPalette
2884
3307
  });
@@ -2888,17 +3311,21 @@ var init_palettes = __esm({
2888
3311
  "use strict";
2889
3312
  init_registry();
2890
3313
  init_color_utils();
2891
- init_bold();
3314
+ init_atlas();
3315
+ init_blueprint();
2892
3316
  init_catppuccin();
2893
3317
  init_gruvbox();
2894
3318
  init_nord();
2895
3319
  init_one_dark();
2896
3320
  init_rose_pine();
3321
+ init_slate();
2897
3322
  init_solarized();
3323
+ init_tidewater();
2898
3324
  init_tokyo_night();
2899
3325
  init_dracula();
2900
3326
  init_monokai();
2901
- init_bold();
3327
+ init_atlas();
3328
+ init_blueprint();
2902
3329
  init_catppuccin();
2903
3330
  init_dracula();
2904
3331
  init_gruvbox();
@@ -2906,9 +3333,15 @@ var init_palettes = __esm({
2906
3333
  init_nord();
2907
3334
  init_one_dark();
2908
3335
  init_rose_pine();
3336
+ init_slate();
2909
3337
  init_solarized();
3338
+ init_tidewater();
2910
3339
  init_tokyo_night();
2911
3340
  palettes = {
3341
+ atlas: atlasPalette,
3342
+ blueprint: blueprintPalette,
3343
+ slate: slatePalette,
3344
+ tidewater: tidewaterPalette,
2912
3345
  nord: nordPalette,
2913
3346
  catppuccin: catppuccinPalette,
2914
3347
  solarized: solarizedPalette,
@@ -2917,8 +3350,7 @@ var init_palettes = __esm({
2917
3350
  oneDark: oneDarkPalette,
2918
3351
  rosePine: rosePinePalette,
2919
3352
  dracula: draculaPalette,
2920
- monokai: monokaiPalette,
2921
- bold: boldPalette
3353
+ monokai: monokaiPalette
2922
3354
  };
2923
3355
  }
2924
3356
  });
@@ -3428,6 +3860,9 @@ function controlsGroupCapsuleWidth(toggles) {
3428
3860
  }
3429
3861
  return w;
3430
3862
  }
3863
+ function isAppHostedControls(config, isExport) {
3864
+ return !isExport && config.controlsHost === "app" && !!config.controlsGroup && config.controlsGroup.toggles.length > 0;
3865
+ }
3431
3866
  function buildControlsGroupLayout(config, state) {
3432
3867
  const cg = config.controlsGroup;
3433
3868
  if (!cg || cg.toggles.length === 0) return void 0;
@@ -3481,6 +3916,7 @@ function buildControlsGroupLayout(config, state) {
3481
3916
  function computeLegendLayout(config, state, containerWidth) {
3482
3917
  const { groups, controls: configControls, mode } = config;
3483
3918
  const isExport = mode === "export";
3919
+ const gated = isAppHostedControls(config, isExport);
3484
3920
  const activeGroupName = state.activeGroup?.toLowerCase() ?? null;
3485
3921
  if (isExport && !activeGroupName) {
3486
3922
  return {
@@ -3491,7 +3927,7 @@ function computeLegendLayout(config, state, containerWidth) {
3491
3927
  pills: []
3492
3928
  };
3493
3929
  }
3494
- const controlsGroupLayout = isExport ? void 0 : buildControlsGroupLayout(config, state);
3930
+ const controlsGroupLayout = isExport || gated ? void 0 : buildControlsGroupLayout(config, state);
3495
3931
  const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0 || !!g.gradient);
3496
3932
  if (visibleGroups.length === 0 && (!configControls || configControls.length === 0) && !controlsGroupLayout) {
3497
3933
  return {
@@ -8345,8 +8781,8 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
8345
8781
  const pt = points[i];
8346
8782
  const ptSize = pt.size ?? symbolSize;
8347
8783
  const minGap = ptSize / 2 + 4;
8348
- const labelWidth = pt.name.length * fontSize * 0.6 + 8;
8349
- const labelX = pt.px - labelWidth / 2;
8784
+ const labelWidth2 = pt.name.length * fontSize * 0.6 + 8;
8785
+ const labelX = pt.px - labelWidth2 / 2;
8350
8786
  let bestLabelY = 0;
8351
8787
  let bestOffset = Infinity;
8352
8788
  let placed = false;
@@ -8358,7 +8794,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
8358
8794
  const candidate = {
8359
8795
  x: labelX,
8360
8796
  y: labelY,
8361
- w: labelWidth,
8797
+ w: labelWidth2,
8362
8798
  h: labelHeight
8363
8799
  };
8364
8800
  let collision = false;
@@ -8400,7 +8836,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
8400
8836
  const labelRect = {
8401
8837
  x: labelX,
8402
8838
  y: bestLabelY,
8403
- w: labelWidth,
8839
+ w: labelWidth2,
8404
8840
  h: labelHeight
8405
8841
  };
8406
8842
  placedLabels.push(labelRect);
@@ -8436,7 +8872,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
8436
8872
  shape: {
8437
8873
  x: labelX - bgPad,
8438
8874
  y: bestLabelY - bgPad,
8439
- width: labelWidth + bgPad * 2,
8875
+ width: labelWidth2 + bgPad * 2,
8440
8876
  height: labelHeight + bgPad * 2
8441
8877
  },
8442
8878
  style: { fill: bg },
@@ -15872,10 +16308,6 @@ function parseMap(content) {
15872
16308
  handleTag(trimmed, lineNumber);
15873
16309
  continue;
15874
16310
  }
15875
- if ((firstWord === "muted" || firstWord === "natural") && trimmed === firstWord) {
15876
- handleDirective(firstWord, "", lineNumber);
15877
- continue;
15878
- }
15879
16311
  if (DIRECTIVE_SET.has(firstWord) && !trimmed.slice(firstWord.length).trimStart().startsWith(":")) {
15880
16312
  handleDirective(
15881
16313
  firstWord,
@@ -15922,24 +16354,6 @@ function parseMap(content) {
15922
16354
  pushWarning(line12, `Duplicate directive "${key}" \u2014 last value wins.`);
15923
16355
  };
15924
16356
  switch (key) {
15925
- case "region":
15926
- dup(d.region);
15927
- d.region = value;
15928
- break;
15929
- case "projection":
15930
- dup(d.projection);
15931
- if (value && ![
15932
- "equirectangular",
15933
- "natural-earth",
15934
- "albers-usa",
15935
- "mercator"
15936
- ].includes(value))
15937
- pushWarning(
15938
- line12,
15939
- `Unknown projection "${value}" (expected equirectangular | natural-earth | albers-usa | mercator).`
15940
- );
15941
- d.projection = value;
15942
- break;
15943
16357
  case "region-metric": {
15944
16358
  dup(d.regionMetric);
15945
16359
  const { label: rmLabel, colorName: rmColor } = peelTrailingColorName(value);
@@ -15955,91 +16369,43 @@ function parseMap(content) {
15955
16369
  dup(d.flowMetric);
15956
16370
  d.flowMetric = value;
15957
16371
  break;
15958
- case "scale":
15959
- dup(d.scale);
15960
- {
15961
- const s = parseScale(value, line12);
15962
- if (s) d.scale = s;
15963
- }
15964
- break;
15965
- case "region-labels":
15966
- dup(d.regionLabels);
15967
- if (value && !["full", "abbrev", "off"].includes(value))
15968
- pushWarning(
15969
- line12,
15970
- `Unknown region-labels "${value}" (expected full | abbrev | off).`
15971
- );
15972
- d.regionLabels = value;
15973
- break;
15974
- case "poi-labels":
15975
- dup(d.poiLabels);
15976
- if (value && !["off", "auto", "all"].includes(value))
15977
- pushWarning(
15978
- line12,
15979
- `Unknown poi-labels "${value}" (expected off | auto | all).`
15980
- );
15981
- d.poiLabels = value;
15982
- break;
15983
- case "default-country":
15984
- dup(d.defaultCountry);
15985
- d.defaultCountry = value;
15986
- break;
15987
- case "default-state":
15988
- dup(d.defaultState);
15989
- d.defaultState = value;
16372
+ case "locale":
16373
+ dup(d.locale);
16374
+ d.locale = value;
15990
16375
  break;
15991
16376
  case "active-tag":
15992
16377
  dup(d.activeTag);
15993
16378
  d.activeTag = value;
15994
16379
  break;
16380
+ case "caption":
16381
+ dup(d.caption);
16382
+ d.caption = value;
16383
+ break;
16384
+ // ── Cosmetic `no-*` opt-outs: bare flags, idempotent (mirror `no-legend`,
16385
+ // no dup warning); each defaults the feature ON when absent. ──
15995
16386
  case "no-legend":
15996
16387
  d.noLegend = true;
15997
16388
  break;
15998
- case "no-insets":
15999
- d.noInsets = true;
16389
+ case "no-coastline":
16390
+ d.noCoastline = true;
16000
16391
  break;
16001
- case "relief":
16002
- d.relief = true;
16392
+ case "no-relief":
16393
+ d.noRelief = true;
16003
16394
  break;
16004
- case "muted":
16005
- case "natural":
16006
- if (d.basemapStyle !== void 0 && d.basemapStyle !== key)
16007
- pushWarning(
16008
- line12,
16009
- `Conflicting basemap dress \u2014 "${d.basemapStyle}" then "${key}"; last wins.`
16010
- );
16011
- d.basemapStyle = key;
16395
+ case "no-context-labels":
16396
+ d.noContextLabels = true;
16012
16397
  break;
16013
- case "subtitle":
16014
- dup(d.subtitle);
16015
- d.subtitle = value;
16398
+ case "no-region-labels":
16399
+ d.noRegionLabels = true;
16016
16400
  break;
16017
- case "caption":
16018
- dup(d.caption);
16019
- d.caption = value;
16401
+ case "no-poi-labels":
16402
+ d.noPoiLabels = true;
16403
+ break;
16404
+ case "no-colorize":
16405
+ d.noColorize = true;
16020
16406
  break;
16021
16407
  }
16022
16408
  }
16023
- function parseScale(value, line12) {
16024
- const toks = value.split(/\s+/).filter(Boolean);
16025
- const min = Number(toks[0]);
16026
- const max = Number(toks[1]);
16027
- if (!Number.isFinite(min) || !Number.isFinite(max)) {
16028
- pushError(line12, `scale requires numeric <min> <max> (got "${value}").`);
16029
- return null;
16030
- }
16031
- const scale = { min, max };
16032
- if (toks[2] === "center") {
16033
- const c = Number(toks[3]);
16034
- if (Number.isFinite(c)) scale.center = c;
16035
- else
16036
- pushError(
16037
- line12,
16038
- `scale center requires a number (got "${toks[3] ?? ""}").`
16039
- );
16040
- }
16041
- return scale;
16042
- }
16043
16409
  function handleTag(trimmed, line12) {
16044
16410
  const m = matchTagBlockHeading(trimmed);
16045
16411
  if (!m) {
@@ -16239,13 +16605,15 @@ function parseMap(content) {
16239
16605
  pushError(line12, `Edge has an empty endpoint: "${trimmed}".`);
16240
16606
  continue;
16241
16607
  }
16242
- const meta = k === links.length - 1 ? lastSplit.meta : {};
16608
+ const isLast = k === links.length - 1;
16609
+ const meta = isLast ? lastSplit.meta : {};
16610
+ const style = links[k].style === "arc" ? "arc" : "straight";
16243
16611
  edges.push({
16244
16612
  from,
16245
16613
  to,
16246
16614
  ...links[k].label !== void 0 && { label: links[k].label },
16247
16615
  directed: links[k].directed,
16248
- style: links[k].style,
16616
+ style,
16249
16617
  meta,
16250
16618
  lineNumber: line12
16251
16619
  });
@@ -16331,22 +16699,19 @@ var init_parser12 = __esm({
16331
16699
  LEG_ARROW_RE = /^(-[^>]*?->|->|~[^>]*?~>|~>|--)\s+(.+)$/;
16332
16700
  AT_RE = /(^|[\s,])at\s*:/i;
16333
16701
  DIRECTIVE_SET = /* @__PURE__ */ new Set([
16334
- "region",
16335
- "projection",
16336
16702
  "region-metric",
16337
16703
  "poi-metric",
16338
16704
  "flow-metric",
16339
- "scale",
16340
- "region-labels",
16341
- "poi-labels",
16342
- "default-country",
16343
- "default-state",
16705
+ "locale",
16344
16706
  "active-tag",
16707
+ "caption",
16345
16708
  "no-legend",
16346
- "no-insets",
16347
- "relief",
16348
- "subtitle",
16349
- "caption"
16709
+ "no-coastline",
16710
+ "no-relief",
16711
+ "no-context-labels",
16712
+ "no-region-labels",
16713
+ "no-poi-labels",
16714
+ "no-colorize"
16350
16715
  ]);
16351
16716
  }
16352
16717
  });
@@ -24358,8 +24723,8 @@ function renderKanban(container, parsed, palette, isDark, options) {
24358
24723
  let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
24359
24724
  for (const meta of tagMeta) {
24360
24725
  cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(`${meta.label}: `);
24361
- const labelWidth = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
24362
- cg.append("text").attr("x", cx + sCardPaddingX + labelWidth).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
24726
+ const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
24727
+ cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
24363
24728
  metaY += sCardMetaLineHeight;
24364
24729
  }
24365
24730
  for (const detail of card.details) {
@@ -24703,8 +25068,8 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
24703
25068
  let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
24704
25069
  for (const meta of tagMeta) {
24705
25070
  cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", palette.textMuted).text(`${meta.label}: `);
24706
- const labelWidth = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
24707
- cg.append("text").attr("x", cx + sCardPaddingX + labelWidth).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
25071
+ const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
25072
+ cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
24708
25073
  metaY += sCardMetaLineHeight;
24709
25074
  }
24710
25075
  for (const detail of card.details) {
@@ -25538,8 +25903,8 @@ function classifyEREntities(tables, relationships) {
25538
25903
  }
25539
25904
  }
25540
25905
  const mmParticipants = /* @__PURE__ */ new Set();
25541
- for (const [id, neighbors] of tableStarNeighbors) {
25542
- if (neighbors.size >= 2) mmParticipants.add(id);
25906
+ for (const [id, neighbors2] of tableStarNeighbors) {
25907
+ if (neighbors2.size >= 2) mmParticipants.add(id);
25543
25908
  }
25544
25909
  const indegreeValues = Object.values(indegreeMap);
25545
25910
  const mean = indegreeValues.reduce((a, b) => a + b, 0) / indegreeValues.length;
@@ -26212,7 +26577,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26212
26577
  controlsExpanded,
26213
26578
  onToggleDescriptions,
26214
26579
  onToggleControlsExpand,
26215
- exportMode = false
26580
+ exportMode = false,
26581
+ controlsHost
26216
26582
  } = options ?? {};
26217
26583
  d3Selection6.select(container).selectAll(":not([data-d3-tooltip])").remove();
26218
26584
  const width = exportDims?.width ?? container.clientWidth;
@@ -26230,7 +26596,11 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26230
26596
  const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
26231
26597
  const sTitleFontSize = sctx.text(TITLE_FONT_SIZE);
26232
26598
  const sTitleY = sctx.structural(TITLE_Y);
26233
- const sLegendHeight = sctx.structural(
26599
+ const reserveHasDescriptions = parsed.nodes.some(
26600
+ (n) => n.description && n.description.length > 0
26601
+ );
26602
+ const willRenderLegend = parsed.tagGroups.length > 0 || reserveHasDescriptions && controlsHost !== "app";
26603
+ const sLegendHeight = willRenderLegend ? sctx.structural(
26234
26604
  getMaxLegendReservedHeight(
26235
26605
  {
26236
26606
  groups: parsed.tagGroups,
@@ -26239,7 +26609,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26239
26609
  },
26240
26610
  width
26241
26611
  )
26242
- );
26612
+ ) : 0;
26243
26613
  const activeGroup = resolveActiveTagGroup(
26244
26614
  parsed.tagGroups,
26245
26615
  parsed.options["active-tag"],
@@ -26554,10 +26924,10 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26554
26924
  const hasDescriptions = parsed.nodes.some(
26555
26925
  (n) => n.description && n.description.length > 0
26556
26926
  );
26557
- const hasLegend = parsed.tagGroups.length > 0 || hasDescriptions;
26927
+ const hasLegend = parsed.tagGroups.length > 0 || hasDescriptions && controlsHost !== "app";
26558
26928
  if (hasLegend) {
26559
26929
  let controlsGroup;
26560
- if (hasDescriptions && onToggleDescriptions) {
26930
+ if (hasDescriptions && (onToggleDescriptions || controlsHost === "app")) {
26561
26931
  controlsGroup = {
26562
26932
  toggles: [
26563
26933
  {
@@ -26575,7 +26945,14 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26575
26945
  groups: parsed.tagGroups,
26576
26946
  position: { placement: "top-center", titleRelation: "below-title" },
26577
26947
  mode: exportMode ? "export" : "preview",
26578
- ...controlsGroup !== void 0 && { controlsGroup }
26948
+ // Keep inactive sibling tag groups visible as collapsed pills so the user
26949
+ // can click one to flip the active colouring dimension (preview only —
26950
+ // export shows just the active group). Without this, declaring a second
26951
+ // tag group (e.g. Team) leaves it invisible whenever another group is
26952
+ // active. The app's BoxesAndLinesPreview already wires pill clicks.
26953
+ showInactivePills: true,
26954
+ ...controlsGroup !== void 0 && { controlsGroup },
26955
+ ...controlsHost !== void 0 && { controlsHost }
26579
26956
  };
26580
26957
  const legendState = {
26581
26958
  activeGroup,
@@ -27822,8 +28199,9 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
27822
28199
  const containerHeight = exportDims?.height ?? (container.getBoundingClientRect().height || 600);
27823
28200
  d3Selection7.select(container).selectAll("*").remove();
27824
28201
  const svg = d3Selection7.select(container).append("svg").attr("width", containerWidth).attr("height", containerHeight).attr("viewBox", `0 0 ${containerWidth} ${containerHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY);
28202
+ const appHosted = options?.controlsHost === "app";
27825
28203
  const hasControls = !!options?.onToggleColorByDepth || !!options?.onToggleDescriptions;
27826
- const hasLegend = parsed.tagGroups.length > 0 || hasControls;
28204
+ const hasLegend = parsed.tagGroups.length > 0 || hasControls && !appHosted;
27827
28205
  const fixedLegend = !isExport && hasLegend;
27828
28206
  const legendReserve = fixedLegend ? getMaxLegendReservedHeight(
27829
28207
  {
@@ -27917,7 +28295,10 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
27917
28295
  }),
27918
28296
  position: { placement: "top-center", titleRelation: "below-title" },
27919
28297
  mode: options?.exportMode ? "export" : "preview",
27920
- ...controlsToggles !== void 0 && { controlsGroup: controlsToggles }
28298
+ ...controlsToggles !== void 0 && { controlsGroup: controlsToggles },
28299
+ ...options?.controlsHost !== void 0 && {
28300
+ controlsHost: options.controlsHost
28301
+ }
27921
28302
  };
27922
28303
  const legendState = {
27923
28304
  activeGroup: options?.colorByDepth ? null : activeTagGroup !== void 0 ? activeTagGroup : parsed.options["active-tag"] ?? null,
@@ -28360,8 +28741,8 @@ function computeFieldAlignX(children) {
28360
28741
  for (const child of children) {
28361
28742
  if (child.metadata["_labelField"] === "true" && child.children.length >= 2) {
28362
28743
  const labelEl = child.children[0];
28363
- const labelWidth = labelEl.label.length * CHAR_WIDTH5;
28364
- maxLabelWidth = Math.max(maxLabelWidth, labelWidth);
28744
+ const labelWidth2 = labelEl.label.length * CHAR_WIDTH5;
28745
+ maxLabelWidth = Math.max(maxLabelWidth, labelWidth2);
28365
28746
  labelFieldCount++;
28366
28747
  }
28367
28748
  }
@@ -33326,7 +33707,7 @@ function hasRoles(node) {
33326
33707
  function computeNodeWidth2(node, expanded, options) {
33327
33708
  const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
33328
33709
  const badgeLen = badgeVal > 0 ? `${badgeVal}x`.length + 2 : 0;
33329
- const labelWidth = (node.label.length + badgeLen) * CHAR_WIDTH7 + PADDING_X3;
33710
+ const labelWidth2 = (node.label.length + badgeLen) * CHAR_WIDTH7 + PADDING_X3;
33330
33711
  const allKeys = [];
33331
33712
  if (node.computedRps > 0) allKeys.push("RPS");
33332
33713
  if (expanded) {
@@ -33370,7 +33751,7 @@ function computeNodeWidth2(node, expanded, options) {
33370
33751
  allKeys.push("overflow");
33371
33752
  }
33372
33753
  }
33373
- if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth);
33754
+ if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
33374
33755
  const maxKeyLen = Math.max(...allKeys.map((k) => k.length));
33375
33756
  let maxRowWidth = 0;
33376
33757
  if (node.computedRps > 0) {
@@ -33458,7 +33839,7 @@ function computeNodeWidth2(node, expanded, options) {
33458
33839
  truncated.length * META_CHAR_WIDTH3 + PADDING_X3
33459
33840
  );
33460
33841
  }
33461
- return Math.max(MIN_NODE_WIDTH2, labelWidth, maxRowWidth + 20, descWidth);
33842
+ return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
33462
33843
  }
33463
33844
  function computeNodeHeight2(node, expanded, options) {
33464
33845
  const propCount = countDisplayProps(node, expanded, options);
@@ -35008,8 +35389,9 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
35008
35389
  }
35009
35390
  return groups;
35010
35391
  }
35011
- function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false) {
35392
+ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false, controlsHost) {
35012
35393
  if (legendGroups.length === 0 && !playback) return;
35394
+ const appHostedPlayback = controlsHost === "app" && !!playback;
35013
35395
  const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
35014
35396
  if (activeGroup) {
35015
35397
  legendG.attr("data-legend-active", activeGroup.toLowerCase());
@@ -35018,14 +35400,29 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
35018
35400
  name: g.name,
35019
35401
  entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
35020
35402
  }));
35021
- if (playback) {
35403
+ if (playback && !appHostedPlayback) {
35022
35404
  allGroups.push({ name: "Playback", entries: [] });
35023
35405
  }
35024
35406
  const legendConfig = {
35025
35407
  groups: allGroups,
35026
35408
  position: { placement: "top-center", titleRelation: "below-title" },
35027
35409
  mode: exportMode ? "export" : "preview",
35028
- showEmptyGroups: true
35410
+ showEmptyGroups: true,
35411
+ ...appHostedPlayback && {
35412
+ controlsHost: "app",
35413
+ controlsGroup: {
35414
+ toggles: [
35415
+ {
35416
+ id: "playback",
35417
+ type: "toggle",
35418
+ label: "Playback",
35419
+ active: true,
35420
+ onToggle: () => {
35421
+ }
35422
+ }
35423
+ ]
35424
+ }
35425
+ }
35029
35426
  };
35030
35427
  const legendState = { activeGroup };
35031
35428
  renderLegendD3(
@@ -35076,8 +35473,9 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
35076
35473
  }
35077
35474
  }
35078
35475
  }
35079
- function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes) {
35476
+ function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes, controlsHost) {
35080
35477
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
35478
+ const appHostedPlayback = controlsHost === "app" && !!playback;
35081
35479
  const ctx = ScaleContext.identity();
35082
35480
  const sc = buildScaledConstants(ctx);
35083
35481
  const legendGroups = computeInfraLegendGroups(
@@ -35086,7 +35484,7 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
35086
35484
  palette,
35087
35485
  layout.edges
35088
35486
  );
35089
- const hasLegend = legendGroups.length > 0 || !!playback;
35487
+ const hasLegend = legendGroups.length > 0 || !!playback && !appHostedPlayback;
35090
35488
  const fixedLegend = !exportMode && hasLegend;
35091
35489
  const legendDynamicH = hasLegend ? getMaxLegendReservedHeight(
35092
35490
  {
@@ -35230,7 +35628,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
35230
35628
  isDark,
35231
35629
  activeGroup ?? null,
35232
35630
  playback ?? void 0,
35233
- exportMode
35631
+ exportMode,
35632
+ controlsHost
35234
35633
  );
35235
35634
  legendSvg.selectAll(".infra-legend-group").style("pointer-events", "auto");
35236
35635
  } else {
@@ -35243,7 +35642,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
35243
35642
  isDark,
35244
35643
  activeGroup ?? null,
35245
35644
  playback ?? void 0,
35246
- exportMode
35645
+ exportMode,
35646
+ controlsHost
35247
35647
  );
35248
35648
  }
35249
35649
  }
@@ -43100,6 +43500,9 @@ function renderTechRadar(container, parsed, palette, isDark, onClickItem, export
43100
43500
  onToggle: (active) => options.onToggleListing(active)
43101
43501
  }
43102
43502
  ]
43503
+ },
43504
+ ...options.controlsHost !== void 0 && {
43505
+ controlsHost: options.controlsHost
43103
43506
  }
43104
43507
  };
43105
43508
  const legendState = {
@@ -44922,7 +45325,7 @@ function computeCycleLayout(parsed, options) {
44922
45325
  const circleNodes = parsed.options["circle-nodes"] === "true";
44923
45326
  const nodeDims = parsed.nodes.map((node) => {
44924
45327
  const hasDesc = !hideDescriptions && node.description.length > 0;
44925
- const labelWidth = Math.max(
45328
+ const labelWidth2 = Math.max(
44926
45329
  MIN_NODE_WIDTH4,
44927
45330
  node.label.length * LABEL_CHAR_W + NODE_PAD_X * 2
44928
45331
  );
@@ -44931,12 +45334,12 @@ function computeCycleLayout(parsed, options) {
44931
45334
  }
44932
45335
  if (!hasDesc) {
44933
45336
  return {
44934
- width: Math.min(MAX_NODE_WIDTH3, labelWidth),
45337
+ width: Math.min(MAX_NODE_WIDTH3, labelWidth2),
44935
45338
  height: PLAIN_NODE_HEIGHT,
44936
45339
  wrappedDesc: []
44937
45340
  };
44938
45341
  }
44939
- return chooseDescribedRectDims(node.description, labelWidth);
45342
+ return chooseDescribedRectDims(node.description, labelWidth2);
44940
45343
  });
44941
45344
  if (circleNodes) {
44942
45345
  const maxDiam = Math.max(...nodeDims.map((d) => d.width));
@@ -45132,10 +45535,10 @@ function computeCycleLayout(parsed, options) {
45132
45535
  scale
45133
45536
  };
45134
45537
  }
45135
- function chooseDescribedRectDims(description, labelWidth) {
45538
+ function chooseDescribedRectDims(description, labelWidth2) {
45136
45539
  const minW = Math.min(
45137
45540
  MAX_NODE_WIDTH3,
45138
- Math.max(MIN_NODE_WIDTH4, labelWidth, DESC_MIN_WIDTH)
45541
+ Math.max(MIN_NODE_WIDTH4, labelWidth2, DESC_MIN_WIDTH)
45139
45542
  );
45140
45543
  let best = null;
45141
45544
  let bestScore = Infinity;
@@ -45564,7 +45967,8 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
45564
45967
  const hideDescriptions = (renderOptions?.hideDescriptions ?? false) || parsed.options["no-descriptions"] === "true" || viewState?.hd === true;
45565
45968
  const showDescriptions = !hideDescriptions;
45566
45969
  const hasDescriptions = parsed.nodes.some((n) => n.description.length > 0) || parsed.edges.some((e) => e.description.length > 0);
45567
- const hasLegend = hasDescriptions && !!renderOptions?.onToggleDescriptions;
45970
+ const appHostedControls = renderOptions?.controlsHost === "app";
45971
+ const hasLegend = !appHostedControls && hasDescriptions && !!renderOptions?.onToggleDescriptions;
45568
45972
  const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
45569
45973
  const legendOffset = hasLegend ? sLegendHeight : 0;
45570
45974
  const layoutHeight = height - (showTitle ? sTitleAreaHeight : 0) - legendOffset;
@@ -45601,7 +46005,10 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
45601
46005
  groups: [],
45602
46006
  position: { placement: "top-center", titleRelation: "below-title" },
45603
46007
  mode: renderOptions?.exportMode ? "export" : "preview",
45604
- controlsGroup
46008
+ controlsGroup,
46009
+ ...renderOptions?.controlsHost !== void 0 && {
46010
+ controlsHost: renderOptions.controlsHost
46011
+ }
45605
46012
  };
45606
46013
  const legendState = {
45607
46014
  activeGroup: null,
@@ -45855,7 +46262,7 @@ var init_renderer15 = __esm({
45855
46262
  });
45856
46263
 
45857
46264
  // src/map/geo.ts
45858
- import { feature } from "topojson-client";
46265
+ import { feature, neighbors } from "topojson-client";
45859
46266
  import { geoBounds, geoArea } from "d3-geo";
45860
46267
  function geomObject(topo) {
45861
46268
  const key = Object.keys(topo.objects)[0];
@@ -45873,6 +46280,29 @@ function featureIndex(topo) {
45873
46280
  }
45874
46281
  return idx;
45875
46282
  }
46283
+ function buildAdjacency(topo) {
46284
+ const cached = adjacencyCache.get(topo);
46285
+ if (cached) return cached;
46286
+ const geometries = geomObject(topo).geometries;
46287
+ const nb = neighbors(geometries);
46288
+ const sets = /* @__PURE__ */ new Map();
46289
+ geometries.forEach((g, i) => {
46290
+ if (!g.type || g.type === "null") return;
46291
+ let set = sets.get(g.id);
46292
+ if (!set) {
46293
+ set = /* @__PURE__ */ new Set();
46294
+ sets.set(g.id, set);
46295
+ }
46296
+ for (const j of nb[i] ?? []) {
46297
+ const nid = geometries[j]?.id;
46298
+ if (nid && nid !== g.id) set.add(nid);
46299
+ }
46300
+ });
46301
+ const out = /* @__PURE__ */ new Map();
46302
+ for (const [iso, set] of sets) out.set(iso, [...set].sort());
46303
+ adjacencyCache.set(topo, out);
46304
+ return out;
46305
+ }
45876
46306
  function decodeFeatures(topo) {
45877
46307
  return geomObject(topo).geometries.map((g) => {
45878
46308
  const f = feature(topo, g);
@@ -46068,11 +46498,12 @@ function unionLongitudes(lons) {
46068
46498
  }
46069
46499
  return { west: pts[gapIdx], east: pts[gapIdx - 1] + 360 };
46070
46500
  }
46071
- var fold, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
46501
+ var fold, adjacencyCache, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
46072
46502
  var init_geo = __esm({
46073
46503
  "src/map/geo.ts"() {
46074
46504
  "use strict";
46075
46505
  fold = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
46506
+ adjacencyCache = /* @__PURE__ */ new WeakMap();
46076
46507
  EDGE_EPS = 1e-9;
46077
46508
  DETACH_GAP_DEG = 10;
46078
46509
  DETACH_AREA_FRAC = 0.25;
@@ -46093,6 +46524,12 @@ function looksUS(lat, lon) {
46093
46524
  if (lat < 15 || lat > 72) return false;
46094
46525
  return lon >= -180 && lon <= -64 || lon >= 172;
46095
46526
  }
46527
+ function looksNorthAmericaNeighbor(lat, lon) {
46528
+ return lat >= 14 && lat <= 72 && lon >= -141 && lon <= -52;
46529
+ }
46530
+ function isWholeSphere(bb) {
46531
+ return bb[0][0] <= -179 && bb[1][0] >= 179 && bb[0][1] <= -89 && bb[1][1] >= 89;
46532
+ }
46096
46533
  function resolveMap(parsed, data) {
46097
46534
  const diagnostics = [...parsed.diagnostics];
46098
46535
  const err = (line12, message, code) => {
@@ -46103,9 +46540,6 @@ function resolveMap(parsed, data) {
46103
46540
  };
46104
46541
  const result = {
46105
46542
  title: parsed.title,
46106
- ...parsed.directives.subtitle !== void 0 && {
46107
- subtitle: parsed.directives.subtitle
46108
- },
46109
46543
  ...parsed.directives.caption !== void 0 && {
46110
46544
  caption: parsed.directives.caption
46111
46545
  },
@@ -46115,7 +46549,7 @@ function resolveMap(parsed, data) {
46115
46549
  // renderer's job (step 4) — the resolver only carries `tags` + `tagGroups`
46116
46550
  // through; it never resolves a tag value to a palette color (#10).
46117
46551
  directives: { ...parsed.directives },
46118
- basemaps: { world: "coarse", subdivisions: [] },
46552
+ basemaps: { world: "detail", subdivisions: [] },
46119
46553
  regions: [],
46120
46554
  pois: [],
46121
46555
  edges: [],
@@ -46124,7 +46558,8 @@ function resolveMap(parsed, data) {
46124
46558
  [-180, -85],
46125
46559
  [180, 85]
46126
46560
  ],
46127
- projection: "natural-earth",
46561
+ projection: "equirectangular",
46562
+ poiFrameContainers: [],
46128
46563
  diagnostics,
46129
46564
  error: parsed.error
46130
46565
  };
@@ -46134,7 +46569,10 @@ function resolveMap(parsed, data) {
46134
46569
  ...[...countryIndex.values()].map((v) => v.name),
46135
46570
  ...[...usStateIndex.values()].map((v) => v.name)
46136
46571
  ];
46137
- const usScoped = parsed.directives.region === "us-states" || parsed.directives.defaultCountry?.toUpperCase() === "US" || parsed.regions.some((r) => {
46572
+ const localeRaw = parsed.directives.locale?.toUpperCase();
46573
+ const localeCountry = localeRaw ? localeRaw.split("-")[0] : void 0;
46574
+ const localeSubdivision = localeRaw && /^[A-Z]{2}-/.test(localeRaw) ? localeRaw : void 0;
46575
+ const usScoped = localeCountry === "US" || parsed.regions.some((r) => {
46138
46576
  const f = fold(r.name);
46139
46577
  return usStateIndex.has(f) && !countryIndex.has(f);
46140
46578
  }) || parsed.regions.some(
@@ -46285,7 +46723,7 @@ function resolveMap(parsed, data) {
46285
46723
  if (!scope)
46286
46724
  warn(
46287
46725
  line12,
46288
- `"${name}" is ambiguous \u2014 resolved to the most-populous match.`,
46726
+ `"${name}" is ambiguous \u2014 resolved to the most-populous match. Set a default with \`locale <ISO>\` (e.g. \`locale US\` / \`locale US-GA\`) to steer it.`,
46289
46727
  "W_MAP_AMBIGUOUS_NAME"
46290
46728
  );
46291
46729
  }
@@ -46298,17 +46736,21 @@ function resolveMap(parsed, data) {
46298
46736
  return fold(pos.name);
46299
46737
  };
46300
46738
  const poiCountries = [];
46301
- let anyNonUsPoi = false;
46739
+ let anyUsPoi = false;
46740
+ let anyNonNaPoi = false;
46302
46741
  const noteCountry = (iso) => {
46303
46742
  if (iso) {
46304
46743
  poiCountries.push(iso);
46305
- if (iso !== "US") anyNonUsPoi = true;
46744
+ if (iso === "US") anyUsPoi = true;
46745
+ if (iso !== "US" && iso !== "CA" && iso !== "MX") anyNonNaPoi = true;
46306
46746
  }
46307
46747
  };
46308
46748
  const deferred = [];
46309
46749
  for (const p of parsed.pois) {
46310
46750
  if (p.pos.kind === "coords") {
46311
- if (!looksUS(p.pos.lat, p.pos.lon)) anyNonUsPoi = true;
46751
+ if (looksUS(p.pos.lat, p.pos.lon)) anyUsPoi = true;
46752
+ else if (!looksNorthAmericaNeighbor(p.pos.lat, p.pos.lon))
46753
+ anyNonNaPoi = true;
46312
46754
  addResolvedPoi(p.pos.lat, p.pos.lon, p);
46313
46755
  continue;
46314
46756
  }
@@ -46326,14 +46768,15 @@ function resolveMap(parsed, data) {
46326
46768
  deferred.push(p);
46327
46769
  }
46328
46770
  }
46329
- const inferredCountry = parsed.directives.defaultCountry?.toUpperCase() ?? mostCommonCountry(regions, poiCountries) ?? void 0;
46771
+ const inferredCountry = localeCountry ?? mostCommonCountry(regions, poiCountries) ?? void 0;
46772
+ const inferredScope = localeSubdivision ?? inferredCountry;
46330
46773
  for (const p of deferred) {
46331
46774
  if (p.pos.kind !== "name") continue;
46332
46775
  const got = lookupName(
46333
46776
  p.pos.name,
46334
46777
  p.pos.scope,
46335
46778
  p.lineNumber,
46336
- inferredCountry,
46779
+ inferredScope,
46337
46780
  true
46338
46781
  );
46339
46782
  if (got.kind === "ok") {
@@ -46403,7 +46846,8 @@ function resolveMap(parsed, data) {
46403
46846
  const meta = sizeValue !== void 0 ? { value: sizeValue } : {};
46404
46847
  if (pos.kind === "coords") {
46405
46848
  const id = alias ? fold(alias) : `@${pos.lat},${pos.lon}`;
46406
- if (!looksUS(pos.lat, pos.lon)) anyNonUsPoi = true;
46849
+ if (looksUS(pos.lat, pos.lon)) anyUsPoi = true;
46850
+ else if (!looksNorthAmericaNeighbor(pos.lat, pos.lon)) anyNonNaPoi = true;
46407
46851
  if (!registry.has(id)) {
46408
46852
  registerPoi(
46409
46853
  id,
@@ -46426,7 +46870,7 @@ function resolveMap(parsed, data) {
46426
46870
  if (registry.has(f)) return f;
46427
46871
  const aliased = declaredByName.get(f);
46428
46872
  if (aliased) return aliased;
46429
- const got = lookupName(pos.name, pos.scope, line12, inferredCountry, true);
46873
+ const got = lookupName(pos.name, pos.scope, line12, inferredScope, true);
46430
46874
  if (got.kind !== "ok") return null;
46431
46875
  noteCountry(got.iso);
46432
46876
  registerPoi(
@@ -46483,9 +46927,12 @@ function resolveMap(parsed, data) {
46483
46927
  }
46484
46928
  routes.push({ stopIds, legs, lineNumber: rt.lineNumber });
46485
46929
  }
46930
+ const hasUsContent = usSubdivisionReferenced || anyUsPoi || localeCountry === "US";
46931
+ const usOriented = !anyNonNaPoi && !regions.some(
46932
+ (r) => r.layer === "country" && !["US", "CA", "MX"].includes(r.iso)
46933
+ ) && hasUsContent;
46486
46934
  const subdivisions = [];
46487
- if (usSubdivisionReferenced || parsed.directives.region === "us-states")
46488
- subdivisions.push("us-states");
46935
+ if (usSubdivisionReferenced || usOriented) subdivisions.push("us-states");
46489
46936
  const regionBoxes = [];
46490
46937
  for (const ref of referencedRegionIds) {
46491
46938
  const bb = featureBbox(data.usStates, ref.id);
@@ -46503,17 +46950,51 @@ function resolveMap(parsed, data) {
46503
46950
  [-180, -85],
46504
46951
  [180, 85]
46505
46952
  ];
46506
- let extent2 = unioned ? pad(unioned, PAD_FRACTION) : DEFAULT_EXTENT;
46953
+ const basePad = regions.length > 0 ? REGION_PAD_FRACTION : PAD_FRACTION;
46954
+ let extent2 = unioned ? pad(unioned, basePad) : DEFAULT_EXTENT;
46955
+ const isPoiOnly = pois.length > 0 && regions.length === 0;
46956
+ const containerRegionIds = [];
46957
+ if (isPoiOnly) {
46958
+ const countries = decodeFeatures(data.worldDetail);
46959
+ const states = decodeFeatures(data.usStates);
46960
+ const seen = /* @__PURE__ */ new Set();
46961
+ const containerBoxes = [];
46962
+ for (const p of pois) {
46963
+ const { country, state } = regionAt([p.lon, p.lat], countries, states);
46964
+ const id = state?.iso ?? country?.iso;
46965
+ if (!id || seen.has(id)) continue;
46966
+ seen.add(id);
46967
+ containerRegionIds.push(id);
46968
+ const bb = state ? featureBbox(data.usStates, id) : featureBboxPrimary(data.worldCoarse, id);
46969
+ if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
46970
+ }
46971
+ const containerUnion = unionExtent(containerBoxes, points);
46972
+ if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
46973
+ }
46974
+ if (isPoiOnly) {
46975
+ const cx = (extent2[0][0] + extent2[1][0]) / 2;
46976
+ const cy = (extent2[0][1] + extent2[1][1]) / 2;
46977
+ const lon = extent2[1][0] - extent2[0][0];
46978
+ const lat = extent2[1][1] - extent2[0][1];
46979
+ const longer = Math.max(lon, lat);
46980
+ if (longer > 0 && longer < POI_ZOOM_FLOOR_DEG) {
46981
+ const k = POI_ZOOM_FLOOR_DEG / longer;
46982
+ const halfLon = lon * k / 2;
46983
+ const halfLat = lat * k / 2;
46984
+ extent2 = [
46985
+ [cx - halfLon, cy - halfLat],
46986
+ [cx + halfLon, cy + halfLat]
46987
+ ];
46988
+ }
46989
+ }
46507
46990
  const lonSpan = extent2[1][0] - extent2[0][0];
46508
46991
  const latSpan = extent2[1][1] - extent2[0][1];
46509
46992
  const span = Math.max(lonSpan, latSpan);
46510
46993
  const maxAbsLat = Math.max(Math.abs(extent2[0][1]), Math.abs(extent2[1][1]));
46511
- const usDominant = (subdivisions.includes("us-states") || regions.some((r) => r.layer === "us-state")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
46512
46994
  let projection;
46513
- const override = parsed.directives.projection;
46514
- if (override === "equirectangular" || override === "natural-earth" || override === "albers-usa" || override === "mercator") {
46515
- projection = override;
46516
- } else if (usDominant) {
46995
+ if (isPoiOnly && usOriented && lonSpan < US_NATIONAL_LON_SPAN) {
46996
+ projection = "mercator";
46997
+ } else if (usOriented) {
46517
46998
  projection = "albers-usa";
46518
46999
  } else if (span > WORLD_SPAN || maxAbsLat > MERCATOR_MAX_LAT) {
46519
47000
  projection = "equirectangular";
@@ -46531,11 +47012,20 @@ function resolveMap(parsed, data) {
46531
47012
  result.edges = edges;
46532
47013
  result.routes = routes;
46533
47014
  result.basemaps = {
46534
- world: span > WORLD_SPAN ? "coarse" : "detail",
47015
+ // Tier is intentionally pinned to detail (50m) at ALL scales. Diagrammo maps
47016
+ // are presentational (palette tints, relief hachures, POI hubs), not
47017
+ // survey-grade — recognizability > generalization: 110m coarse drops the
47018
+ // Italian boot to a stump at world scale. `WORLD_SPAN` lives on only for the
47019
+ // projection decision (the `usOriented`/`span > WORLD_SPAN` chain above); it
47020
+ // no longer gates basemap resolution.
47021
+ // `worldCoarse` is still loaded — it's the authoritative name/bbox index
47022
+ // (featureIndex, featureBboxPrimary), not dead code.
47023
+ world: "detail",
46535
47024
  subdivisions
46536
47025
  };
46537
47026
  result.extent = extent2;
46538
47027
  result.projection = projection;
47028
+ result.poiFrameContainers = containerRegionIds;
46539
47029
  result.error = parsed.error ?? firstError(diagnostics);
46540
47030
  return result;
46541
47031
  }
@@ -46572,7 +47062,7 @@ function firstError(diags) {
46572
47062
  const e = diags.find((d) => d.severity === "error");
46573
47063
  return e ? formatDgmoError(e) : null;
46574
47064
  }
46575
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES, US_STATE_POSTAL;
47065
+ var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, US_NATIONAL_LON_SPAN, REGION_ALIASES, US_STATE_POSTAL;
46576
47066
  var init_resolver2 = __esm({
46577
47067
  "src/map/resolver.ts"() {
46578
47068
  "use strict";
@@ -46581,8 +47071,11 @@ var init_resolver2 = __esm({
46581
47071
  WORLD_SPAN = 90;
46582
47072
  MERCATOR_MAX_LAT = 80;
46583
47073
  PAD_FRACTION = 0.05;
47074
+ REGION_PAD_FRACTION = 0.12;
46584
47075
  WORLD_LAT_SOUTH = -58;
46585
47076
  WORLD_LAT_NORTH = 78;
47077
+ POI_ZOOM_FLOOR_DEG = 7;
47078
+ US_NATIONAL_LON_SPAN = 48;
46586
47079
  REGION_ALIASES = {
46587
47080
  // Common everyday names → the Natural-Earth display name actually shipped.
46588
47081
  "united states": "united states of america",
@@ -46660,10 +47153,277 @@ var init_resolver2 = __esm({
46660
47153
  }
46661
47154
  });
46662
47155
 
47156
+ // src/map/colorize.ts
47157
+ function assignColors(isos, adjacency) {
47158
+ const sorted = [...isos].sort();
47159
+ const byIso = /* @__PURE__ */ new Map();
47160
+ let maxIndex = -1;
47161
+ for (const iso of sorted) {
47162
+ const taken = /* @__PURE__ */ new Set();
47163
+ for (const n of adjacency.get(iso) ?? []) {
47164
+ const c = byIso.get(n);
47165
+ if (c !== void 0) taken.add(c);
47166
+ }
47167
+ let h = 0;
47168
+ while (taken.has(h)) h++;
47169
+ byIso.set(iso, h);
47170
+ if (h > maxIndex) maxIndex = h;
47171
+ }
47172
+ return { byIso, huesNeeded: maxIndex + 1 };
47173
+ }
47174
+ var init_colorize = __esm({
47175
+ "src/map/colorize.ts"() {
47176
+ "use strict";
47177
+ }
47178
+ });
47179
+
47180
+ // src/map/context-labels.ts
47181
+ function tierBand(maxSpanDeg) {
47182
+ if (maxSpanDeg >= 90) return "world";
47183
+ if (maxSpanDeg >= 20) return "continental";
47184
+ if (maxSpanDeg >= 5) return "regional";
47185
+ return "local";
47186
+ }
47187
+ function labelBudget(width, height, band) {
47188
+ const bandCap = {
47189
+ world: 6,
47190
+ continental: 5,
47191
+ regional: 4,
47192
+ local: 3
47193
+ };
47194
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
47195
+ return Math.max(0, Math.min(area2, bandCap[band]));
47196
+ }
47197
+ function waterEligible(tier, kind, band) {
47198
+ switch (band) {
47199
+ case "world":
47200
+ return tier <= 1 && (kind === "ocean" || kind === "sea");
47201
+ case "continental":
47202
+ return tier <= 2;
47203
+ case "regional":
47204
+ return tier <= 3;
47205
+ case "local":
47206
+ return tier <= 4;
47207
+ }
47208
+ }
47209
+ function insideViewport(p, width, height) {
47210
+ return !!p && Number.isFinite(p[0]) && Number.isFinite(p[1]) && p[0] >= 0 && p[0] <= width && p[1] >= 0 && p[1] <= height;
47211
+ }
47212
+ function labelWidth(text, letterSpacing) {
47213
+ const spacing = letterSpacing > 0 ? Math.max(0, text.length - 1) * letterSpacing : 0;
47214
+ return measureLegendText(text, FONT) + spacing + 2 * PADX;
47215
+ }
47216
+ function wrapLabel2(text, letterSpacing) {
47217
+ const words = text.split(/\s+/).filter(Boolean);
47218
+ if (words.length <= 1) return [text];
47219
+ const maxLines = words.length >= 4 ? 3 : 2;
47220
+ const n = words.length;
47221
+ let best = null;
47222
+ for (let mask = 0; mask < 1 << n - 1; mask++) {
47223
+ const lines = [];
47224
+ let cur = [words[0]];
47225
+ for (let i = 1; i < n; i++) {
47226
+ if (mask & 1 << i - 1) {
47227
+ lines.push(cur.join(" "));
47228
+ cur = [words[i]];
47229
+ } else cur.push(words[i]);
47230
+ }
47231
+ lines.push(cur.join(" "));
47232
+ if (lines.length > maxLines) continue;
47233
+ const cost = Math.round(
47234
+ Math.max(...lines.map((l) => labelWidth(l, letterSpacing)))
47235
+ );
47236
+ const head = labelWidth(lines[0], letterSpacing);
47237
+ if (!best || cost < best.cost || cost === best.cost && lines.length < best.lines.length || cost === best.cost && lines.length === best.lines.length && head > best.head)
47238
+ best = { lines, cost, head };
47239
+ }
47240
+ return best?.lines ?? [text];
47241
+ }
47242
+ function rectAround(cx, cy, lines, letterSpacing) {
47243
+ const w = Math.max(...lines.map((l) => labelWidth(l, letterSpacing)));
47244
+ const h = (lines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY;
47245
+ return { x: cx - w / 2, y: cy - h / 2, w, h };
47246
+ }
47247
+ function rectFits(r, width, height) {
47248
+ return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
47249
+ }
47250
+ function overlapsPadded(a, b, pad2) {
47251
+ return a.x - pad2 < b.x + b.w && a.x + a.w + pad2 > b.x && a.y - pad2 < b.y + b.h && a.y + a.h + pad2 > b.y;
47252
+ }
47253
+ function placeContextLabels(args) {
47254
+ const {
47255
+ projection,
47256
+ dLonSpan,
47257
+ dLatSpan,
47258
+ width,
47259
+ height,
47260
+ waterBodies,
47261
+ countries,
47262
+ palette,
47263
+ project,
47264
+ collides,
47265
+ overLand
47266
+ } = args;
47267
+ void projection;
47268
+ const band = tierBand(Math.max(dLonSpan, dLatSpan));
47269
+ const budget = labelBudget(width, height, band);
47270
+ if (budget <= 0) return [];
47271
+ const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
47272
+ const countryColor = palette.textMuted;
47273
+ const haloColor = palette.bg;
47274
+ const candidates = [];
47275
+ const center = [width / 2, height / 2];
47276
+ for (const e of waterBodies?.entries ?? []) {
47277
+ const [lat, lon, name, tier, kind, alt] = e;
47278
+ if (!waterEligible(tier, kind, band)) continue;
47279
+ const wlines = wrapLabel2(name, WATER_LETTER_SPACING);
47280
+ const anchorsLngLat = [[lon, lat]];
47281
+ for (const a of alt ?? []) anchorsLngLat.push([a[1], a[0]]);
47282
+ let best = null;
47283
+ let bestD = Infinity;
47284
+ let nearestProj = null;
47285
+ let nearestProjD = Infinity;
47286
+ for (const [aLon, aLat] of anchorsLngLat) {
47287
+ const p = project(aLon, aLat);
47288
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
47289
+ const d = (p[0] - center[0]) ** 2 + (p[1] - center[1]) ** 2;
47290
+ if (d < nearestProjD) {
47291
+ nearestProjD = d;
47292
+ nearestProj = p;
47293
+ }
47294
+ if (!insideViewport(p, width, height)) continue;
47295
+ if (d < bestD) {
47296
+ bestD = d;
47297
+ best = p;
47298
+ }
47299
+ }
47300
+ if (!best && tier === 0 && nearestProj) {
47301
+ const overX = Math.max(0, -nearestProj[0], nearestProj[0] - width);
47302
+ const overY = Math.max(0, -nearestProj[1], nearestProj[1] - height);
47303
+ if (overX <= width * EDGE_CLAMP_OVERSHOOT && overY <= height * EDGE_CLAMP_OVERSHOOT) {
47304
+ const halfW = Math.max(...wlines.map((l) => labelWidth(l, WATER_LETTER_SPACING))) / 2;
47305
+ const halfH = ((wlines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY) / 2;
47306
+ const m = EDGE_CLAMP_MARGIN;
47307
+ best = [
47308
+ Math.min(Math.max(nearestProj[0], halfW + m), width - halfW - m),
47309
+ Math.min(Math.max(nearestProj[1], halfH + m), height - halfH - m)
47310
+ ];
47311
+ }
47312
+ }
47313
+ if (!best) continue;
47314
+ candidates.push({
47315
+ text: name,
47316
+ lines: wlines,
47317
+ cx: best[0],
47318
+ cy: best[1],
47319
+ italic: true,
47320
+ letterSpacing: WATER_LETTER_SPACING,
47321
+ color: waterColor,
47322
+ // Water before any country (×1000), then by tier, then kind, then name.
47323
+ sort: tier * 10 + KIND_ORDER[kind]
47324
+ });
47325
+ }
47326
+ const ranked = countries.map((c) => {
47327
+ const [x0, y0, x1, y1] = c.bbox;
47328
+ const w = x1 - x0;
47329
+ const h = y1 - y0;
47330
+ return { c, w, h, area: w * h };
47331
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
47332
+ let ci = 0;
47333
+ for (const r of ranked) {
47334
+ const { c, w, h } = r;
47335
+ if (w > width * 0.66 || h > height * 0.66) continue;
47336
+ if (!insideViewport(c.anchor, width, height)) continue;
47337
+ const text = c.name;
47338
+ const tw = labelWidth(text, 0);
47339
+ if (tw > w || FONT + 2 * PADY > h) continue;
47340
+ candidates.push({
47341
+ text,
47342
+ lines: [text],
47343
+ cx: c.anchor[0],
47344
+ cy: c.anchor[1],
47345
+ italic: false,
47346
+ letterSpacing: 0,
47347
+ color: countryColor,
47348
+ // Always after every water body (+1e6); larger area = earlier.
47349
+ sort: 1e6 + ci++
47350
+ });
47351
+ }
47352
+ candidates.sort((a, b) => a.sort - b.sort);
47353
+ const placed = [];
47354
+ const placedRects = [];
47355
+ for (const cand of candidates) {
47356
+ if (placed.length >= budget) break;
47357
+ const rect = rectAround(cand.cx, cand.cy, cand.lines, cand.letterSpacing);
47358
+ if (!rectFits(rect, width, height)) continue;
47359
+ if (cand.italic && overLand) {
47360
+ const inset = 2;
47361
+ const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
47362
+ const touchesLand = cand.lines.some((line12, li) => {
47363
+ const lw = labelWidth(line12, cand.letterSpacing);
47364
+ const x0 = cand.cx - lw / 2 + inset;
47365
+ const x1 = cand.cx + lw / 2 - inset;
47366
+ const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
47367
+ const base = top + li * LINE_HEIGHT;
47368
+ return [base, base - FONT * 0.4, base - FONT * 0.8].some(
47369
+ (y) => xs.some((x) => overLand(x, y))
47370
+ );
47371
+ });
47372
+ if (touchesLand) continue;
47373
+ }
47374
+ if (collides(rect)) continue;
47375
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
47376
+ placedRects.push(rect);
47377
+ placed.push({
47378
+ x: cand.cx,
47379
+ y: cand.cy,
47380
+ text: cand.text,
47381
+ anchor: "middle",
47382
+ color: cand.color,
47383
+ // No halo: the bg-coloured outline reads as a ghost box behind the text
47384
+ // over the tinted water/land. Context labels are muted enough to sit
47385
+ // cleanly on the basemap without one.
47386
+ halo: false,
47387
+ haloColor,
47388
+ italic: cand.italic,
47389
+ letterSpacing: cand.letterSpacing,
47390
+ ...cand.lines.length > 1 ? { lines: cand.lines } : {},
47391
+ lineNumber: 0
47392
+ });
47393
+ }
47394
+ return placed;
47395
+ }
47396
+ var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, KIND_ORDER;
47397
+ var init_context_labels = __esm({
47398
+ "src/map/context-labels.ts"() {
47399
+ "use strict";
47400
+ init_color_utils();
47401
+ init_legend_constants();
47402
+ FONT = 11;
47403
+ LINE_HEIGHT = FONT + 2;
47404
+ PADX = 4;
47405
+ PADY = 3;
47406
+ WATER_LETTER_SPACING = 1.5;
47407
+ CONTEXT_PAD = 4;
47408
+ EDGE_CLAMP_MARGIN = 8;
47409
+ EDGE_CLAMP_OVERSHOOT = 0.35;
47410
+ KIND_ORDER = {
47411
+ ocean: 0,
47412
+ sea: 1,
47413
+ gulf: 2,
47414
+ bay: 3,
47415
+ strait: 4,
47416
+ channel: 5,
47417
+ sound: 6
47418
+ };
47419
+ }
47420
+ });
47421
+
46663
47422
  // src/map/layout.ts
46664
47423
  import {
46665
47424
  geoPath,
46666
47425
  geoNaturalEarth1,
47426
+ geoEqualEarth,
46667
47427
  geoEquirectangular,
46668
47428
  geoConicEqualArea,
46669
47429
  geoMercator,
@@ -46675,12 +47435,34 @@ function geomObject2(topo) {
46675
47435
  const key = Object.keys(topo.objects)[0];
46676
47436
  return topo.objects[key];
46677
47437
  }
47438
+ function mergeFeatures(a, b) {
47439
+ const polysOf = (f) => {
47440
+ const g = f.geometry;
47441
+ if (!g) return null;
47442
+ if (g.type === "Polygon") return [g.coordinates];
47443
+ if (g.type === "MultiPolygon") return g.coordinates;
47444
+ return null;
47445
+ };
47446
+ const pa = polysOf(a);
47447
+ const pb = polysOf(b);
47448
+ if (!pa || !pb) return a;
47449
+ return {
47450
+ ...a,
47451
+ geometry: { type: "MultiPolygon", coordinates: [...pa, ...pb] }
47452
+ };
47453
+ }
46678
47454
  function decodeLayer(topo) {
47455
+ const cached = decodeCache.get(topo);
47456
+ if (cached) return cached;
46679
47457
  const out = /* @__PURE__ */ new Map();
46680
47458
  for (const g of geomObject2(topo).geometries) {
46681
47459
  const f = feature2(topo, g);
46682
- out.set(g.id, { ...f, id: g.id });
47460
+ if (!f.geometry) continue;
47461
+ const tagged = { ...f, id: g.id };
47462
+ const existing = out.get(g.id);
47463
+ out.set(g.id, existing ? mergeFeatures(existing, tagged) : tagged);
46683
47464
  }
47465
+ decodeCache.set(topo, out);
46684
47466
  return out;
46685
47467
  }
46686
47468
  function projectionFor(family) {
@@ -46689,9 +47471,12 @@ function projectionFor(family) {
46689
47471
  return usConusProjection();
46690
47472
  case "mercator":
46691
47473
  return geoMercator();
47474
+ case "equal-earth":
47475
+ return geoEqualEarth();
47476
+ case "equirectangular":
47477
+ return geoEquirectangular();
46692
47478
  case "natural-earth":
46693
47479
  return geoNaturalEarth1();
46694
- case "equirectangular":
46695
47480
  default:
46696
47481
  return geoEquirectangular();
46697
47482
  }
@@ -46710,13 +47495,11 @@ function mapNeutralLandColor(palette, isDark, _dataActive = false) {
46710
47495
  isDark ? LAND_TINT_DARK : LAND_TINT_LIGHT
46711
47496
  );
46712
47497
  }
46713
- function layoutMap(resolved, data, size, opts) {
46714
- const { palette, isDark } = opts;
46715
- const { width, height } = size;
47498
+ function buildMapProjection(resolved, data) {
46716
47499
  const wantsUsStates = resolved.basemaps.subdivisions.includes("us-states");
46717
- const usCrisp = resolved.projection === "albers-usa" && wantsUsStates && !!data.naLand;
47500
+ const usCrisp = (resolved.projection === "albers-usa" || resolved.projection === "mercator") && wantsUsStates && !!data.naLand;
46718
47501
  const worldTopo = usCrisp ? data.worldDetail : resolved.basemaps.world === "detail" ? data.worldDetail : data.worldCoarse;
46719
- const worldLayer = decodeLayer(worldTopo);
47502
+ const worldLayer = new Map(decodeLayer(worldTopo));
46720
47503
  if (usCrisp && data.naLand) {
46721
47504
  const [nbW, nbS, nbE, nbN] = [-140, 10, -52, 66];
46722
47505
  const crisp = decodeLayer(data.naLand);
@@ -46725,16 +47508,109 @@ function layoutMap(resolved, data, size, opts) {
46725
47508
  if (!base) continue;
46726
47509
  const [[bw, bs], [be, bn]] = geoBounds2(base);
46727
47510
  if (bw >= nbW && be <= nbE && bs >= nbS && bn <= nbN)
46728
- worldLayer.set(iso, cf);
47511
+ worldLayer.set(iso, { ...cf, properties: base.properties });
46729
47512
  }
46730
47513
  }
46731
47514
  const usLayer = wantsUsStates ? decodeLayer(data.usStates) : null;
47515
+ const extentOutline = () => {
47516
+ const [[w, s], [e, n]] = resolved.extent;
47517
+ const N = 16;
47518
+ const coords = [];
47519
+ for (let i = 0; i <= N; i++) {
47520
+ const t = i / N;
47521
+ const lon = w + (e - w) * t;
47522
+ const lat = s + (n - s) * t;
47523
+ coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
47524
+ }
47525
+ return {
47526
+ type: "Feature",
47527
+ properties: {},
47528
+ geometry: { type: "MultiPoint", coordinates: coords }
47529
+ };
47530
+ };
47531
+ let fitFeatures;
47532
+ if (resolved.projection === "albers-usa" && usLayer) {
47533
+ fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
47534
+ const neighborPoints = resolved.pois.filter((p) => !inAlaska(p.lon, p.lat) && !inHawaii(p.lon, p.lat)).map((p) => [p.lon, p.lat]);
47535
+ if (neighborPoints.length > 0) {
47536
+ fitFeatures.push({
47537
+ type: "Feature",
47538
+ properties: {},
47539
+ geometry: { type: "MultiPoint", coordinates: neighborPoints }
47540
+ });
47541
+ }
47542
+ for (const r of resolved.regions) {
47543
+ if (r.layer === "country" && (r.iso === "CA" || r.iso === "MX")) {
47544
+ const cf = worldLayer.get(r.iso);
47545
+ if (cf) fitFeatures.push(cf);
47546
+ }
47547
+ }
47548
+ } else {
47549
+ fitFeatures = [extentOutline()];
47550
+ }
47551
+ const fitTarget = { type: "FeatureCollection", features: fitFeatures };
47552
+ const projection = projectionFor(resolved.projection);
47553
+ if (resolved.projection !== "albers-usa") {
47554
+ let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
47555
+ if (centerLon > 180) centerLon -= 360;
47556
+ projection.rotate([-centerLon, 0]);
47557
+ }
47558
+ const fitGB = geoBounds2(fitTarget);
47559
+ const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
47560
+ return {
47561
+ projection,
47562
+ fitTarget,
47563
+ fitIsGlobal,
47564
+ worldLayer,
47565
+ usLayer,
47566
+ usCrisp,
47567
+ wantsUsStates,
47568
+ worldTopo
47569
+ };
47570
+ }
47571
+ function parsePathRings(d) {
47572
+ const rings = [];
47573
+ let cur = [];
47574
+ const re = /([MLZ])([^MLZ]*)/g;
47575
+ let m;
47576
+ while (m = re.exec(d)) {
47577
+ if (m[1] === "Z") {
47578
+ if (cur.length) rings.push(cur);
47579
+ cur = [];
47580
+ continue;
47581
+ }
47582
+ if (m[1] === "M" && cur.length) {
47583
+ rings.push(cur);
47584
+ cur = [];
47585
+ }
47586
+ const nums = m[2].split(/[ ,]+/).map(Number);
47587
+ for (let i = 0; i + 1 < nums.length; i += 2) {
47588
+ const x = nums[i];
47589
+ const y = nums[i + 1];
47590
+ if (Number.isFinite(x) && Number.isFinite(y)) cur.push([x, y]);
47591
+ }
47592
+ }
47593
+ if (cur.length) rings.push(cur);
47594
+ return rings;
47595
+ }
47596
+ function layoutMap(resolved, data, size, opts) {
47597
+ const { palette, isDark } = opts;
47598
+ const { width, height } = size;
47599
+ const {
47600
+ projection,
47601
+ fitTarget,
47602
+ fitIsGlobal,
47603
+ worldLayer,
47604
+ usLayer,
47605
+ usCrisp,
47606
+ worldTopo
47607
+ } = buildMapProjection(resolved, data);
46732
47608
  const usContext = usLayer !== null;
46733
47609
  const regionStroke = isDark ? mix(palette.bg, palette.text, 78) : mix(palette.text, palette.bg, 78);
46734
47610
  const values = resolved.regions.filter((r) => r.value !== void 0).map((r) => r.value);
46735
- const scaleOverride = resolved.directives.scale;
46736
- const rampMin = scaleOverride ? scaleOverride.min : Math.min(...values);
46737
- const rampMax = scaleOverride ? scaleOverride.max : Math.max(...values);
47611
+ const allNonNegative = values.length > 0 && values.every((v) => v >= 0);
47612
+ const rampMin = allNonNegative ? 0 : Math.min(...values);
47613
+ const rampMax = Math.max(...values);
46738
47614
  const rampHue = resolveColor(resolved.directives.regionMetricColor ?? "", palette) ?? palette.colors.red;
46739
47615
  const hasRamp = values.length > 0;
46740
47616
  const VALUE_NAME = hasRamp ? resolved.directives.regionMetric?.trim() || "Value" : null;
@@ -46755,7 +47631,7 @@ function layoutMap(resolved, data, size, opts) {
46755
47631
  activeGroup = VALUE_NAME ?? (resolved.tagGroups.length > 0 ? resolved.tagGroups[0].name : null);
46756
47632
  }
46757
47633
  const activeIsScore = VALUE_NAME !== null && activeGroup === VALUE_NAME;
46758
- const mutedBasemap = resolved.directives.basemapStyle === "muted" ? true : resolved.directives.basemapStyle === "natural" ? false : activeGroup !== null;
47634
+ const mutedBasemap = activeGroup !== null;
46759
47635
  const neutralFill = mapNeutralLandColor(palette, isDark, mutedBasemap);
46760
47636
  const water = mapBackgroundColor(palette, isDark, mutedBasemap);
46761
47637
  const lakeStroke = mix(regionStroke, water, 45);
@@ -46764,6 +47640,39 @@ function layoutMap(resolved, data, size, opts) {
46764
47640
  palette.bg,
46765
47641
  mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
46766
47642
  );
47643
+ const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && resolved.tagGroups.length === 0;
47644
+ const colorByIso = /* @__PURE__ */ new Map();
47645
+ if (colorizeActive) {
47646
+ const adjacency = /* @__PURE__ */ new Map();
47647
+ const addEdges = (src) => {
47648
+ for (const [iso, ns] of src) {
47649
+ const cur = adjacency.get(iso);
47650
+ if (cur) cur.push(...ns);
47651
+ else adjacency.set(iso, [...ns]);
47652
+ }
47653
+ };
47654
+ addEdges(buildAdjacency(worldTopo));
47655
+ if (usLayer) {
47656
+ addEdges(buildAdjacency(data.usStates));
47657
+ for (const [country, states] of Object.entries(FOREIGN_BORDER)) {
47658
+ const cn = adjacency.get(country);
47659
+ if (!cn) continue;
47660
+ for (const st of states) {
47661
+ const sn = adjacency.get(st);
47662
+ if (!sn) continue;
47663
+ cn.push(st);
47664
+ sn.push(country);
47665
+ }
47666
+ }
47667
+ }
47668
+ const { byIso, huesNeeded } = assignColors(
47669
+ [...adjacency.keys()],
47670
+ adjacency
47671
+ );
47672
+ const tints = politicalTints(palette, huesNeeded, isDark);
47673
+ for (const [iso, idx] of byIso) colorByIso.set(iso, tints[idx]);
47674
+ }
47675
+ const colorizeStroke = (fill2) => mix(fill2, palette.text, 35);
46767
47676
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
46768
47677
  const fillForValue = (s) => {
46769
47678
  const t = rampMax > rampMin ? (s - rampMin) / (rampMax - rampMin) : 1;
@@ -46799,43 +47708,15 @@ function layoutMap(resolved, data, size, opts) {
46799
47708
  if (activeIsScore) {
46800
47709
  return r.value !== void 0 ? fillForValue(r.value) : neutralFill;
46801
47710
  }
47711
+ if (colorizeActive) return (r.iso && colorByIso.get(r.iso)) ?? neutralFill;
46802
47712
  return tagFill(r.tags, activeGroup) ?? neutralFill;
46803
47713
  };
46804
47714
  const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
46805
- const extentOutline = () => {
46806
- const [[w, s], [e, n]] = resolved.extent;
46807
- const N = 16;
46808
- const coords = [];
46809
- for (let i = 0; i <= N; i++) {
46810
- const t = i / N;
46811
- const lon = w + (e - w) * t;
46812
- const lat = s + (n - s) * t;
46813
- coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
46814
- }
46815
- return {
46816
- type: "Feature",
46817
- properties: {},
46818
- geometry: { type: "MultiPoint", coordinates: coords }
46819
- };
46820
- };
46821
- let fitFeatures;
46822
- if (resolved.projection === "albers-usa" && usLayer) {
46823
- fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
46824
- } else {
46825
- fitFeatures = [extentOutline()];
46826
- }
46827
- const fitTarget = { type: "FeatureCollection", features: fitFeatures };
46828
- const projection = projectionFor(resolved.projection);
46829
- if (resolved.projection !== "albers-usa") {
46830
- let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
46831
- if (centerLon > 180) centerLon -= 360;
46832
- projection.rotate([-centerLon, 0]);
46833
- }
46834
- const TITLE_GAP = 16;
47715
+ const TITLE_GAP2 = 16;
46835
47716
  let topPad = FIT_PAD;
46836
47717
  if (resolved.title && resolved.pois.length > 0) {
46837
47718
  const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
46838
- topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP);
47719
+ topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
46839
47720
  }
46840
47721
  const fitBox = [
46841
47722
  [FIT_PAD, topPad],
@@ -46845,12 +47726,10 @@ function layoutMap(resolved, data, size, opts) {
46845
47726
  ]
46846
47727
  ];
46847
47728
  projection.fitExtent(fitBox, fitTarget);
46848
- const fitGB = geoBounds2(fitTarget);
46849
- const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
46850
47729
  let path;
46851
47730
  let project;
46852
47731
  let stretchParams = null;
46853
- if (fitIsGlobal) {
47732
+ if (fitIsGlobal && !opts.preferContain) {
46854
47733
  const cb = geoPath(projection).bounds(fitTarget);
46855
47734
  const bx0 = cb[0][0];
46856
47735
  const by0 = cb[0][1];
@@ -46892,7 +47771,9 @@ function layoutMap(resolved, data, size, opts) {
46892
47771
  const insets = [];
46893
47772
  const insetRegions = [];
46894
47773
  const insetLabelSeeds = [];
46895
- if (resolved.projection === "albers-usa" && usLayer && !resolved.directives.noInsets) {
47774
+ const akRef = resolved.regions.some((r) => r.iso === "US-AK") || resolved.pois.some((p) => inAlaska(p.lon, p.lat));
47775
+ const hiRef = resolved.regions.some((r) => r.iso === "US-HI") || resolved.pois.some((p) => inHawaii(p.lon, p.lat));
47776
+ if (resolved.projection === "albers-usa" && usLayer && (akRef || hiRef)) {
46896
47777
  const PAD = 8;
46897
47778
  const GAP = 12;
46898
47779
  const yB = height - FIT_PAD;
@@ -46957,8 +47838,18 @@ function layoutMap(resolved, data, size, opts) {
46957
47838
  );
46958
47839
  const d = geoPath(proj)(f) ?? "";
46959
47840
  if (!d) return xr;
47841
+ let contextLand;
47842
+ if (iso === "US-AK") {
47843
+ const can = worldLayer.get("CA");
47844
+ const cd = can ? geoPath(proj)(can) ?? "" : "";
47845
+ if (cd)
47846
+ contextLand = {
47847
+ d: cd,
47848
+ fill: colorizeActive ? colorByIso.get("CA") ?? foreignFill : foreignFill
47849
+ };
47850
+ }
46960
47851
  const r = regionById.get(iso);
46961
- let fill2 = neutralFill;
47852
+ let fill2 = colorizeActive ? colorByIso.get(iso) ?? neutralFill : neutralFill;
46962
47853
  let lineNumber = -1;
46963
47854
  if (r?.layer === "us-state") {
46964
47855
  fill2 = regionFill(r);
@@ -46977,13 +47868,14 @@ function layoutMap(resolved, data, size, opts) {
46977
47868
  ],
46978
47869
  // The FITTED inset projection (just fit to this box) — captured so the
46979
47870
  // geo-query can invert pixels inside the frame back to AK/HI coords.
46980
- projection: proj
47871
+ projection: proj,
47872
+ ...contextLand && { contextLand }
46981
47873
  });
46982
47874
  insetRegions.push({
46983
47875
  id: iso,
46984
47876
  d,
46985
47877
  fill: fill2,
46986
- stroke: regionStroke,
47878
+ stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
46987
47879
  lineNumber,
46988
47880
  layer: "us-state",
46989
47881
  ...r?.value !== void 0 && { value: r.value },
@@ -46996,13 +47888,16 @@ function layoutMap(resolved, data, size, opts) {
46996
47888
  }
46997
47889
  return xr;
46998
47890
  };
46999
- const akRight = placeInset(
47000
- "US-AK",
47001
- alaskaProjection(),
47002
- FIT_PAD,
47003
- width * 0.15
47004
- );
47005
- placeInset("US-HI", hawaiiProjection(), akRight + 24, width * 0.1);
47891
+ let akRight = FIT_PAD;
47892
+ if (akRef)
47893
+ akRight = placeInset("US-AK", alaskaProjection(), FIT_PAD, width * 0.15);
47894
+ if (hiRef)
47895
+ placeInset(
47896
+ "US-HI",
47897
+ hawaiiProjection(),
47898
+ akRef ? akRight + 24 : FIT_PAD,
47899
+ width * 0.1
47900
+ );
47006
47901
  }
47007
47902
  const conusFit = resolved.projection === "albers-usa" && !!usLayer;
47008
47903
  const classifyExtent = conusFit ? geoBounds2(fitTarget) : resolved.extent;
@@ -47018,15 +47913,24 @@ function layoutMap(resolved, data, size, opts) {
47018
47913
  };
47019
47914
  const ringOverlapsView = (ring) => {
47020
47915
  let loMin = Infinity, loMax = -Infinity, rawMin = Infinity, rawMax = -Infinity;
47916
+ const lons = [];
47021
47917
  for (const [rawLon] of ring) {
47022
47918
  const lon = normLon(rawLon);
47919
+ lons.push(lon);
47023
47920
  if (lon < loMin) loMin = lon;
47024
47921
  if (lon > loMax) loMax = lon;
47025
47922
  if (rawLon < rawMin) rawMin = rawLon;
47026
47923
  if (rawLon > rawMax) rawMax = rawLon;
47027
47924
  }
47028
- if (loMax - loMin > 270) return false;
47029
- if (rawMax - rawMin > 180 && loMax - loMin < 90) return false;
47925
+ lons.sort((a, b) => a - b);
47926
+ let maxGap = 0;
47927
+ for (let i = 1; i < lons.length; i++)
47928
+ maxGap = Math.max(maxGap, lons[i] - lons[i - 1]);
47929
+ if (lons.length > 1)
47930
+ maxGap = Math.max(maxGap, lons[0] + 360 - lons[lons.length - 1]);
47931
+ const occupiedArc = 360 - maxGap;
47932
+ if (occupiedArc > 270) return false;
47933
+ if (rawMax - rawMin > 180 && occupiedArc < 90) return false;
47030
47934
  let px0 = Infinity, py0 = Infinity, px1 = -Infinity, py1 = -Infinity, anyFinite = false;
47031
47935
  for (const [lon, lat] of ring) {
47032
47936
  const p = project(lon, lat);
@@ -47099,7 +48003,7 @@ function layoutMap(resolved, data, size, opts) {
47099
48003
  const regions = [];
47100
48004
  const pushRegionLayer = (layerFeatures, layerKind, shouldCull) => {
47101
48005
  for (const [iso, f] of layerFeatures) {
47102
- if (layerKind === "us-state" && usContext && INSET_STATES.has(iso))
48006
+ if (layerKind === "us-state" && usContext && resolved.projection === "albers-usa" && INSET_STATES.has(iso))
47103
48007
  continue;
47104
48008
  if (layerKind === "country" && usContext && iso === "US") continue;
47105
48009
  if (layerKind === "country" && iso === "AQ" && !regionById.has("AQ"))
@@ -47111,7 +48015,8 @@ function layoutMap(resolved, data, size, opts) {
47111
48015
  if (!d) continue;
47112
48016
  const isThisLayer = r?.layer === layerKind;
47113
48017
  const isForeign = layerKind === "country" && usContext && iso !== "US";
47114
- let fill2 = isForeign ? foreignFill : neutralFill;
48018
+ const baseFill = isForeign ? foreignFill : neutralFill;
48019
+ let fill2 = colorizeActive ? colorByIso.get(iso) ?? baseFill : baseFill;
47115
48020
  let label;
47116
48021
  let lineNumber = -1;
47117
48022
  let layer = "base";
@@ -47120,12 +48025,14 @@ function layoutMap(resolved, data, size, opts) {
47120
48025
  lineNumber = r.lineNumber;
47121
48026
  layer = layerKind;
47122
48027
  label = r.name;
48028
+ } else {
48029
+ label = f.properties?.name;
47123
48030
  }
47124
48031
  regions.push({
47125
48032
  id: iso,
47126
48033
  d,
47127
48034
  fill: fill2,
47128
- stroke: regionStroke,
48035
+ stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
47129
48036
  lineNumber,
47130
48037
  layer,
47131
48038
  ...label !== void 0 && { label },
@@ -47153,9 +48060,41 @@ function layoutMap(resolved, data, size, opts) {
47153
48060
  });
47154
48061
  }
47155
48062
  }
48063
+ const pointInRings = (px, py, rings) => {
48064
+ let inside = false;
48065
+ for (const ring of rings) {
48066
+ for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
48067
+ const [xi, yi] = ring[i];
48068
+ const [xj, yj] = ring[j];
48069
+ if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
48070
+ inside = !inside;
48071
+ }
48072
+ }
48073
+ return inside;
48074
+ };
48075
+ const fillHitTargets = [...regions, ...insetRegions].map((r) => ({
48076
+ fill: r.fill,
48077
+ rings: parsePathRings(r.d)
48078
+ }));
48079
+ const fillAt = (x, y) => {
48080
+ let hit = water;
48081
+ for (const t of fillHitTargets)
48082
+ if (pointInRings(x, y, t.rings)) hit = t.fill;
48083
+ return hit;
48084
+ };
48085
+ const labelOnFill = (fill2) => {
48086
+ const color = contrastRatio(fill2, palette.textOnFillDark) >= contrastRatio(fill2, palette.textOnFillLight) ? palette.textOnFillDark : palette.textOnFillLight;
48087
+ const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
48088
+ return {
48089
+ color,
48090
+ halo: contrastRatio(fill2, color) < REGION_LABEL_HALO_RATIO,
48091
+ haloColor
48092
+ };
48093
+ };
48094
+ const reliefAllowed = resolved.directives.noRelief !== true;
47156
48095
  const relief = [];
47157
48096
  let reliefHatch = null;
47158
- if (resolved.directives.relief === true && data.mountainRanges) {
48097
+ if (reliefAllowed && data.mountainRanges) {
47159
48098
  for (const [, f] of decodeLayer(data.mountainRanges)) {
47160
48099
  const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
47161
48100
  if (!viewF) continue;
@@ -47171,16 +48110,32 @@ function layoutMap(resolved, data, size, opts) {
47171
48110
  if (relief.length) {
47172
48111
  const darkTone = isDark ? palette.bg : palette.text;
47173
48112
  const lightTone = isDark ? palette.text : palette.bg;
47174
- const landLum = relativeLuminance(neutralFill);
48113
+ const reliefLandRef = colorizeActive ? isDark ? palette.surface : palette.bg : neutralFill;
48114
+ const landLum = relativeLuminance(reliefLandRef);
47175
48115
  const tone = Math.abs(landLum - relativeLuminance(darkTone)) > 0.04 ? darkTone : lightTone;
47176
48116
  reliefHatch = {
47177
- color: mix(tone, neutralFill, RELIEF_HATCH_STRENGTH),
48117
+ color: mix(tone, reliefLandRef, RELIEF_HATCH_STRENGTH),
47178
48118
  spacing: RELIEF_HATCH_SPACING,
47179
48119
  width: RELIEF_HATCH_WIDTH
47180
48120
  };
47181
48121
  }
47182
48122
  }
47183
- const riverColor = mix(water, regionStroke, 16);
48123
+ let coastlineStyle = null;
48124
+ if (resolved.directives.noCoastline !== true) {
48125
+ const minDim = Math.min(width, height);
48126
+ coastlineStyle = {
48127
+ color: mix(regionStroke, water, COASTLINE_STROKE_MIX),
48128
+ // N equal-width rings: distance steps outward by COASTLINE_STEP; opacity
48129
+ // fades linearly from NEAR (innermost) to FAR (outermost).
48130
+ lines: Array.from({ length: COASTLINE_RING_COUNT }, (_, k) => ({
48131
+ d: (COASTLINE_D0 + k * COASTLINE_STEP) * minDim,
48132
+ thickness: COASTLINE_THICKNESS * minDim,
48133
+ opacity: COASTLINE_OPACITY_NEAR + (COASTLINE_OPACITY_FAR - COASTLINE_OPACITY_NEAR) * k / (COASTLINE_RING_COUNT - 1)
48134
+ })),
48135
+ minExtent: (isGlobalView ? COASTLINE_MIN_EXTENT_GLOBAL : COASTLINE_MIN_EXTENT) * minDim
48136
+ };
48137
+ }
48138
+ const riverColor = mix(palette.colors.blue, water, 32);
47184
48139
  const rivers = [];
47185
48140
  if (data.rivers) {
47186
48141
  for (const [, f] of decodeLayer(data.rivers)) {
@@ -47236,38 +48191,108 @@ function layoutMap(resolved, data, size, opts) {
47236
48191
  const xy = project(p.lon, p.lat);
47237
48192
  if (xy) projected.push({ p, xy });
47238
48193
  }
47239
- const coloGroups = /* @__PURE__ */ new Map();
48194
+ const placePoi = (e, cx, cy, clusterId) => {
48195
+ const { fill: fill2, stroke: stroke2 } = poiFill(e.p);
48196
+ poiScreen.set(e.p.id, { cx, cy, r: radiusFor(e.p) });
48197
+ const num = routeNumberById.get(e.p.id);
48198
+ pois.push({
48199
+ id: e.p.id,
48200
+ cx,
48201
+ cy,
48202
+ r: radiusFor(e.p),
48203
+ fill: fill2,
48204
+ stroke: stroke2,
48205
+ lineNumber: e.p.lineNumber,
48206
+ implicit: !!e.p.implicit,
48207
+ isOrigin: originIds.has(e.p.id),
48208
+ ...num !== void 0 && { routeNumber: num },
48209
+ ...Object.keys(e.p.tags).length > 0 && { tags: e.p.tags },
48210
+ ...clusterId !== void 0 && { clusterId }
48211
+ });
48212
+ };
48213
+ const clusters = [];
48214
+ const connected = /* @__PURE__ */ new Set();
48215
+ for (const e of resolved.edges) {
48216
+ connected.add(e.fromId);
48217
+ connected.add(e.toId);
48218
+ }
48219
+ for (const rt of resolved.routes) {
48220
+ rt.stopIds.forEach((id) => connected.add(id));
48221
+ }
48222
+ const radiusOf = (e) => radiusFor(e.p);
47240
48223
  for (const e of projected) {
47241
- const key = `${Math.round(e.xy[0] / COLO_EPS)},${Math.round(e.xy[1] / COLO_EPS)}`;
47242
- const arr = coloGroups.get(key);
47243
- if (arr) arr.push(e);
47244
- else coloGroups.set(key, [e]);
47245
- }
47246
- for (const group of coloGroups.values()) {
47247
- group.forEach((e, i) => {
47248
- let cx = e.xy[0];
47249
- let cy = e.xy[1];
47250
- if (group.length > 1) {
47251
- const ang = i * GOLDEN_ANGLE;
47252
- cx += Math.cos(ang) * COLO_R;
47253
- cy += Math.sin(ang) * COLO_R;
47254
- }
47255
- const { fill: fill2, stroke: stroke2 } = poiFill(e.p);
47256
- poiScreen.set(e.p.id, { cx, cy, r: radiusFor(e.p) });
47257
- const num = routeNumberById.get(e.p.id);
47258
- pois.push({
47259
- id: e.p.id,
47260
- cx,
47261
- cy,
47262
- r: radiusFor(e.p),
47263
- fill: fill2,
47264
- stroke: stroke2,
47265
- lineNumber: e.p.lineNumber,
47266
- implicit: !!e.p.implicit,
47267
- isOrigin: originIds.has(e.p.id),
47268
- ...num !== void 0 && { routeNumber: num },
47269
- ...Object.keys(e.p.tags).length > 0 && { tags: e.p.tags }
47270
- });
48224
+ if (connected.has(e.p.id)) placePoi(e, e.xy[0], e.xy[1]);
48225
+ }
48226
+ const groups = [];
48227
+ for (const e of projected) {
48228
+ if (connected.has(e.p.id)) continue;
48229
+ const r = radiusOf(e);
48230
+ const near = groups.find(
48231
+ (g) => g.some(
48232
+ (q) => Math.hypot(q.xy[0] - e.xy[0], q.xy[1] - e.xy[1]) < (r + radiusOf(q)) * STACK_OVERLAP
48233
+ )
48234
+ );
48235
+ if (near) near.push(e);
48236
+ else groups.push([e]);
48237
+ }
48238
+ for (const g of groups) {
48239
+ if (g.length === 1) {
48240
+ placePoi(g[0], g[0].xy[0], g[0].xy[1]);
48241
+ continue;
48242
+ }
48243
+ const clusterId = g[0].p.id;
48244
+ const cx0 = g.reduce((s, e) => s + e.xy[0], 0) / g.length;
48245
+ const cy0 = g.reduce((s, e) => s + e.xy[1], 0) / g.length;
48246
+ const maxR = Math.max(...g.map(radiusOf));
48247
+ const sep = 2 * maxR + STACK_RING_GAP;
48248
+ const ringR = Math.max(
48249
+ COLO_R,
48250
+ sep / (2 * Math.sin(Math.PI / Math.max(g.length, 2)))
48251
+ );
48252
+ const positions = g.map((e, i) => {
48253
+ if (g.length <= STACK_RING_MAX) {
48254
+ const ang2 = -Math.PI / 2 + i * 2 * Math.PI / g.length;
48255
+ return {
48256
+ e,
48257
+ mx: cx0 + Math.cos(ang2) * ringR,
48258
+ my: cy0 + Math.sin(ang2) * ringR
48259
+ };
48260
+ }
48261
+ const ang = i * GOLDEN_ANGLE;
48262
+ const rr = ringR * Math.sqrt((i + 1) / g.length);
48263
+ return { e, mx: cx0 + Math.cos(ang) * rr, my: cy0 + Math.sin(ang) * rr };
48264
+ });
48265
+ let minX = cx0 - maxR;
48266
+ let maxX = cx0 + maxR;
48267
+ let minY = cy0 - maxR;
48268
+ let maxY = cy0 + maxR;
48269
+ for (const { mx, my, e } of positions) {
48270
+ const r = radiusOf(e);
48271
+ minX = Math.min(minX, mx - r);
48272
+ maxX = Math.max(maxX, mx + r);
48273
+ minY = Math.min(minY, my - r);
48274
+ maxY = Math.max(maxY, my + r);
48275
+ }
48276
+ let dx = 0;
48277
+ let dy = 0;
48278
+ if (minX + dx < 2) dx = 2 - minX;
48279
+ if (maxX + dx > width - 2) dx = width - 2 - maxX;
48280
+ if (minY + dy < 2) dy = 2 - minY;
48281
+ if (maxY + dy > height - 2) dy = height - 2 - maxY;
48282
+ const legsOut = [];
48283
+ for (const { e, mx, my } of positions) {
48284
+ const fx = mx + dx;
48285
+ const fy = my + dy;
48286
+ placePoi(e, fx, fy, clusterId);
48287
+ legsOut.push({ x2: fx, y2: fy, color: poiFill(e.p).fill });
48288
+ }
48289
+ clusters.push({
48290
+ id: clusterId,
48291
+ cx: cx0 + dx,
48292
+ cy: cy0 + dy,
48293
+ count: g.length,
48294
+ hitR: ringR + maxR + 6,
48295
+ legs: legsOut
47271
48296
  });
47272
48297
  }
47273
48298
  const legs = [];
@@ -47317,16 +48342,26 @@ function layoutMap(resolved, data, size, opts) {
47317
48342
  if (!a || !b) continue;
47318
48343
  const mx = (a.cx + b.cx) / 2;
47319
48344
  const my = (a.cy + b.cy) / 2;
48345
+ const bow = {
48346
+ curved: leg.style === "arc",
48347
+ offset: 0,
48348
+ labelX: mx,
48349
+ labelY: my - 4
48350
+ };
48351
+ const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
47320
48352
  legs.push({
47321
- d: legPath(a, b, leg.style === "arc", 0),
48353
+ d: legPath(a, b, bow.curved, bow.offset),
47322
48354
  width: routeWidthFor(Number(leg.value)),
47323
48355
  color: mix(palette.text, palette.bg, 72),
47324
48356
  arrow: true,
47325
48357
  lineNumber: leg.lineNumber,
47326
48358
  ...leg.label !== void 0 && {
47327
48359
  label: leg.label,
47328
- labelX: mx,
47329
- labelY: my - 4
48360
+ labelX: bow.labelX,
48361
+ labelY: bow.labelY,
48362
+ labelColor: routeLabelStyle.color,
48363
+ labelHalo: routeLabelStyle.halo,
48364
+ labelHaloColor: routeLabelStyle.haloColor
47330
48365
  }
47331
48366
  });
47332
48367
  }
@@ -47354,20 +48389,29 @@ function layoutMap(resolved, data, size, opts) {
47354
48389
  const a = poiScreen.get(e.fromId);
47355
48390
  const b = poiScreen.get(e.toId);
47356
48391
  if (!a || !b) return;
47357
- const curved = e.style === "arc" || n > 1;
47358
- const offset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
48392
+ const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
47359
48393
  const mx = (a.cx + b.cx) / 2;
47360
48394
  const my = (a.cy + b.cy) / 2;
48395
+ const bow = {
48396
+ curved: e.style === "arc" || n > 1,
48397
+ offset: fanOffset,
48398
+ labelX: mx,
48399
+ labelY: my - 4
48400
+ };
48401
+ const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
47361
48402
  legs.push({
47362
- d: legPath(a, b, curved, offset),
48403
+ d: legPath(a, b, bow.curved, bow.offset),
47363
48404
  width: widthFor(e),
47364
48405
  color: mix(palette.text, palette.bg, 66),
47365
48406
  arrow: e.directed,
47366
48407
  lineNumber: e.lineNumber,
47367
48408
  ...e.label !== void 0 && {
47368
48409
  label: e.label,
47369
- labelX: mx,
47370
- labelY: my - 4
48410
+ labelX: bow.labelX,
48411
+ labelY: bow.labelY,
48412
+ labelColor: edgeLabelStyle.color,
48413
+ labelHalo: edgeLabelStyle.halo,
48414
+ labelHaloColor: edgeLabelStyle.haloColor
47371
48415
  }
47372
48416
  });
47373
48417
  });
@@ -47409,25 +48453,25 @@ function layoutMap(resolved, data, size, opts) {
47409
48453
  }
47410
48454
  }
47411
48455
  const collides = (rect) => markers.some((m) => rectCircleOverlap(rect, m)) || obstacles.some((o) => rectsOverlap(rect, o)) || legSegments.some((s) => segmentRectOverlap(s[0], s[1], s[2], s[3], rect));
47412
- const regionLabelMode = resolved.directives.regionLabels ?? "off";
48456
+ const showRegionLabels = resolved.directives.noRegionLabels !== true;
48457
+ const isCompact = width < COMPACT_WIDTH_PX;
47413
48458
  const LABEL_PADX = 6;
47414
48459
  const LABEL_PADY = 3;
47415
- const labelW = (text) => measureLegendText(text, FONT) + 2 * LABEL_PADX;
47416
- const labelH = FONT + 2 * LABEL_PADY;
48460
+ const labelW = (text) => measureLegendText(text, FONT2) + 2 * LABEL_PADX;
48461
+ const labelH = FONT2 + 2 * LABEL_PADY;
47417
48462
  const pushRegionLabel = (x, y, text, fill2, lineNumber) => {
47418
- const color = contrastText(
47419
- fill2,
47420
- palette.textOnFillLight,
47421
- palette.textOnFillDark
48463
+ const { color, haloColor } = labelOnFill(fill2);
48464
+ const halfW = measureLegendText(text, FONT2) / 2;
48465
+ const overflows = [y - FONT2 * 0.55, y - FONT2 * 0.1].some(
48466
+ (sy) => fillAt(x - halfW, sy) !== fill2 || fillAt(x + halfW, sy) !== fill2
47422
48467
  );
47423
- const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
47424
48468
  labels.push({
47425
48469
  x,
47426
48470
  y,
47427
48471
  text,
47428
48472
  anchor: "middle",
47429
48473
  color,
47430
- halo: true,
48474
+ halo: overflows,
47431
48475
  haloColor,
47432
48476
  lineNumber
47433
48477
  });
@@ -47436,21 +48480,50 @@ function layoutMap(resolved, data, size, opts) {
47436
48480
  US: [-98.5, 39.5]
47437
48481
  // CONUS geographic centre (near Lebanon, Kansas)
47438
48482
  };
47439
- if (regionLabelMode === "full" || regionLabelMode === "abbrev") {
47440
- for (const r of regions) {
47441
- if (r.layer === "base" || r.label === void 0) continue;
47442
- const f = r.layer === "us-state" ? usLayer?.get(r.id) : worldLayer.get(r.id);
47443
- if (!f) continue;
48483
+ const REGION_LABEL_GAP = 2;
48484
+ const regionLabelRect = (cx, cy, text) => {
48485
+ const w = measureLegendText(text, FONT2) + 2 * REGION_LABEL_GAP;
48486
+ return { x: cx - w / 2, y: cy - FONT2 / 2, w, h: FONT2 };
48487
+ };
48488
+ if (showRegionLabels) {
48489
+ const frameContainers = new Set(resolved.poiFrameContainers);
48490
+ const entries = regions.map((r) => {
48491
+ const isContainer = frameContainers.has(r.id);
48492
+ if (r.layer === "base" && !isContainer || r.label === void 0)
48493
+ return null;
48494
+ const isUsState = r.layer === "us-state" || r.id.startsWith("US-");
48495
+ const f = isUsState ? usLayer?.get(r.id) : worldLayer.get(r.id);
48496
+ if (!f) return null;
47444
48497
  const [[x0, y0], [x1, y1]] = path.bounds(f);
47445
- const text = regionLabelMode === "abbrev" ? r.id.replace(/^US-/, "") : r.label;
47446
- if (labelW(text) > x1 - x0 || labelH > y1 - y0) continue;
47447
- const anchor = r.layer !== "us-state" ? WORLD_LABEL_ANCHORS[r.id] : void 0;
48498
+ const boxW = x1 - x0;
48499
+ const boxH = y1 - y0;
48500
+ const abbrev = isUsState ? r.id.replace(/^US-/, "") : void 0;
48501
+ const candidates = abbrev !== void 0 ? isCompact ? [abbrev, r.label] : [r.label, abbrev] : [r.label];
48502
+ const anchor = !isUsState ? WORLD_LABEL_ANCHORS[r.id] : void 0;
47448
48503
  const c = anchor ? project(anchor[0], anchor[1]) : path.centroid(f);
47449
- if (!c || !Number.isFinite(c[0])) continue;
48504
+ if (!c || !Number.isFinite(c[0])) return null;
48505
+ return { r, c, boxW, boxH, area: boxW * boxH, candidates };
48506
+ }).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
48507
+ const placedRegionRects = [];
48508
+ const POI_LABEL_PAD = 14;
48509
+ const poiObstacles = pois.map((p) => ({
48510
+ x: p.cx - p.r - POI_LABEL_PAD,
48511
+ y: p.cy - p.r - POI_LABEL_PAD,
48512
+ w: 2 * (p.r + POI_LABEL_PAD),
48513
+ h: 2 * (p.r + POI_LABEL_PAD)
48514
+ }));
48515
+ for (const { r, c, boxW, boxH, candidates } of entries) {
48516
+ const text = candidates.find((t) => {
48517
+ if (labelW(t) > boxW || labelH > boxH) return false;
48518
+ const rect = regionLabelRect(c[0], c[1], t);
48519
+ return !placedRegionRects.some((p) => rectsOverlap(rect, p)) && !poiObstacles.some((o) => rectsOverlap(rect, o));
48520
+ });
48521
+ if (text === void 0) continue;
48522
+ placedRegionRects.push(regionLabelRect(c[0], c[1], text));
47450
48523
  pushRegionLabel(c[0], c[1], text, r.fill, r.lineNumber);
47451
48524
  }
47452
48525
  for (const seed of insetLabelSeeds) {
47453
- const text = regionLabelMode === "abbrev" ? seed.iso.replace(/^US-/, "") : seed.name;
48526
+ const text = isCompact ? seed.iso.replace(/^US-/, "") : seed.name;
47454
48527
  const src = regionById.get(seed.iso);
47455
48528
  pushRegionLabel(
47456
48529
  seed.x,
@@ -47461,22 +48534,26 @@ function layoutMap(resolved, data, size, opts) {
47461
48534
  );
47462
48535
  }
47463
48536
  }
47464
- const poiLabelMode = resolved.directives.poiLabels ?? "auto";
47465
- if (poiLabelMode !== "off") {
47466
- const ordered = [...pois].sort(
47467
- (a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1)
47468
- );
48537
+ if (resolved.directives.noPoiLabels !== true) {
48538
+ const ordered = [...pois].filter((p) => p.clusterId === void 0).sort((a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1));
47469
48539
  const poiById = new Map(resolved.pois.map((q) => [q.id, q]));
47470
48540
  const labelText = (p) => {
47471
48541
  const src = poiById.get(p.id);
47472
48542
  return src?.label ?? src?.name ?? p.id;
47473
48543
  };
47474
- const poiLabH = FONT * 1.25;
48544
+ const poiLabH = FONT2 * 1.25;
47475
48545
  const labelInfo = (p) => {
47476
48546
  const text = labelText(p);
47477
- return { text, w: measureLegendText(text, FONT) };
48547
+ return { text, w: measureLegendText(text, FONT2) };
47478
48548
  };
47479
48549
  const GAP = 3;
48550
+ const clusterMembersById = /* @__PURE__ */ new Map();
48551
+ for (const p of pois) {
48552
+ if (p.clusterId === void 0) continue;
48553
+ const arr = clusterMembersById.get(p.clusterId);
48554
+ if (arr) arr.push(p);
48555
+ else clusterMembersById.set(p.clusterId, [p]);
48556
+ }
47480
48557
  const inlineRect = (p, w, side) => {
47481
48558
  switch (side) {
47482
48559
  case "right":
@@ -47506,11 +48583,11 @@ function layoutMap(resolved, data, size, opts) {
47506
48583
  const x = side === "right" ? rect.x : side === "left" ? rect.x + w : p.cx;
47507
48584
  labels.push({
47508
48585
  x,
47509
- y: rect.y + poiLabH / 2 + FONT / 3,
48586
+ y: rect.y + poiLabH / 2 + FONT2 / 3,
47510
48587
  text,
47511
48588
  anchor,
47512
48589
  color: palette.text,
47513
- halo: true,
48590
+ halo: false,
47514
48591
  haloColor: palette.bg,
47515
48592
  poiId: p.id,
47516
48593
  lineNumber: p.lineNumber
@@ -47521,43 +48598,60 @@ function layoutMap(resolved, data, size, opts) {
47521
48598
  return rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect);
47522
48599
  };
47523
48600
  const GROUP_R = 30;
47524
- const groups = [];
48601
+ const groups2 = [];
47525
48602
  for (const p of ordered) {
47526
- const near = groups.find(
48603
+ const near = groups2.find(
47527
48604
  (g) => g.some((q) => Math.hypot(q.cx - p.cx, q.cy - p.cy) < GROUP_R)
47528
48605
  );
47529
48606
  if (near) near.push(p);
47530
- else groups.push([p]);
48607
+ else groups2.push([p]);
47531
48608
  }
47532
48609
  const ROW_GAP2 = 3;
47533
48610
  const step = poiLabH + ROW_GAP2;
47534
48611
  const COL_GAP = 16;
47535
- const placeColumn = (group) => {
47536
- const items = group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
48612
+ const makeItems = (group) => group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
48613
+ const columnRows = (items, side) => {
47537
48614
  const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
47538
48615
  const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
47539
- const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
47540
48616
  const maxW = Math.max(...items.map((o) => o.w));
47541
- const side = right + COL_GAP + maxW <= width - 2 ? "right" : "left";
47542
- const colX = side === "right" ? right + COL_GAP : left - COL_GAP;
48617
+ const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
48618
+ const colX = side === "right" ? Math.min(right + COL_GAP, width - 2 - maxW) : Math.max(left - COL_GAP, 2 + maxW);
47543
48619
  const totalH = items.length * step;
47544
48620
  let startY = cyMid - totalH / 2;
47545
48621
  startY = Math.max(2, Math.min(startY, height - totalH - 2));
47546
- items.forEach((o, i) => {
48622
+ return items.map((o, i) => {
47547
48623
  const rowCy = startY + i * step + step / 2;
47548
- obstacles.push({
47549
- x: side === "right" ? colX : colX - o.w,
47550
- y: rowCy - poiLabH / 2,
47551
- w: o.w,
47552
- h: poiLabH
47553
- });
48624
+ return {
48625
+ o,
48626
+ colX,
48627
+ rowCy,
48628
+ rect: {
48629
+ x: side === "right" ? colX : colX - o.w,
48630
+ y: rowCy - poiLabH / 2,
48631
+ w: o.w,
48632
+ h: poiLabH
48633
+ }
48634
+ };
48635
+ });
48636
+ };
48637
+ const wouldColumnBeClean = (items, side) => columnRows(items, side).every(
48638
+ ({ rect }) => rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect)
48639
+ );
48640
+ const defaultColumnSide = (items) => {
48641
+ const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
48642
+ const maxW = Math.max(...items.map((o) => o.w));
48643
+ return right + COL_GAP + maxW <= width - 2 ? "right" : "left";
48644
+ };
48645
+ const commitColumn = (items, side, clusterId) => {
48646
+ for (const { o, colX, rowCy, rect } of columnRows(items, side)) {
48647
+ obstacles.push(rect);
47554
48648
  labels.push({
47555
48649
  x: colX,
47556
- y: rowCy + FONT / 3,
48650
+ y: rowCy + FONT2 / 3,
47557
48651
  text: o.text,
47558
48652
  anchor: side === "right" ? "start" : "end",
47559
48653
  color: palette.text,
47560
- halo: true,
48654
+ halo: false,
47561
48655
  haloColor: palette.bg,
47562
48656
  leader: {
47563
48657
  x1: o.p.cx,
@@ -47567,24 +48661,141 @@ function layoutMap(resolved, data, size, opts) {
47567
48661
  },
47568
48662
  leaderColor: o.p.fill,
47569
48663
  poiId: o.p.id,
47570
- lineNumber: o.p.lineNumber
48664
+ lineNumber: o.p.lineNumber,
48665
+ ...clusterId !== void 0 && { clusterMember: clusterId }
47571
48666
  });
48667
+ }
48668
+ };
48669
+ const pushHidden = (p) => {
48670
+ const { text, w } = labelInfo(p);
48671
+ let x = p.cx + p.r + GAP;
48672
+ let anchor = "start";
48673
+ if (x + w > width) {
48674
+ x = p.cx - p.r - GAP - w;
48675
+ anchor = "end";
48676
+ }
48677
+ const y = Math.max(0, Math.min(p.cy - poiLabH / 2, height - poiLabH));
48678
+ labels.push({
48679
+ x: anchor === "start" ? x : x + w,
48680
+ y: y + poiLabH / 2 + FONT2 / 3,
48681
+ text,
48682
+ anchor,
48683
+ color: palette.text,
48684
+ halo: false,
48685
+ haloColor: palette.bg,
48686
+ poiId: p.id,
48687
+ hidden: true,
48688
+ lineNumber: p.lineNumber
47572
48689
  });
47573
48690
  };
47574
- for (const g of groups) {
48691
+ for (const [clusterId, members] of clusterMembersById) {
48692
+ if (members.length === 0) continue;
48693
+ const items = makeItems(members);
48694
+ const side = wouldColumnBeClean(items, "right") ? "right" : wouldColumnBeClean(items, "left") ? "left" : defaultColumnSide(items);
48695
+ commitColumn(items, side, clusterId);
48696
+ }
48697
+ const maxExtent = MAX_CLUSTER_EXTENT_FACTOR * Math.min(width, height);
48698
+ const clusterPending = [];
48699
+ for (const g of groups2) {
48700
+ const items = makeItems(g);
47575
48701
  if (g.length === 1) {
47576
- const p = g[0];
47577
- const { text, w } = labelInfo(p);
48702
+ const { p, text, w } = items[0];
47578
48703
  const side = ["right", "left", "above", "below"].find(
47579
48704
  (s) => inlineFits(p, w, s)
47580
48705
  );
47581
- if (side) {
47582
- pushInline(p, text, w, side);
47583
- continue;
48706
+ if (side) pushInline(p, text, w, side);
48707
+ else commitColumn(items, defaultColumnSide(items));
48708
+ continue;
48709
+ }
48710
+ const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
48711
+ const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
48712
+ const minCy = Math.min(...items.map((o) => o.p.cy));
48713
+ const maxCy = Math.max(...items.map((o) => o.p.cy));
48714
+ const diag = Math.hypot(right - left, maxCy - minCy);
48715
+ if (diag > maxExtent || items.length > MAX_COLUMN_ROWS) {
48716
+ items.forEach((o) => pushHidden(o.p));
48717
+ } else {
48718
+ clusterPending.push(items);
48719
+ }
48720
+ }
48721
+ for (const items of clusterPending) {
48722
+ const side = ["right", "left"].find(
48723
+ (s) => wouldColumnBeClean(items, s)
48724
+ );
48725
+ if (side) commitColumn(items, side);
48726
+ else items.forEach((o) => pushHidden(o.p));
48727
+ }
48728
+ }
48729
+ if (resolved.directives.noContextLabels !== true) {
48730
+ for (const l of labels) {
48731
+ if (l.hidden) continue;
48732
+ const w = labelW(l.text);
48733
+ const x = l.anchor === "start" ? l.x : l.anchor === "end" ? l.x - w : l.x - w / 2;
48734
+ obstacles.push({ x, y: l.y - labelH / 2, w, h: labelH });
48735
+ }
48736
+ for (const box of insets)
48737
+ obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
48738
+ const countryCandidates = [];
48739
+ for (const f of worldLayer.values()) {
48740
+ const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
48741
+ if (!iso || regionById.has(iso)) continue;
48742
+ let hasReferencedSub = false;
48743
+ for (const k of regionById.keys())
48744
+ if (k.startsWith(iso + "-")) {
48745
+ hasReferencedSub = true;
48746
+ break;
47584
48747
  }
48748
+ if (hasReferencedSub) continue;
48749
+ const b = path.bounds(f);
48750
+ const [x0, y0] = b[0];
48751
+ const [x1, y1] = b[1];
48752
+ if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
48753
+ const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
48754
+ const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
48755
+ countryCandidates.push({
48756
+ name: f.properties?.name ?? iso,
48757
+ bbox: [x0, y0, x1, y1],
48758
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
48759
+ });
48760
+ }
48761
+ const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
48762
+ (id) => id.startsWith("US-")
48763
+ );
48764
+ if (usLayer && framedStateContainers) {
48765
+ const containerSet = new Set(resolved.poiFrameContainers);
48766
+ for (const [iso, f] of usLayer) {
48767
+ if (containerSet.has(iso) || regionById.has(iso)) continue;
48768
+ const viewF = cullFeatureToView(f);
48769
+ if (!viewF) continue;
48770
+ const b = path.bounds(viewF);
48771
+ const [x0, y0] = b[0];
48772
+ const [x1, y1] = b[1];
48773
+ if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
48774
+ const a = path.centroid(viewF);
48775
+ countryCandidates.push({
48776
+ name: f.properties?.name ?? iso,
48777
+ bbox: [x0, y0, x1, y1],
48778
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
48779
+ });
47585
48780
  }
47586
- placeColumn(g);
47587
48781
  }
48782
+ const contextLabels = placeContextLabels({
48783
+ projection: resolved.projection,
48784
+ dLonSpan,
48785
+ dLatSpan,
48786
+ width,
48787
+ height,
48788
+ waterBodies: data.waterBodies,
48789
+ countries: countryCandidates,
48790
+ palette,
48791
+ project,
48792
+ collides,
48793
+ // Water labels must stay over open water — `fillAt` returns the ocean
48794
+ // backdrop colour off-land and a region fill on-land (lakes/states count
48795
+ // as land here, which is the safe side for an ocean name).
48796
+ overLand: (x, y) => fillAt(x, y) !== water
48797
+ });
48798
+ labels.push(...contextLabels);
47588
48799
  }
47589
48800
  let legend = null;
47590
48801
  if (!resolved.directives.noLegend) {
@@ -47621,25 +48832,31 @@ function layoutMap(resolved, data, size, opts) {
47621
48832
  rivers,
47622
48833
  relief,
47623
48834
  reliefHatch,
48835
+ coastlineStyle,
47624
48836
  legs,
47625
48837
  pois,
48838
+ clusters,
47626
48839
  labels,
47627
48840
  legend,
47628
48841
  insets,
47629
48842
  insetRegions,
47630
48843
  projection,
47631
- stretch: stretchParams
48844
+ stretch: stretchParams,
48845
+ diagnostics: []
47632
48846
  };
47633
48847
  }
47634
- var FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT, COLO_EPS, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK, WATER_TINT_LIGHT, WATER_TINT_DARK, RIVER_WIDTH, RELIEF_MIN_AREA, RELIEF_MIN_DIM, RELIEF_HATCH_SPACING, RELIEF_HATCH_WIDTH, RELIEF_HATCH_STRENGTH, FOREIGN_TINT_LIGHT, FOREIGN_TINT_DARK, MUTED_FOREIGN_LIGHT, MUTED_FOREIGN_DARK, COLO_R, GOLDEN_ANGLE, FAN_STEP, ARC_CURVE_FRAC, usConusProjection, alaskaProjection, hawaiiProjection, INSET_STATES, US_NON_CONUS;
48848
+ var FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT2, MAX_CLUSTER_EXTENT_FACTOR, MAX_COLUMN_ROWS, REGION_LABEL_HALO_RATIO, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK, WATER_TINT_LIGHT, WATER_TINT_DARK, RIVER_WIDTH, COMPACT_WIDTH_PX, RELIEF_MIN_AREA, RELIEF_MIN_DIM, RELIEF_HATCH_SPACING, RELIEF_HATCH_WIDTH, RELIEF_HATCH_STRENGTH, COASTLINE_RING_COUNT, COASTLINE_D0, COASTLINE_STEP, COASTLINE_THICKNESS, COASTLINE_OPACITY_NEAR, COASTLINE_OPACITY_FAR, COASTLINE_MIN_EXTENT, COASTLINE_MIN_EXTENT_GLOBAL, COASTLINE_STROKE_MIX, FOREIGN_TINT_LIGHT, FOREIGN_TINT_DARK, MUTED_FOREIGN_LIGHT, MUTED_FOREIGN_DARK, COLO_R, GOLDEN_ANGLE, STACK_OVERLAP, STACK_RING_MAX, STACK_RING_GAP, FAN_STEP, ARC_CURVE_FRAC, decodeCache, usConusProjection, alaskaProjection, hawaiiProjection, INSET_STATES, inAlaska, inHawaii, FOREIGN_BORDER, US_NON_CONUS;
47635
48849
  var init_layout15 = __esm({
47636
48850
  "src/map/layout.ts"() {
47637
48851
  "use strict";
47638
48852
  init_color_utils();
48853
+ init_geo();
48854
+ init_colorize();
47639
48855
  init_colors();
47640
48856
  init_label_layout();
47641
48857
  init_legend_constants();
47642
48858
  init_title_constants();
48859
+ init_context_labels();
47643
48860
  FIT_PAD = 24;
47644
48861
  RAMP_FLOOR = 15;
47645
48862
  R_DEFAULT = 6;
@@ -47647,32 +48864,66 @@ var init_layout15 = __esm({
47647
48864
  R_MAX = 22;
47648
48865
  W_MIN = 1.25;
47649
48866
  W_MAX = 8;
47650
- FONT = 11;
47651
- COLO_EPS = 1.5;
48867
+ FONT2 = 11;
48868
+ MAX_CLUSTER_EXTENT_FACTOR = 0.18;
48869
+ MAX_COLUMN_ROWS = 7;
48870
+ REGION_LABEL_HALO_RATIO = 4.5;
47652
48871
  LAND_TINT_LIGHT = 12;
47653
48872
  LAND_TINT_DARK = 24;
47654
48873
  TAG_TINT_LIGHT = 60;
47655
48874
  TAG_TINT_DARK = 68;
47656
- WATER_TINT_LIGHT = 13;
47657
- WATER_TINT_DARK = 14;
48875
+ WATER_TINT_LIGHT = 24;
48876
+ WATER_TINT_DARK = 24;
47658
48877
  RIVER_WIDTH = 1.3;
48878
+ COMPACT_WIDTH_PX = 480;
47659
48879
  RELIEF_MIN_AREA = 12;
47660
48880
  RELIEF_MIN_DIM = 2;
47661
- RELIEF_HATCH_SPACING = 3;
47662
- RELIEF_HATCH_WIDTH = 0.25;
48881
+ RELIEF_HATCH_SPACING = 2;
48882
+ RELIEF_HATCH_WIDTH = 0.15;
47663
48883
  RELIEF_HATCH_STRENGTH = 32;
48884
+ COASTLINE_RING_COUNT = 5;
48885
+ COASTLINE_D0 = 16e-4;
48886
+ COASTLINE_STEP = 28e-4;
48887
+ COASTLINE_THICKNESS = 14e-4;
48888
+ COASTLINE_OPACITY_NEAR = 0.5;
48889
+ COASTLINE_OPACITY_FAR = 0.1;
48890
+ COASTLINE_MIN_EXTENT = 6e-4;
48891
+ COASTLINE_MIN_EXTENT_GLOBAL = 6e-4;
48892
+ COASTLINE_STROKE_MIX = 32;
47664
48893
  FOREIGN_TINT_LIGHT = 30;
47665
48894
  FOREIGN_TINT_DARK = 62;
47666
48895
  MUTED_FOREIGN_LIGHT = 28;
47667
48896
  MUTED_FOREIGN_DARK = 16;
47668
48897
  COLO_R = 9;
47669
48898
  GOLDEN_ANGLE = 2.399963229728653;
48899
+ STACK_OVERLAP = 1;
48900
+ STACK_RING_MAX = 8;
48901
+ STACK_RING_GAP = 4;
47670
48902
  FAN_STEP = 16;
47671
48903
  ARC_CURVE_FRAC = 0.18;
48904
+ decodeCache = /* @__PURE__ */ new WeakMap();
47672
48905
  usConusProjection = () => geoConicEqualArea().parallels([29.5, 45.5]).rotate([96, 0]);
47673
48906
  alaskaProjection = () => geoConicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]);
47674
48907
  hawaiiProjection = () => geoMercator();
47675
48908
  INSET_STATES = /* @__PURE__ */ new Set(["US-AK", "US-HI"]);
48909
+ inAlaska = (lon, lat) => lat >= 51 && (lon <= -129 || lon >= 172);
48910
+ inHawaii = (lon, lat) => lat >= 18 && lat <= 23 && lon >= -161 && lon <= -154;
48911
+ FOREIGN_BORDER = {
48912
+ CA: [
48913
+ "US-AK",
48914
+ "US-WA",
48915
+ "US-ID",
48916
+ "US-MT",
48917
+ "US-ND",
48918
+ "US-MN",
48919
+ "US-MI",
48920
+ "US-NY",
48921
+ "US-VT",
48922
+ "US-NH",
48923
+ "US-ME"
48924
+ ],
48925
+ MX: ["US-CA", "US-AZ", "US-NM", "US-TX"]
48926
+ };
47676
48927
  US_NON_CONUS = /* @__PURE__ */ new Set([
47677
48928
  "US-AK",
47678
48929
  "US-HI",
@@ -47692,6 +48943,58 @@ __export(renderer_exports16, {
47692
48943
  renderMapForExport: () => renderMapForExport
47693
48944
  });
47694
48945
  import * as d3Selection18 from "d3-selection";
48946
+ function pointInRing2(px, py, ring) {
48947
+ let inside = false;
48948
+ for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
48949
+ const [xi, yi] = ring[i];
48950
+ const [xj, yj] = ring[j];
48951
+ if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
48952
+ inside = !inside;
48953
+ }
48954
+ return inside;
48955
+ }
48956
+ function ringToPath(ring) {
48957
+ let d = "";
48958
+ for (let i = 0; i < ring.length; i++)
48959
+ d += (i ? "L" : "M") + ring[i][0] + "," + ring[i][1];
48960
+ return d + "Z";
48961
+ }
48962
+ function coastlineOuterRings(regions, minExtent) {
48963
+ const paths = [];
48964
+ for (const r of regions) {
48965
+ const rings = parsePathRings(r.d);
48966
+ for (let i = 0; i < rings.length; i++) {
48967
+ const ring = rings[i];
48968
+ if (ring.length < 3) continue;
48969
+ let minX = Infinity;
48970
+ let minY = Infinity;
48971
+ let maxX = -Infinity;
48972
+ let maxY = -Infinity;
48973
+ for (const [x, y] of ring) {
48974
+ if (x < minX) minX = x;
48975
+ if (x > maxX) maxX = x;
48976
+ if (y < minY) minY = y;
48977
+ if (y > maxY) maxY = y;
48978
+ }
48979
+ if (Math.max(maxX - minX, maxY - minY) < minExtent) continue;
48980
+ const [fx, fy] = ring[0];
48981
+ let depth = 0;
48982
+ for (let j = 0; j < rings.length; j++)
48983
+ if (j !== i && pointInRing2(fx, fy, rings[j])) depth++;
48984
+ if (depth % 2 === 1) continue;
48985
+ paths.push(ringToPath(ring));
48986
+ }
48987
+ }
48988
+ return paths;
48989
+ }
48990
+ function appendWaterLines(g, outerRings, style, flatWater) {
48991
+ const d = outerRings.join(" ");
48992
+ const linesOuterFirst = [...style.lines].sort((a, b) => b.d - a.d);
48993
+ for (const line12 of linesOuterFirst) {
48994
+ g.append("path").attr("d", d).attr("stroke", style.color).attr("stroke-width", 2 * (line12.d + line12.thickness)).attr("stroke-opacity", line12.opacity).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
48995
+ g.append("path").attr("d", d).attr("stroke", flatWater).attr("stroke-width", 2 * line12.d).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
48996
+ }
48997
+ }
47695
48998
  function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims, activeGroupOverride) {
47696
48999
  d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
47697
49000
  const width = exportDims?.width ?? container.clientWidth;
@@ -47704,6 +49007,11 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47704
49007
  {
47705
49008
  palette,
47706
49009
  isDark,
49010
+ // Export-only: forward the contain-fit request from mapExportDimensions so a
49011
+ // clamped/floored (off-aspect) export canvas letterboxes instead of
49012
+ // stretch-distorting. The in-app preview pane passes no exportDims → unset →
49013
+ // keeps the global stretch-fill.
49014
+ preferContain: exportDims?.preferContain ?? false,
47707
49015
  ...activeGroupOverride !== void 0 && {
47708
49016
  activeGroup: activeGroupOverride
47709
49017
  }
@@ -47717,6 +49025,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47717
49025
  const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
47718
49026
  const drawRegion = (g, r, strokeWidth) => {
47719
49027
  const p = g.append("path").attr("d", r.d).attr("fill", r.fill).attr("stroke", r.stroke).attr("stroke-width", strokeWidth);
49028
+ if (r.label) p.attr("data-region-name", r.label);
47720
49029
  if (r.layer !== "base") {
47721
49030
  p.classed("dgmo-map-region", true).attr("data-region", r.id);
47722
49031
  if (r.value !== void 0) p.attr("data-value", r.value);
@@ -47751,6 +49060,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47751
49060
  gRelief.append("line").attr("x1", 0).attr("y1", y).attr("x2", width).attr("y2", y);
47752
49061
  }
47753
49062
  }
49063
+ if (layout.coastlineStyle) {
49064
+ const cs = layout.coastlineStyle;
49065
+ const maskId = "dgmo-map-water-mask";
49066
+ const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
49067
+ mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
49068
+ const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
49069
+ const lakeD = layout.regions.filter((r) => r.id === "lake").map((r) => r.d).join(" ");
49070
+ if (landD) mask.append("path").attr("d", landD).attr("fill", "black");
49071
+ if (lakeD) mask.append("path").attr("d", lakeD).attr("fill", "white");
49072
+ if (layout.insets.length) {
49073
+ const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
49074
+ for (const box of layout.insets) {
49075
+ const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
49076
+ mask.append("path").attr("d", d).attr("fill", "black").attr("stroke", "black").attr("stroke-width", 2 * reach).attr("stroke-linejoin", "round");
49077
+ }
49078
+ }
49079
+ const gWater = svg.append("g").attr("class", "dgmo-map-water-lines").attr("fill", "none").attr("mask", `url(#${maskId})`);
49080
+ appendWaterLines(
49081
+ gWater,
49082
+ coastlineOuterRings(layout.regions, cs.minExtent),
49083
+ cs,
49084
+ layout.background
49085
+ );
49086
+ const byStroke = /* @__PURE__ */ new Map();
49087
+ for (const r of layout.regions) {
49088
+ const arr = byStroke.get(r.stroke);
49089
+ if (arr) arr.push(r.d);
49090
+ else byStroke.set(r.stroke, [r.d]);
49091
+ }
49092
+ for (const [stroke2, ds] of byStroke)
49093
+ gWater.append("path").attr("d", ds.join(" ")).attr("stroke", stroke2).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
49094
+ }
47754
49095
  if (layout.rivers.length) {
47755
49096
  const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
47756
49097
  for (const r of layout.rivers) {
@@ -47759,15 +49100,61 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47759
49100
  }
47760
49101
  if (layout.insets.length) {
47761
49102
  const insetG = svg.append("g").attr("class", "dgmo-map-insets");
47762
- for (const box of layout.insets) {
49103
+ layout.insets.forEach((box, bi) => {
47763
49104
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
47764
49105
  insetG.append("path").attr("d", d).attr("fill", layout.background).attr("stroke", mix(palette.text, palette.bg, 55)).attr("stroke-width", 1).attr("stroke-linejoin", "round");
47765
- }
49106
+ if (box.contextLand) {
49107
+ const clipId = `dgmo-map-inset-clip-${bi}`;
49108
+ defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
49109
+ insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
49110
+ }
49111
+ });
47766
49112
  for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
47767
- }
49113
+ if (layout.coastlineStyle) {
49114
+ const cs = layout.coastlineStyle;
49115
+ const maskId = "dgmo-map-inset-water-mask";
49116
+ const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
49117
+ for (const box of layout.insets) {
49118
+ const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
49119
+ mask.append("path").attr("d", d).attr("fill", "white");
49120
+ }
49121
+ layout.insets.forEach((box, bi) => {
49122
+ if (box.contextLand)
49123
+ mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#dgmo-map-inset-clip-${bi})`);
49124
+ });
49125
+ for (const r of layout.insetRegions)
49126
+ if (r.id !== "lake")
49127
+ mask.append("path").attr("d", r.d).attr("fill", "black");
49128
+ for (const r of layout.insetRegions)
49129
+ if (r.id === "lake")
49130
+ mask.append("path").attr("d", r.d).attr("fill", "white");
49131
+ const clipId = "dgmo-map-inset-water-clip";
49132
+ const clip = defs.append("clipPath").attr("id", clipId);
49133
+ for (const box of layout.insets) {
49134
+ const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
49135
+ clip.append("path").attr("d", d);
49136
+ }
49137
+ const gInsetWater = insetG.append("g").attr("clip-path", `url(#${clipId})`).append("g").attr("class", "dgmo-map-inset-water-lines").attr("fill", "none").attr("mask", `url(#${maskId})`);
49138
+ appendWaterLines(
49139
+ gInsetWater,
49140
+ coastlineOuterRings(layout.insetRegions, cs.minExtent),
49141
+ cs,
49142
+ layout.background
49143
+ );
49144
+ for (const r of layout.insetRegions)
49145
+ gInsetWater.append("path").attr("d", r.d).attr("stroke", r.stroke).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
49146
+ }
49147
+ }
49148
+ const wireSync = (sel, lineNumber) => {
49149
+ if (lineNumber < 1) return;
49150
+ sel.attr("data-line-number", lineNumber);
49151
+ if (onClickItem)
49152
+ sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
49153
+ };
47768
49154
  const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
47769
49155
  layout.legs.forEach((leg, i) => {
47770
49156
  const p = gLegs.append("path").attr("d", leg.d).attr("stroke", leg.color).attr("stroke-width", leg.width).attr("stroke-linecap", "round");
49157
+ wireSync(p, leg.lineNumber);
47771
49158
  if (leg.arrow) {
47772
49159
  const id = `dgmo-map-arrow-${i}`;
47773
49160
  const s = arrowSize(leg.width);
@@ -47775,25 +49162,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47775
49162
  p.attr("marker-end", `url(#${id})`);
47776
49163
  }
47777
49164
  if (leg.label !== void 0 && leg.labelX !== void 0) {
47778
- emitText(
49165
+ const lt = emitText(
47779
49166
  gLegs,
47780
49167
  leg.labelX,
47781
49168
  leg.labelY ?? 0,
47782
49169
  leg.label,
47783
49170
  "middle",
47784
- palette.textMuted,
47785
- haloColor,
47786
- true,
49171
+ leg.labelColor ?? palette.textMuted,
49172
+ leg.labelHaloColor ?? haloColor,
49173
+ leg.labelHalo ?? true,
47787
49174
  LABEL_FONT - 1
47788
49175
  );
49176
+ wireSync(lt, leg.lineNumber);
47789
49177
  }
47790
49178
  });
49179
+ const gSpider = svg.append("g").attr("class", "dgmo-map-spider");
49180
+ for (const cl of layout.clusters) {
49181
+ if (!exportDims) {
49182
+ gSpider.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", cl.hitR).attr("fill", "transparent").attr("data-cluster-hit", cl.id).style("cursor", "pointer");
49183
+ }
49184
+ for (const leg of cl.legs) {
49185
+ gSpider.append("line").attr("x1", cl.cx).attr("y1", cl.cy).attr("x2", leg.x2).attr("y2", leg.y2).attr("stroke", leg.color).attr("stroke-width", 1).attr("data-cluster-deco", cl.id).style("pointer-events", "none");
49186
+ }
49187
+ gSpider.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", 2).attr("fill", mix(palette.textMuted, palette.bg, 40)).attr("data-cluster-deco", cl.id).style("pointer-events", "none");
49188
+ }
47791
49189
  const gPois = svg.append("g").attr("class", "dgmo-map-pois");
47792
49190
  for (const poi of layout.pois) {
47793
49191
  if (poi.isOrigin) {
47794
49192
  gPois.append("circle").attr("cx", poi.cx).attr("cy", poi.cy).attr("r", poi.r + 3).attr("fill", "none").attr("stroke", poi.stroke).attr("stroke-width", 1.5);
47795
49193
  }
47796
49194
  const c = gPois.append("circle").attr("cx", poi.cx).attr("cy", poi.cy).attr("r", poi.r).attr("fill", poi.fill).attr("stroke", poi.stroke).attr("stroke-width", 1).attr("data-line-number", poi.lineNumber).attr("data-poi", poi.id);
49195
+ if (poi.clusterId !== void 0)
49196
+ c.attr("data-cluster-member", poi.clusterId);
47797
49197
  if (poi.tags) {
47798
49198
  for (const [group, value] of Object.entries(poi.tags)) {
47799
49199
  c.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
@@ -47821,12 +49221,32 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47821
49221
  }
47822
49222
  const gLabels = svg.append("g").attr("class", "dgmo-map-labels");
47823
49223
  for (const lab of layout.labels) {
49224
+ if (lab.hidden) {
49225
+ if (exportDims) continue;
49226
+ emitText(
49227
+ gLabels,
49228
+ lab.x,
49229
+ lab.y,
49230
+ lab.text,
49231
+ lab.anchor,
49232
+ lab.color,
49233
+ lab.haloColor,
49234
+ lab.halo,
49235
+ LABEL_FONT,
49236
+ lab.italic,
49237
+ lab.letterSpacing
49238
+ ).attr("data-poi", lab.poiId ?? null).attr("data-poi-hidden", "").style("opacity", 0).style("pointer-events", "none");
49239
+ continue;
49240
+ }
47824
49241
  if (lab.leader) {
47825
49242
  const line12 = gLabels.append("line").attr("x1", lab.leader.x1).attr("y1", lab.leader.y1).attr("x2", lab.leader.x2).attr("y2", lab.leader.y2).attr(
47826
49243
  "stroke",
47827
49244
  lab.leaderColor ?? mix(palette.textMuted, palette.bg, 60)
47828
49245
  ).attr("stroke-width", lab.leaderColor ? 1 : 0.75);
47829
49246
  if (lab.poiId !== void 0) line12.attr("data-poi", lab.poiId);
49247
+ if (lab.clusterMember !== void 0)
49248
+ line12.attr("data-cluster-member", lab.clusterMember);
49249
+ wireSync(line12, lab.lineNumber);
47830
49250
  }
47831
49251
  const t = emitText(
47832
49252
  gLabels,
@@ -47837,11 +49257,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47837
49257
  lab.color,
47838
49258
  lab.haloColor,
47839
49259
  lab.halo,
47840
- LABEL_FONT
49260
+ LABEL_FONT,
49261
+ lab.italic,
49262
+ lab.letterSpacing,
49263
+ lab.lines
47841
49264
  );
47842
49265
  if (lab.poiId !== void 0) {
47843
49266
  t.attr("data-poi", lab.poiId).style("cursor", "default");
47844
49267
  }
49268
+ if (lab.clusterMember !== void 0) {
49269
+ t.attr("data-cluster-member", lab.clusterMember);
49270
+ }
49271
+ wireSync(t, lab.lineNumber);
49272
+ }
49273
+ if (!exportDims && layout.clusters.length) {
49274
+ const gBadge = svg.append("g").attr("class", "dgmo-map-cluster-badges");
49275
+ for (const cl of layout.clusters) {
49276
+ const g = gBadge.append("g").attr("data-cluster", cl.id).style("opacity", 0).style("pointer-events", "none");
49277
+ const R = 9;
49278
+ g.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", R).attr("fill", mix(palette.textMuted, palette.bg, 35)).attr("stroke", palette.textMuted).attr("stroke-width", 1);
49279
+ g.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", R + 2.5).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1);
49280
+ emitText(
49281
+ g,
49282
+ cl.cx,
49283
+ cl.cy + 3,
49284
+ String(cl.count),
49285
+ "middle",
49286
+ palette.text,
49287
+ palette.bg,
49288
+ false,
49289
+ LABEL_FONT
49290
+ );
49291
+ }
47845
49292
  }
47846
49293
  if (layout.legend) {
47847
49294
  const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
@@ -47878,7 +49325,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47878
49325
  svg.append("text").attr("class", "dgmo-map-title").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.title);
47879
49326
  }
47880
49327
  if (layout.subtitle) {
47881
- svg.append("text").attr("x", width / 2).attr("y", TITLE_Y + TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("font-size", LABEL_FONT + 1).attr("fill", palette.textMuted).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.subtitle);
49328
+ svg.append("text").attr("class", "dgmo-map-subtitle").attr("x", width / 2).attr("y", TITLE_Y + TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("font-size", LABEL_FONT + 1).attr("fill", palette.textMuted).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.subtitle);
47882
49329
  }
47883
49330
  if (layout.caption) {
47884
49331
  svg.append("text").attr("x", width / 2).attr("y", height - 8).attr("text-anchor", "middle").attr("font-size", LABEL_FONT).attr("fill", palette.textMuted).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.caption);
@@ -47887,10 +49334,21 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
47887
49334
  function renderMapForExport(container, resolved, data, palette, isDark, exportDims) {
47888
49335
  renderMap(container, resolved, data, palette, isDark, void 0, exportDims);
47889
49336
  }
47890
- function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize) {
47891
- const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color).text(text);
49337
+ function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic, letterSpacing, lines) {
49338
+ const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color);
49339
+ if (lines && lines.length > 1) {
49340
+ const lineHeight = fontSize + 2;
49341
+ const startDy = -((lines.length - 1) / 2) * lineHeight;
49342
+ lines.forEach((ln, i) => {
49343
+ t.append("tspan").attr("x", x).attr("dy", i === 0 ? startDy : lineHeight).text(ln);
49344
+ });
49345
+ } else {
49346
+ t.text(text);
49347
+ }
49348
+ if (italic) t.attr("font-style", "italic");
49349
+ if (letterSpacing) t.attr("letter-spacing", letterSpacing);
47892
49350
  if (withHalo) {
47893
- t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7);
49351
+ t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width", 2).attr("stroke-linejoin", "round").attr("stroke-linecap", "round").attr("stroke-opacity", 0.55);
47894
49352
  }
47895
49353
  return t;
47896
49354
  }
@@ -47907,6 +49365,56 @@ var init_renderer16 = __esm({
47907
49365
  }
47908
49366
  });
47909
49367
 
49368
+ // src/map/dimensions.ts
49369
+ var dimensions_exports = {};
49370
+ __export(dimensions_exports, {
49371
+ mapContentAspect: () => mapContentAspect,
49372
+ mapExportDimensions: () => mapExportDimensions
49373
+ });
49374
+ import { geoPath as geoPath2 } from "d3-geo";
49375
+ function mapContentAspect(resolved, data, ref = REF) {
49376
+ const { projection, fitTarget } = buildMapProjection(resolved, data);
49377
+ projection.fitSize([ref, ref], fitTarget);
49378
+ const b = geoPath2(projection).bounds(fitTarget);
49379
+ const w = b[1][0] - b[0][0];
49380
+ const h = b[1][1] - b[0][1];
49381
+ const aspect = w / h;
49382
+ return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
49383
+ }
49384
+ function mapExportDimensions(resolved, data, baseWidth = 1200) {
49385
+ const raw = mapContentAspect(resolved, data);
49386
+ const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49387
+ const width = baseWidth;
49388
+ let height = Math.round(width / clamped);
49389
+ let chromeReserve = 0;
49390
+ if (resolved.title && resolved.pois.length > 0) {
49391
+ const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
49392
+ chromeReserve += Math.max(FIT_PAD2, bannerBottom + TITLE_GAP) - FIT_PAD2;
49393
+ }
49394
+ let floored = false;
49395
+ if (height - chromeReserve < MIN_MAP_BAND) {
49396
+ height = Math.round(chromeReserve + MIN_MAP_BAND);
49397
+ floored = true;
49398
+ }
49399
+ const preferContain = clamped !== raw || floored;
49400
+ return { width, height, preferContain };
49401
+ }
49402
+ var FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
49403
+ var init_dimensions = __esm({
49404
+ "src/map/dimensions.ts"() {
49405
+ "use strict";
49406
+ init_title_constants();
49407
+ init_layout15();
49408
+ FIT_PAD2 = 24;
49409
+ TITLE_GAP = 16;
49410
+ ASPECT_MAX = 3;
49411
+ ASPECT_MIN = 0.9;
49412
+ MIN_MAP_BAND = 200;
49413
+ FALLBACK_ASPECT = 1.5;
49414
+ REF = 1e3;
49415
+ }
49416
+ });
49417
+
47910
49418
  // src/map/load-data.ts
47911
49419
  var load_data_exports = {};
47912
49420
  __export(load_data_exports, {
@@ -47965,12 +49473,17 @@ function loadMapData() {
47965
49473
  mountainRanges,
47966
49474
  naLand,
47967
49475
  naLakes,
49476
+ waterBodies,
47968
49477
  gazetteer
47969
49478
  ] = await Promise.all([
49479
+ // worldCoarse (110m) is LOAD-BEARING but NOT a render source: the world
49480
+ // basemap renders from worldDetail (50m) at all scales (resolver pins
49481
+ // basemaps.world = 'detail'). Coarse stays as the authoritative region
49482
+ // name index + dominant-landmass bbox source in resolver.ts. Do not drop it.
47970
49483
  readJson(nb, dir, FILES.worldCoarse),
47971
49484
  readJson(nb, dir, FILES.worldDetail),
47972
49485
  readJson(nb, dir, FILES.usStates),
47973
- // Lakes/rivers/mountain/NA assets are optional — older bundles may predate them.
49486
+ // Lakes/rivers/mountain/NA/water assets are optional — older bundles may predate them.
47974
49487
  readJson(nb, dir, FILES.lakes).catch(() => void 0),
47975
49488
  readJson(nb, dir, FILES.rivers).catch(() => void 0),
47976
49489
  readJson(nb, dir, FILES.mountainRanges).catch(
@@ -47978,6 +49491,7 @@ function loadMapData() {
47978
49491
  ),
47979
49492
  readJson(nb, dir, FILES.naLand).catch(() => void 0),
47980
49493
  readJson(nb, dir, FILES.naLakes).catch(() => void 0),
49494
+ readJson(nb, dir, FILES.waterBodies).catch(() => void 0),
47981
49495
  readJson(nb, dir, FILES.gazetteer)
47982
49496
  ]);
47983
49497
  return validate({
@@ -47989,7 +49503,8 @@ function loadMapData() {
47989
49503
  ...rivers && { rivers },
47990
49504
  ...mountainRanges && { mountainRanges },
47991
49505
  ...naLand && { naLand },
47992
- ...naLakes && { naLakes }
49506
+ ...naLakes && { naLakes },
49507
+ ...waterBodies && { waterBodies }
47993
49508
  });
47994
49509
  })().catch((e) => {
47995
49510
  cache = void 0;
@@ -48010,6 +49525,7 @@ var init_load_data = __esm({
48010
49525
  mountainRanges: "mountain-ranges.json",
48011
49526
  naLand: "na-land.json",
48012
49527
  naLakes: "na-lakes.json",
49528
+ waterBodies: "water-bodies.json",
48013
49529
  gazetteer: "gazetteer.json"
48014
49530
  };
48015
49531
  CANDIDATE_DIRS = [
@@ -50023,8 +51539,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
50023
51539
  const lines = splitParticipantLabel(p.label, LABEL_MAX_CHARS);
50024
51540
  if (lines.length === 0) continue;
50025
51541
  const widest = Math.max(...lines.map((l) => l.length));
50026
- const labelWidth = widest * LABEL_CHAR_WIDTH + 10;
50027
- uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth);
51542
+ const labelWidth2 = widest * LABEL_CHAR_WIDTH + 10;
51543
+ uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
50028
51544
  }
50029
51545
  uniformBoxWidth = Math.min(MAX_BOX_WIDTH, uniformBoxWidth);
50030
51546
  const effectiveGap = Math.max(PARTICIPANT_GAP, uniformBoxWidth + 30);
@@ -52723,15 +54239,15 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
52723
54239
  textColor,
52724
54240
  onClickItem
52725
54241
  );
52726
- const neighbors = /* @__PURE__ */ new Map();
52727
- for (const node of nodes) neighbors.set(node, /* @__PURE__ */ new Set());
54242
+ const neighbors2 = /* @__PURE__ */ new Map();
54243
+ for (const node of nodes) neighbors2.set(node, /* @__PURE__ */ new Set());
52728
54244
  for (const link of links) {
52729
- neighbors.get(link.source).add(link.target);
52730
- neighbors.get(link.target).add(link.source);
54245
+ neighbors2.get(link.source).add(link.target);
54246
+ neighbors2.get(link.target).add(link.source);
52731
54247
  }
52732
54248
  const FADE_OPACITY3 = 0.1;
52733
54249
  function handleMouseEnter(hovered) {
52734
- const connected = neighbors.get(hovered);
54250
+ const connected = neighbors2.get(hovered);
52735
54251
  g.selectAll(".arc-link").each(function() {
52736
54252
  const el = d3Selection23.select(this);
52737
54253
  const src = el.attr("data-source");
@@ -54722,7 +56238,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
54722
56238
  8,
54723
56239
  Math.floor(OVERLAP_WRAP_TARGET_W / OVERLAP_CH_W)
54724
56240
  );
54725
- function wrapLabel2(text, maxChars) {
56241
+ function wrapLabel3(text, maxChars) {
54726
56242
  const words = text.split(/\s+/).filter(Boolean);
54727
56243
  const lines = [];
54728
56244
  let cur = "";
@@ -54768,7 +56284,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
54768
56284
  if (!ov.label) continue;
54769
56285
  const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
54770
56286
  if (idxs.some((idx) => idx < 0)) continue;
54771
- const lines = wrapLabel2(ov.label, MAX_WRAP_CHARS);
56287
+ const lines = wrapLabel3(ov.label, MAX_WRAP_CHARS);
54772
56288
  wrappedOverlapLabels.set(ov, lines);
54773
56289
  const dir = predictOverlapDirRaw(idxs);
54774
56290
  const longest = lines.reduce((m, l) => Math.max(m, l.length), 0);
@@ -56206,6 +57722,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
56206
57722
  const { parseMap: parseMap2 } = await Promise.resolve().then(() => (init_parser12(), parser_exports11));
56207
57723
  const { resolveMap: resolveMap2 } = await Promise.resolve().then(() => (init_resolver2(), resolver_exports));
56208
57724
  const { renderMapForExport: renderMapForExport2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
57725
+ const { mapExportDimensions: mapExportDimensions2 } = await Promise.resolve().then(() => (init_dimensions(), dimensions_exports));
56209
57726
  const effectivePalette2 = await resolveExportPalette(theme, palette);
56210
57727
  const mapParsed = parseMap2(content);
56211
57728
  let mapData = options?.mapData;
@@ -56218,14 +57735,15 @@ async function renderForExport(content, theme, palette, viewState, options) {
56218
57735
  }
56219
57736
  }
56220
57737
  const mapResolved = resolveMap2(mapParsed, mapData);
56221
- const container2 = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
57738
+ const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
57739
+ const container2 = createExportContainer(dims2.width, dims2.height);
56222
57740
  renderMapForExport2(
56223
57741
  container2,
56224
57742
  mapResolved,
56225
57743
  mapData,
56226
57744
  effectivePalette2,
56227
57745
  theme === "dark",
56228
- { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
57746
+ dims2
56229
57747
  );
56230
57748
  return finalizeSvgExport(container2, theme, effectivePalette2);
56231
57749
  }
@@ -57072,7 +58590,8 @@ async function render(content, options) {
57072
58590
  ...options?.c4Container !== void 0 && {
57073
58591
  c4Container: options.c4Container
57074
58592
  },
57075
- ...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup }
58593
+ ...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup },
58594
+ ...options?.mapData !== void 0 && { mapData: options.mapData }
57076
58595
  });
57077
58596
  if (chartType === "map") {
57078
58597
  try {
@@ -57083,7 +58602,7 @@ async function render(content, options) {
57083
58602
  Promise.resolve().then(() => (init_load_data(), load_data_exports))
57084
58603
  ]
57085
58604
  );
57086
- const data = await loadMapData2();
58605
+ const data = options?.mapData ?? await loadMapData2();
57087
58606
  diagnostics = [...resolveMap2(parseMap2(content), data).diagnostics];
57088
58607
  } catch {
57089
58608
  }
@@ -57322,8 +58841,8 @@ function detectCycles(parsed) {
57322
58841
  const parent = /* @__PURE__ */ new Map();
57323
58842
  function dfs(nodeId3) {
57324
58843
  color.set(nodeId3, 1);
57325
- const neighbors = adj.get(nodeId3) ?? [];
57326
- for (const next of neighbors) {
58844
+ const neighbors2 = adj.get(nodeId3) ?? [];
58845
+ for (const next of neighbors2) {
57327
58846
  const c = color.get(next) ?? 0;
57328
58847
  if (c === 1) {
57329
58848
  const lineKey = `${nodeId3}->${next}`;
@@ -57508,6 +59027,7 @@ init_resolver2();
57508
59027
  init_load_data();
57509
59028
  init_layout15();
57510
59029
  init_renderer16();
59030
+ init_dimensions();
57511
59031
 
57512
59032
  // src/map/geo-query.ts
57513
59033
  init_parser12();
@@ -57581,7 +59101,9 @@ function nearestCity(lonLat, gazetteer) {
57581
59101
  name: c[4],
57582
59102
  iso: c[2],
57583
59103
  ...c[5] !== void 0 && { sub: c[5] },
57584
- distanceKm: best.dist
59104
+ distanceKm: best.dist,
59105
+ lat: c[0],
59106
+ lon: c[1]
57585
59107
  };
57586
59108
  }
57587
59109
  function roundCoord(n) {
@@ -57660,7 +59182,7 @@ function createMapGeoQuery(opts) {
57660
59182
  }
57661
59183
  return out;
57662
59184
  };
57663
- return { invert, project, locate, cities };
59185
+ return { invert, project, locate, cities, diagnostics: layout.diagnostics };
57664
59186
  }
57665
59187
 
57666
59188
  // src/map/completion.ts
@@ -58386,9 +59908,12 @@ var GLOBAL_DIRECTIVES = {
58386
59908
  "gruvbox",
58387
59909
  "tokyo-night",
58388
59910
  "one-dark",
58389
- "bold",
58390
59911
  "dracula",
58391
- "monokai"
59912
+ "monokai",
59913
+ "atlas",
59914
+ "blueprint",
59915
+ "slate",
59916
+ "tidewater"
58392
59917
  ]
58393
59918
  },
58394
59919
  theme: {
@@ -58784,18 +60309,12 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
58784
60309
  ],
58785
60310
  [
58786
60311
  "map",
58787
- // Geographic map directives (§24B.2/.7). `poi`/`route` are content
58788
- // keywords, not directives; metadata keys (value/label/style) live in the
58789
- // reserved-key registry.
60312
+ // Geographic map directives (§24B.2/.7). Cosmetics are ON by default — the
60313
+ // only switches are bare `no-*` opt-outs, surfaced proactively so a
60314
+ // zero-config map still hints at what can be turned off. `poi`/`route` are
60315
+ // content keywords, not directives; metadata keys (value/label/style) live
60316
+ // in the reserved-key registry.
58790
60317
  withGlobals({
58791
- region: {
58792
- description: "Basemap: us-states (force US state mesh + scoping) | world (inert \u2014 already the default)",
58793
- values: ["us-states", "world"]
58794
- },
58795
- projection: {
58796
- description: "Override the auto projection",
58797
- values: ["equirectangular", "natural-earth", "albers-usa", "mercator"]
58798
- },
58799
60318
  "region-metric": { description: "Label for the region value ramp" },
58800
60319
  "poi-metric": {
58801
60320
  description: "Label for the POI value (marker size) channel"
@@ -58803,21 +60322,30 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
58803
60322
  "flow-metric": {
58804
60323
  description: "Label for the edge/leg value (thickness) channel"
58805
60324
  },
58806
- scale: { description: "Override value ramp anchors: scale <min> <max>" },
58807
- "region-labels": {
58808
- description: "Subdivision name labels",
58809
- values: ["full", "abbrev", "off"]
60325
+ locale: {
60326
+ description: "Default country/state for bare place names, e.g. locale US-GA"
58810
60327
  },
58811
- "poi-labels": {
58812
- description: "POI labels/values",
58813
- values: ["off", "auto", "all"]
60328
+ "active-tag": {
60329
+ description: "Which tag group leads when several are present"
58814
60330
  },
58815
- "default-country": { description: "ISO scope for bare city resolution" },
58816
- "default-state": { description: "ISO subdivision scope" },
60331
+ caption: { description: "Caption line (data-source attribution)" },
58817
60332
  "no-legend": { description: "Suppress the legend" },
58818
- relief: { description: "Subtle mountain-range relief shading" },
58819
- subtitle: { description: "Subtitle line" },
58820
- caption: { description: "Caption line" }
60333
+ "no-coastline": {
60334
+ description: "Turn off coastal water-lines (on by default)"
60335
+ },
60336
+ "no-relief": {
60337
+ description: "Turn off mountain-range relief shading (on by default)"
60338
+ },
60339
+ "no-context-labels": {
60340
+ description: "Turn off orientation labels for water + nearby countries"
60341
+ },
60342
+ "no-region-labels": {
60343
+ description: "Turn off subdivision name labels (on by default)"
60344
+ },
60345
+ "no-poi-labels": { description: "Turn off POI labels (on by default)" },
60346
+ "no-colorize": {
60347
+ description: "Force plain green-land reference dress (regions are auto-coloured by default)"
60348
+ }
58821
60349
  })
58822
60350
  ]
58823
60351
  ]);
@@ -60292,7 +61820,8 @@ export {
60292
61820
  applyCollapseProjection,
60293
61821
  applyGroupOrdering,
60294
61822
  applyPositionOverrides,
60295
- boldPalette,
61823
+ atlasPalette,
61824
+ blueprintPalette,
60296
61825
  buildExtendedChartOption,
60297
61826
  buildNoteMessageMap,
60298
61827
  buildRenderSequence,
@@ -60395,6 +61924,8 @@ export {
60395
61924
  looksLikeState,
60396
61925
  makeDgmoError,
60397
61926
  mapBackgroundColor,
61927
+ mapContentAspect,
61928
+ mapExportDimensions,
60398
61929
  mapNeutralLandColor,
60399
61930
  matchesContiguously,
60400
61931
  measurePertAnalysisBlock,
@@ -60530,9 +62061,11 @@ export {
60530
62061
  shapeFill,
60531
62062
  simulateCanonical,
60532
62063
  simulateFast,
62064
+ slatePalette,
60533
62065
  solarizedPalette,
60534
62066
  suggestChartTypes,
60535
62067
  themes,
62068
+ tidewaterPalette,
60536
62069
  tint,
60537
62070
  tokyoNightPalette,
60538
62071
  transformLine,