var WuysMap = (() => {
  var __defProp = Object.defineProperty;
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  var __getOwnPropNames = Object.getOwnPropertyNames;
  var __hasOwnProp = Object.prototype.hasOwnProperty;
  var __export = (target, all) => {
    for (var name in all)
      __defProp(target, name, { get: all[name], enumerable: true });
  };
  var __copyProps = (to, from, except, desc) => {
    if (from && typeof from === "object" || typeof from === "function") {
      for (let key of __getOwnPropNames(from))
        if (!__hasOwnProp.call(to, key) && key !== except)
          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
    }
    return to;
  };
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

  // map/src/bundle-entry.js
  var bundle_entry_exports = {};
  __export(bundle_entry_exports, {
    attachOverlays: () => attachOverlays,
    createMap: () => createMap,
    layers: () => layers,
    mountMap: () => mountMap,
    projections: () => projections,
    themes: () => themes
  });

  // map/src/core/state.js
  var NAV_ZOOM_MIN = 1;
  var NAV_ZOOM_MAX = 100;
  var NAV_LAT_MAX = 89.9;
  function createState(opts = {}) {
    return {
      /** Static data; loaded async via data/loader.js */
      data: {
        topology: null,
        countries: /* @__PURE__ */ new Map(),
        // iso → { bbox, centroid, area, geom }
        flags: /* @__PURE__ */ new Map(),
        // iso → data URI (or null when not yet loaded)
        labels: /* @__PURE__ */ new Map(),
        // iso → canonical label entry
        spotlights: [],
        contestedRegions: [],
        isoCodes: { numToA2: /* @__PURE__ */ new Map(), a2ToNum: /* @__PURE__ */ new Map() },
        ready: false
      },
      /** Navigation state — current (animated) and target. */
      nav: {
        lon: opts.lon ?? 0,
        lat: opts.lat ?? 0,
        zoom: opts.zoom ?? 1,
        tLon: opts.lon ?? 0,
        tLat: opts.lat ?? 0,
        tZoom: opts.zoom ?? 1
      },
      /** View mode + active projection. */
      view: {
        mode: opts.view ?? "flat",
        // "flat" | "globe"
        projection: opts.projection ?? "equalEarth"
      },
      /** Theme + display flags. Three INDEPENDENT toggles — none clobbers the
       *  others. Theme controls visual styling; flags/labels control feature
       *  visibility. Each persists independently in the host's storage. */
      theme: {
        id: opts.theme ?? "vector",
        flagsVisible: opts.flagsVisible ?? true,
        labelsVisible: opts.labelsVisible ?? false,
        customColors: null
      },
      /** Layers — set of enabled IDs + their config + render order. */
      layers: {
        enabled: new Set(opts.layers ?? ["countries", "labels", "spotlights", "contested"]),
        order: /* @__PURE__ */ new Map(),
        // id → number (smaller = drawn first / lower z)
        config: /* @__PURE__ */ new Map()
        // id → arbitrary layer-specific config
      },
      /** Label editor state machine. */
      editor: {
        mode: "off",
        // "off" | "labels"
        selectedIso: null,
        drag: null
      },
      /** Transient interaction state. */
      interactions: {
        isPointerDown: false,
        isAnimating: false,
        hoveredIso: null
      },
      /** Performance counters (for dev HUD). */
      perf: {
        fps: 0,
        frameTime: 0,
        drawCalls: 0,
        lastFrameAt: 0
      }
    };
  }
  function clampTarget(state) {
    const n = state.nav;
    n.tZoom = Math.max(NAV_ZOOM_MIN, Math.min(NAV_ZOOM_MAX, n.tZoom));
    n.tLat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, n.tLat));
    while (n.tLon > 180) n.tLon -= 360;
    while (n.tLon < -180) n.tLon += 360;
  }
  function normalizeLon(state) {
    const n = state.nav;
    while (n.lon > 180) n.lon -= 360;
    while (n.lon < -180) n.lon += 360;
  }

  // map/src/core/events.js
  function createEventBus() {
    const handlers = /* @__PURE__ */ new Map();
    function on(event, handler) {
      if (!handlers.has(event)) handlers.set(event, /* @__PURE__ */ new Set());
      handlers.get(event).add(handler);
      return () => off(event, handler);
    }
    function off(event, handler) {
      const set = handlers.get(event);
      if (set) set.delete(handler);
    }
    function once(event, handler) {
      const wrapped = (payload) => {
        off(event, wrapped);
        handler(payload);
      };
      return on(event, wrapped);
    }
    function emit(event, payload) {
      const set = handlers.get(event);
      if (!set || set.size === 0) return;
      for (const handler of [...set]) {
        try {
          handler(payload);
        } catch (err) {
          console.error("[wuys/map] event handler threw for", event, err);
        }
      }
    }
    function clear() {
      handlers.clear();
    }
    return { on, off, once, emit, clear };
  }

  // map/src/core/animation.js
  var easings = {
    linear: (t) => t,
    easeOutQuad: (t) => 1 - (1 - t) ** 2,
    easeOutCubic: (t) => 1 - (1 - t) ** 3,
    easeOutQuart: (t) => 1 - (1 - t) ** 4,
    easeInOutQuad: (t) => t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2,
    easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2
  };
  function createAnimator(opts) {
    const tol = opts.tolerance ?? {};
    const k = opts.k ?? 0.22;
    let rafId = 0;
    function step() {
      rafId = 0;
      const cur = opts.getCurrent();
      const tgt = opts.getTarget();
      let stillMoving = false;
      for (const key of Object.keys(tgt)) {
        let d = tgt[key] - cur[key];
        if (opts.adjustDelta) d = opts.adjustDelta(key, d);
        const t = tol[key] ?? 1e-3;
        if (Math.abs(d) > t) {
          opts.setCurrent(key, cur[key] + d * k);
          stillMoving = true;
        } else {
          opts.setCurrent(key, tgt[key]);
        }
      }
      if (opts.onStep) opts.onStep();
      if (stillMoving) {
        rafId = requestAnimationFrame(step);
      } else if (opts.onSettle) {
        opts.onSettle();
      }
    }
    return {
      kick() {
        if (!rafId) rafId = requestAnimationFrame(step);
      },
      snap() {
        if (rafId) {
          cancelAnimationFrame(rafId);
          rafId = 0;
        }
        const tgt = opts.getTarget();
        for (const key of Object.keys(tgt)) opts.setCurrent(key, tgt[key]);
        if (opts.onStep) opts.onStep();
      },
      stop() {
        if (rafId) {
          cancelAnimationFrame(rafId);
          rafId = 0;
        }
      },
      isRunning() {
        return rafId !== 0;
      }
    };
  }
  function shortestLonDelta(key, delta) {
    if (key !== "lon" && key !== "tLon") return delta;
    if (delta > 180) return delta - 360;
    if (delta < -180) return delta + 360;
    return delta;
  }
  function runTween(opts) {
    const ease = typeof opts.easing === "function" ? opts.easing : easings[opts.easing] || easings.easeOutCubic;
    const start = performance.now();
    const keys = Object.keys(opts.to);
    let rafId = 0;
    let running = true;
    function step(now) {
      rafId = 0;
      if (!running) return;
      const elapsed = now - start;
      const t = Math.min(1, elapsed / opts.duration);
      const e = ease(t);
      const out = {};
      for (const k of keys) {
        const a = opts.from[k] ?? 0;
        let b = opts.to[k];
        if (k === "lon" || k === "tLon") {
          let d = b - a;
          if (d > 180) b -= 360;
          else if (d < -180) b += 360;
        }
        out[k] = a + (b - a) * e;
      }
      opts.onStep(out);
      if (t < 1) {
        rafId = requestAnimationFrame(step);
      } else {
        running = false;
        if (opts.onDone) opts.onDone();
      }
    }
    rafId = requestAnimationFrame(step);
    return {
      stop() {
        running = false;
        if (rafId) cancelAnimationFrame(rafId);
      },
      isRunning() {
        return running;
      }
    };
  }

  // map/src/core/nav.js
  function createNav({ state, events, animator, ctx, onTweenStep }) {
    let activeTween = null;
    function setCenter({ lon, lat, zoom }) {
      if (lon != null) state.nav.tLon = lon;
      if (lat != null) state.nav.tLat = lat;
      if (zoom != null) state.nav.tZoom = zoom;
      clampTarget(state);
      animator.kick();
    }
    function setZoom(zoom) {
      setCenter({ zoom });
    }
    function zoomBy(factor) {
      const next = Math.max(NAV_ZOOM_MIN, Math.min(NAV_ZOOM_MAX, state.nav.zoom * factor));
      state.nav.zoom = next;
      state.nav.tZoom = next;
      animator.snap();
      if (onTweenStep) onTweenStep();
      events.emit("nav:change", state.nav);
    }
    function panBy({ dx, dy }) {
      if (!ctx || !ctx.viewportToWorld) return;
      const delta = ctx.viewportToWorld(dx, dy);
      state.nav.tLon += delta.dLon;
      state.nav.tLat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, state.nav.tLat + delta.dLat));
      clampTarget(state);
      animator.kick();
    }
    function flyTo({ lon, lat, zoom, duration = 900, easing = "easeOutCubic" }) {
      if (activeTween) activeTween.stop();
      const from = { lon: state.nav.lon, lat: state.nav.lat, zoom: state.nav.zoom };
      const to = {
        lon: lon != null ? lon : state.nav.lon,
        lat: lat != null ? lat : state.nav.lat,
        zoom: zoom != null ? zoom : state.nav.zoom
      };
      to.zoom = Math.max(NAV_ZOOM_MIN, Math.min(NAV_ZOOM_MAX, to.zoom));
      to.lat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, to.lat));
      events.emit("nav:flyto-start", { from, to, duration, easing });
      activeTween = runTween({
        from,
        to,
        duration,
        easing,
        onStep: (v) => {
          state.nav.lon = v.lon;
          state.nav.lat = v.lat;
          state.nav.zoom = v.zoom;
          state.nav.tLon = v.lon;
          state.nav.tLat = v.lat;
          state.nav.tZoom = v.zoom;
          if (onTweenStep) onTweenStep();
          events.emit("nav:change", state.nav);
        },
        onDone: () => {
          activeTween = null;
          events.emit("nav:flyto-end", to);
          events.emit("nav:end", state.nav);
        }
      });
    }
    function fitCountry(iso, { padding = 50 } = {}) {
      const cm = state.data.countries.get(iso);
      if (!cm) return;
      const span = Math.max(cm.lonRange ?? 30, cm.latRange ?? 30);
      const zoom = Math.max(1, Math.min(NAV_ZOOM_MAX, 60 / span));
      flyTo({ lon: cm.centroid?.lon ?? 0, lat: cm.centroid?.lat ?? 0, zoom });
    }
    function reset({ animated = true } = {}) {
      state.nav.tLon = 0;
      state.nav.tLat = 0;
      state.nav.tZoom = 1;
      if (animated) {
        animator.kick();
      } else {
        animator.snap();
      }
    }
    function getCenter() {
      return { lon: state.nav.lon, lat: state.nav.lat, zoom: state.nav.zoom };
    }
    function getTarget() {
      return { lon: state.nav.tLon, lat: state.nav.tLat, zoom: state.nav.tZoom };
    }
    return { setCenter, setZoom, zoomBy, panBy, flyTo, fitCountry, reset, getCenter, getTarget };
  }

  // map/src/core/hash.js
  function attachHashSync({ state, events, prefix = "map" }) {
    let writeTimer = 0;
    const HASH_RE = new RegExp("^#" + prefix + "/");
    function write() {
      if (writeTimer) clearTimeout(writeTimer);
      writeTimer = setTimeout(() => {
        writeTimer = 0;
        try {
          const parts = [
            "view=" + state.view.mode,
            "lon=" + state.nav.lon.toFixed(2),
            "lat=" + state.nav.lat.toFixed(2),
            "z=" + state.nav.zoom.toFixed(2),
            "theme=" + state.theme.id
          ];
          const next = "#" + prefix + "/" + parts.join(",");
          if (location.hash !== next) history.replaceState(null, "", next);
        } catch (_) {
        }
      }, 250);
    }
    function read() {
      try {
        const h = location.hash || "";
        if (!HASH_RE.test(h)) return null;
        const parts = h.slice(2 + prefix.length).split(",");
        const opts = {};
        for (const p of parts) {
          const [k, v] = p.split("=");
          opts[k] = v;
        }
        return opts;
      } catch (_) {
        return null;
      }
    }
    events.on("nav:change", write);
    events.on("view:change", write);
    events.on("theme:change", write);
    return { read, write };
  }

  // map/src/math/projections/equal-earth.js
  var equal_earth_exports = {};
  __export(equal_earth_exports, {
    meta: () => meta,
    project: () => project,
    unproject: () => unproject
  });
  var A1 = 1.340264;
  var A2 = -0.081106;
  var A3 = 893e-6;
  var A4 = 3796e-6;
  var M = Math.sqrt(3) / 2;
  var X_HALF = 2.7063831;
  var Y_HALF = 1.3173627;
  var VB_W = 2e3;
  var VB_H = 1e3;
  var SCALE = Math.min(VB_W / 2 / X_HALF, VB_H / 2 / Y_HALF);
  var CX = VB_W / 2;
  var CY = VB_H / 2;
  function project(lon, lat) {
    const lonR = lon * Math.PI / 180;
    const latR = lat * Math.PI / 180;
    const th = Math.asin(M * Math.sin(latR));
    const t2 = th * th;
    const t6 = t2 * t2 * t2;
    const Mp = A1 + 3 * A2 * t2 + t6 * (7 * A3 + 9 * A4 * t2);
    const Mt = A1 + A2 * t2 + t6 * (A3 + A4 * t2);
    const xn = 2 * Math.sqrt(3) * lonR * Math.cos(th) / (3 * Mp);
    const yn = Mt * th;
    return [CX + xn * SCALE, CY - yn * SCALE];
  }
  function unproject(vbx, vby) {
    const xn = (vbx - CX) / SCALE;
    const yn = (CY - vby) / SCALE;
    let theta = yn;
    for (let i = 0; i < 20; i++) {
      const t22 = theta * theta;
      const t62 = t22 * t22 * t22;
      const Mt = A1 + A2 * t22 + t62 * (A3 + A4 * t22);
      const Mp2 = A1 + 3 * A2 * t22 + t62 * (7 * A3 + 9 * A4 * t22);
      const f = Mt * theta - yn;
      const d = f / Mp2;
      theta -= d;
      if (Math.abs(d) < 1e-9) break;
    }
    const latR = Math.asin(Math.max(-1, Math.min(1, Math.sin(theta) / M)));
    const t2 = theta * theta;
    const t6 = t2 * t2 * t2;
    const Mp = A1 + 3 * A2 * t2 + t6 * (7 * A3 + 9 * A4 * t2);
    const cosTh = Math.cos(theta);
    const lonR = cosTh < 1e-9 ? 0 : 3 * Mp * xn / (2 * Math.sqrt(3) * cosTh);
    return { lon: lonR * 180 / Math.PI, lat: latR * 180 / Math.PI };
  }
  var meta = {
    id: "equalEarth",
    name: "Equal Earth",
    type: "pseudocylindrical",
    equalArea: true,
    viewBox: { w: VB_W, h: VB_H, cx: CX, cy: CY },
    scale: SCALE,
    naturalRange: { xHalf: X_HALF, yHalf: Y_HALF }
  };

  // map/src/math/projections/orthographic.js
  var orthographic_exports = {};
  __export(orthographic_exports, {
    meta: () => meta2,
    project: () => project2,
    projector: () => projector,
    unproject: () => unproject2
  });
  var CX2 = 1e3;
  var CY2 = 500;
  var BASE_R = 450;
  function projector(rotLambdaRad, rotPhiRad) {
    const sinPhi0 = Math.sin(rotPhiRad);
    const cosPhi0 = Math.cos(rotPhiRad);
    return function(lon, lat) {
      const lambda = lon * Math.PI / 180;
      const phi = lat * Math.PI / 180;
      const dLam = lambda - rotLambdaRad;
      const cosLam = Math.cos(dLam);
      const sinLam = Math.sin(dLam);
      const cosPhi = Math.cos(phi);
      const sinPhi = Math.sin(phi);
      return {
        x: cosPhi * sinLam,
        y: cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosLam,
        z: sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosLam
      };
    };
  }
  function project2(lon, lat, opts) {
    const rotLambdaRad = (opts.rotLon ?? 0) * Math.PI / 180;
    const rotPhiRad = (opts.rotLat ?? 0) * Math.PI / 180;
    const zoom = opts.zoom ?? 1;
    const baseR = BASE_R * zoom;
    const p = projector(rotLambdaRad, rotPhiRad)(lon, lat);
    return {
      vbx: CX2 + p.x * baseR,
      vby: CY2 - p.y * baseR,
      z: p.z,
      visible: p.z >= 0
    };
  }
  function unproject2(vbx, vby, opts) {
    const rotLambdaRad = (opts.rotLon ?? 0) * Math.PI / 180;
    const rotPhiRad = (opts.rotLat ?? 0) * Math.PI / 180;
    const zoom = opts.zoom ?? 1;
    const baseR = BASE_R * zoom;
    const xn = (vbx - CX2) / baseR;
    const yn = (CY2 - vby) / baseR;
    const rho2 = xn * xn + yn * yn;
    if (rho2 < 1e-12) {
      return { lon: opts.rotLon ?? 0, lat: opts.rotLat ?? 0 };
    }
    let rho = Math.sqrt(rho2);
    if (rho > 1) rho = 1;
    const c = Math.asin(rho);
    const sinC = Math.sin(c);
    const cosC = Math.cos(c);
    const sinPhi0 = Math.sin(rotPhiRad);
    const cosPhi0 = Math.cos(rotPhiRad);
    const lat = Math.asin(cosC * sinPhi0 + yn * sinC * cosPhi0 / rho);
    const lon = rotLambdaRad + Math.atan2(xn * sinC, rho * cosPhi0 * cosC - yn * sinPhi0 * sinC);
    return { lon: lon * 180 / Math.PI, lat: lat * 180 / Math.PI };
  }
  var meta2 = {
    id: "orthographic",
    name: "Orthographic",
    type: "azimuthal",
    equalArea: false,
    viewBox: { w: 2e3, h: 1e3, cx: CX2, cy: CY2 },
    baseR: BASE_R
  };

  // map/src/data/topology.js
  function decodeTopojson(topo) {
    const { transform, arcs: rawArcs, objects } = topo;
    const [sx, sy] = transform.scale;
    const [tx, ty] = transform.translate;
    const arcs = rawArcs.map((arc) => {
      const out = [];
      let x = 0, y = 0;
      for (const pair of arc) {
        x += pair[0];
        y += pair[1];
        out.push([x * sx + tx, y * sy + ty]);
      }
      return out;
    });
    const objName = Object.keys(objects)[0];
    const geometries = objects[objName].geometries;
    return { arcs, geometries };
  }
  function ringFromArcs(arcRefs, arcs) {
    const pts = [];
    for (const idx of arcRefs) {
      const reverse = idx < 0;
      const realIdx = reverse ? ~idx : idx;
      let arc = arcs[realIdx];
      if (reverse) arc = arc.slice().reverse();
      const start = pts.length ? 1 : 0;
      for (let i = start; i < arc.length; i++) pts.push(arc[i]);
    }
    return pts;
  }

  // map/src/math/geodesy.js
  var DEG = Math.PI / 180;
  function shoelaceCentroid(points) {
    let twoArea = 0, cx = 0, cy = 0;
    const n = points.length;
    for (let i = 0; i < n; i++) {
      const [x0, y0] = points[i];
      const [x1, y1] = points[(i + 1) % n];
      const cross2 = x0 * y1 - x1 * y0;
      twoArea += cross2;
      cx += (x0 + x1) * cross2;
      cy += (y0 + y1) * cross2;
    }
    if (Math.abs(twoArea) < 1e-9) return null;
    return {
      x: cx / (3 * twoArea),
      y: cy / (3 * twoArea),
      area: Math.abs(twoArea) / 2
    };
  }

  // map/src/data/polylabel.js
  function polylabel(polygon, precision = 0.1) {
    if (!polygon || polygon.length < 3) return { x: 0, y: 0, distance: 0 };
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    for (const p of polygon) {
      if (p[0] < minX) minX = p[0];
      if (p[0] > maxX) maxX = p[0];
      if (p[1] < minY) minY = p[1];
      if (p[1] > maxY) maxY = p[1];
    }
    const w = maxX - minX, h = maxY - minY;
    const cellSize = Math.min(w, h);
    if (cellSize === 0) return { x: minX, y: minY, distance: 0 };
    let half = cellSize / 2;
    const queue = [];
    function pushCell(c) {
      queue.push(c);
      let i = queue.length - 1;
      while (i > 0 && queue[i].max > queue[Math.floor((i - 1) / 2)].max) {
        [queue[i], queue[Math.floor((i - 1) / 2)]] = [queue[Math.floor((i - 1) / 2)], queue[i]];
        i = Math.floor((i - 1) / 2);
      }
    }
    function popCell() {
      const top = queue[0];
      const last = queue.pop();
      if (queue.length) {
        queue[0] = last;
        let i = 0;
        while (true) {
          const l = 2 * i + 1, r = 2 * i + 2;
          let largest = i;
          if (l < queue.length && queue[l].max > queue[largest].max) largest = l;
          if (r < queue.length && queue[r].max > queue[largest].max) largest = r;
          if (largest === i) break;
          [queue[i], queue[largest]] = [queue[largest], queue[i]];
          i = largest;
        }
      }
      return top;
    }
    for (let x = minX; x < maxX; x += cellSize) {
      for (let y = minY; y < maxY; y += cellSize) {
        pushCell(makeCell(x + half, y + half, half, polygon));
      }
    }
    let best = getCentroidCell(polygon);
    const bbCenter = makeCell(minX + w / 2, minY + h / 2, 0, polygon);
    if (bbCenter.d > best.d) best = bbCenter;
    while (queue.length) {
      const cell = popCell();
      if (cell.d > best.d) best = cell;
      if (cell.max - best.d <= precision) continue;
      half = cell.h / 2;
      pushCell(makeCell(cell.x - half, cell.y - half, half, polygon));
      pushCell(makeCell(cell.x + half, cell.y - half, half, polygon));
      pushCell(makeCell(cell.x - half, cell.y + half, half, polygon));
      pushCell(makeCell(cell.x + half, cell.y + half, half, polygon));
    }
    return { x: best.x, y: best.y, distance: best.d };
  }
  function makeCell(x, y, h, polygon) {
    const d = pointToPolygonDist(x, y, polygon);
    return { x, y, h, d, max: d + h * Math.SQRT2 };
  }
  function pointToPolygonDist(x, y, polygon) {
    let inside = false;
    let minDistSq = Infinity;
    for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
      const ax = polygon[i][0], ay = polygon[i][1];
      const bx = polygon[j][0], by = polygon[j][1];
      if (ay > y !== by > y && x < (bx - ax) * (y - ay) / (by - ay) + ax) {
        inside = !inside;
      }
      const dsq = segDistSq(x, y, ax, ay, bx, by);
      if (dsq < minDistSq) minDistSq = dsq;
    }
    const d = Math.sqrt(minDistSq);
    return inside ? d : -d;
  }
  function segDistSq(px, py, ax, ay, bx, by) {
    let x = ax, y = ay;
    let dx = bx - ax, dy = by - ay;
    if (dx !== 0 || dy !== 0) {
      const t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy);
      if (t > 1) {
        x = bx;
        y = by;
      } else if (t > 0) {
        x += dx * t;
        y += dy * t;
      }
    }
    dx = px - x;
    dy = py - y;
    return dx * dx + dy * dy;
  }
  function getCentroidCell(polygon) {
    let area = 0, x = 0, y = 0;
    for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
      const ax = polygon[i][0], ay = polygon[i][1];
      const bx = polygon[j][0], by = polygon[j][1];
      const f = ax * by - bx * ay;
      x += (ax + bx) * f;
      y += (ay + by) * f;
      area += f * 3;
    }
    if (area === 0) return makeCell(polygon[0][0], polygon[0][1], 0, polygon);
    return makeCell(x / area, y / area, 0, polygon);
  }

  // map/src/data/triangulate.js
  function simplifyDP(pts, tolerance) {
    if (!pts || pts.length < 4) return pts ? pts.slice() : [];
    const keep = new Uint8Array(pts.length);
    keep[0] = 1;
    keep[pts.length - 1] = 1;
    const stack = [[0, pts.length - 1]];
    while (stack.length) {
      const [a, b] = stack.pop();
      if (b <= a + 1) continue;
      const [ax, ay] = pts[a];
      const [bx, by] = pts[b];
      let maxD = 0, maxI = -1;
      const dx = bx - ax, dy = by - ay;
      const len2 = dx * dx + dy * dy || 1;
      for (let i = a + 1; i < b; i++) {
        const [px, py] = pts[i];
        const t = ((px - ax) * dx + (py - ay) * dy) / len2;
        const cx = ax + dx * t;
        const cy = ay + dy * t;
        const ddx = px - cx, ddy = py - cy;
        const d2 = ddx * ddx + ddy * ddy;
        if (d2 > maxD) {
          maxD = d2;
          maxI = i;
        }
      }
      if (maxI !== -1 && maxD > tolerance * tolerance) {
        keep[maxI] = 1;
        stack.push([a, maxI], [maxI, b]);
      }
    }
    const out = [];
    for (let i = 0; i < pts.length; i++) if (keep[i]) out.push(pts[i]);
    return out;
  }
  function simplifyToCount(ring, targetCount) {
    if (!ring || ring.length <= targetCount) return ring ? ring.slice() : [];
    let pts = ring;
    if (pts.length >= 2) {
      const [fx, fy] = pts[0];
      const [lx, ly] = pts[pts.length - 1];
      if (Math.abs(fx - lx) < 1e-12 && Math.abs(fy - ly) < 1e-12) {
        pts = pts.slice(0, -1);
      }
    }
    let lo = 0, hi = 50;
    let best = pts;
    for (let iter = 0; iter < 24; iter++) {
      const mid = (lo + hi) / 2;
      const s = simplifyDP(pts, mid);
      if (s.length > targetCount) {
        lo = mid;
      } else {
        best = s;
        if (s.length === targetCount) return best;
        hi = mid;
      }
      if (hi - lo < 1e-4) break;
    }
    if (best.length < 4) return pts.slice(0, Math.min(pts.length, 4));
    return best;
  }
  function signedArea(pts) {
    let s = 0;
    const n = pts.length;
    for (let i = 0; i < n; i++) {
      const [x0, y0] = pts[i];
      const [x1, y1] = pts[(i + 1) % n];
      s += x0 * y1 - x1 * y0;
    }
    return s / 2;
  }
  function pointInTriangle(px, py, ax, ay, bx, by, cx, cy) {
    const v0x = cx - ax, v0y = cy - ay;
    const v1x = bx - ax, v1y = by - ay;
    const v2x = px - ax, v2y = py - ay;
    const dot00 = v0x * v0x + v0y * v0y;
    const dot01 = v0x * v1x + v0y * v1y;
    const dot02 = v0x * v2x + v0y * v2y;
    const dot11 = v1x * v1x + v1y * v1y;
    const dot12 = v1x * v2x + v1y * v2y;
    const denom = dot00 * dot11 - dot01 * dot01;
    if (Math.abs(denom) < 1e-20) return false;
    const inv = 1 / denom;
    const u = (dot11 * dot02 - dot01 * dot12) * inv;
    const v = (dot00 * dot12 - dot01 * dot02) * inv;
    return u > 1e-9 && v > 1e-9 && u + v < 1 - 1e-9;
  }
  function cross(ax, ay, bx, by, cx, cy) {
    return (bx - ax) * (cy - by) - (by - ay) * (cx - bx);
  }
  function earClip(ring) {
    if (!ring || ring.length < 3) return [];
    let pts = ring;
    if (pts.length >= 2) {
      const [fx, fy] = pts[0];
      const [lx, ly] = pts[pts.length - 1];
      if (Math.abs(fx - lx) < 1e-12 && Math.abs(fy - ly) < 1e-12) {
        pts = pts.slice(0, -1);
      }
    }
    if (pts.length < 3) return [];
    if (signedArea(pts) < 0) pts = pts.slice().reverse();
    const idx = [];
    for (let i = 0; i < pts.length; i++) idx.push(i);
    const triangles = [];
    let guard = 3 * pts.length;
    while (idx.length > 3 && guard-- > 0) {
      let earFound = false;
      for (let i = 0; i < idx.length; i++) {
        const prev = idx[(i - 1 + idx.length) % idx.length];
        const curr = idx[i];
        const next = idx[(i + 1) % idx.length];
        const [ax, ay] = pts[prev];
        const [bx, by] = pts[curr];
        const [cx, cy] = pts[next];
        if (cross(ax, ay, bx, by, cx, cy) <= 0) continue;
        let contains = false;
        for (let j = 0; j < idx.length; j++) {
          if (j === i) continue;
          const k = idx[j];
          if (k === prev || k === next) continue;
          const [px, py] = pts[k];
          if (pointInTriangle(px, py, ax, ay, bx, by, cx, cy)) {
            contains = true;
            break;
          }
        }
        if (contains) continue;
        triangles.push([pts[prev], pts[curr], pts[next]]);
        idx.splice(i, 1);
        earFound = true;
        break;
      }
      if (!earFound) break;
    }
    if (idx.length === 3) {
      triangles.push([pts[idx[0]], pts[idx[1]], pts[idx[2]]]);
    }
    return triangles;
  }

  // map/src/data/centroids.js
  var FLAG_TRIANGLE_TARGET_VERTS = 8;
  var MIN_AREA_RATIO = 5e-3;
  var MAX_POLYS_PER_COUNTRY = 12;
  function computeCountryMeta(decoded, numToAlpha2) {
    const result = /* @__PURE__ */ new Map();
    for (const geom of decoded.geometries) {
      const iso = numToAlpha2.get(String(parseInt(geom.id, 10)));
      if (!iso) continue;
      const meta3 = metaForGeom(geom, decoded.arcs);
      if (!meta3) continue;
      meta3.name = geom.properties && geom.properties.name || iso.toUpperCase();
      result.set(iso, meta3);
    }
    return result;
  }
  function metaForGeom(geom, arcs) {
    const polys = geom.type === "Polygon" ? [geom.arcs] : geom.type === "MultiPolygon" ? geom.arcs : null;
    if (!polys) return null;
    let lonMin = 180, lonMax = -180, latMin = 90, latMax = -90;
    const raw = [];
    for (const poly of polys) {
      if (!poly.length) continue;
      const points = ringFromArcs(poly[0], arcs);
      if (points.length < 3) continue;
      let pLonMin = 180, pLonMax = -180, pLatMin = 90, pLatMax = -90;
      for (const [x, y] of points) {
        if (x < pLonMin) pLonMin = x;
        if (x > pLonMax) pLonMax = x;
        if (y < pLatMin) pLatMin = y;
        if (y > pLatMax) pLatMax = y;
        if (x < lonMin) lonMin = x;
        if (x > lonMax) lonMax = x;
        if (y < latMin) latMin = y;
        if (y > latMax) latMax = y;
      }
      const c = shoelaceCentroid(points);
      if (!c || c.area === 0) continue;
      raw.push({
        points,
        area: c.area,
        lon: c.x,
        lat: c.y,
        lonMin: pLonMin,
        lonMax: pLonMax,
        latMin: pLatMin,
        latMax: pLatMax
      });
    }
    if (raw.length === 0) return null;
    raw.sort((a, b) => b.area - a.area);
    const biggest = raw[0];
    const areaThreshold = biggest.area * MIN_AREA_RATIO;
    const kept = raw.filter((p, i) => i === 0 || p.area >= areaThreshold).slice(0, MAX_POLYS_PER_COUNTRY);
    const polysMeta = kept.map((p) => {
      const span = Math.min(p.lonMax - p.lonMin, p.latMax - p.latMin);
      const precision = Math.max(0.02, span / 200);
      const pol = polylabel(p.points, precision);
      return {
        lon: p.lon,
        lat: p.lat,
        lonMin: p.lonMin,
        lonMax: p.lonMax,
        latMin: p.latMin,
        latMax: p.latMax,
        lonRange: p.lonMax - p.lonMin,
        latRange: p.latMax - p.latMin,
        anchorLon: pol.x,
        anchorLat: pol.y,
        anchorRadius: pol.distance,
        area: p.area,
        // Ring points (lon,lat[]) — needed by the flag layer to project a clip
        // path per polygon every frame in globe mode, so each island's flag
        // stays put under orthographic distortion.
        points: p.points,
        // Triangulation: simplify polygon ring to ≤8 verts, then ear-clip.
        // ≤6 triangles per polygon. Per-triangle 3-point affine in the flag
        // layer gives full fill + no rotation drift.
        triangles: earClip(simplifyToCount(p.points, FLAG_TRIANGLE_TARGET_VERTS))
      };
    });
    const main = polysMeta[0];
    return {
      // Country-level (for labels / navigation / sidebar)
      lon: main.lon,
      lat: main.lat,
      lonMin,
      lonMax,
      latMin,
      latMax,
      lonRange: lonMax - lonMin,
      latRange: latMax - latMin,
      // Main-poly bbox (kept for backwards compat with anything that still asks)
      mainLonMin: main.lonMin,
      mainLonMax: main.lonMax,
      mainLatMin: main.latMin,
      mainLatMax: main.latMax,
      mainLonRange: main.lonRange,
      mainLatRange: main.latRange,
      flagAnchorLon: main.anchorLon,
      flagAnchorLat: main.anchorLat,
      flagAnchorRadius: main.anchorRadius,
      // NEW: per-polygon array. Flag layer iterates this and stamps every entry.
      polys: polysMeta
    };
  }

  // map/src/data/loader.js
  var _topoPromise = null;
  var _flagsPromise = null;
  var _flagPositionsPromise = null;
  function loadScriptGlobal(scriptSrc, globalName) {
    if (typeof window !== "undefined" && window[globalName] != null) {
      return Promise.resolve(window[globalName]);
    }
    return new Promise((resolve, reject) => {
      const s = document.createElement("script");
      s.src = scriptSrc;
      s.async = true;
      s.onload = () => {
        if (window[globalName] == null) {
          reject(new Error("Script loaded but " + globalName + " not set: " + scriptSrc));
        } else {
          resolve(window[globalName]);
        }
      };
      s.onerror = () => reject(new Error("Failed to load " + scriptSrc));
      document.head.appendChild(s);
    });
  }
  function loadTopology(opts = {}) {
    if (_topoPromise) return _topoPromise;
    const src = opts.url || "world-topo-data.js?v=4";
    _topoPromise = loadScriptGlobal(src, "WUYS_WORLD_TOPO");
    return _topoPromise;
  }
  function loadFlags(opts = {}) {
    if (_flagsPromise) return _flagsPromise;
    const src = opts.url || "flags-data.js?v=8";
    _flagsPromise = loadScriptGlobal(src, "WUYS_FLAGS").then((data) => {
      if (data && data.flags) return data.flags;
      return data;
    });
    return _flagsPromise;
  }
  function loadFlagPositions(opts = {}) {
    if (_flagPositionsPromise) return _flagPositionsPromise;
    const src = opts.url || "flag-positions.js?v=1";
    _flagPositionsPromise = loadScriptGlobal(src, "WUYS_FLAG_POSITIONS").catch(() => ({}));
    return _flagPositionsPromise;
  }

  // map/src/data/iso-codes.js
  function tableFromDict(dict) {
    const m = /* @__PURE__ */ new Map();
    for (const [k, v] of Object.entries(dict || {})) m.set(String(parseInt(k, 10)), v);
    return m;
  }

  // map/src/renderers/base.js
  var REGISTRY = /* @__PURE__ */ new Map();
  function registerRenderer(id, factory) {
    REGISTRY.set(id, factory);
  }
  function createRenderer(id, args) {
    const factory = REGISTRY.get(id);
    if (!factory) {
      throw new Error("[wuys/map] Unknown renderer: " + id + ". Available: " + [...REGISTRY.keys()].join(", "));
    }
    return factory(args);
  }
  function listRenderers() {
    return [...REGISTRY.keys()];
  }

  // map/src/math/antimeridian.js
  function splitOnAntimeridian(points, halfW) {
    if (!points || points.length < 2) return "";
    let d = "";
    let prevX = null;
    for (let i = 0; i < points.length; i++) {
      const [x, y] = points[i];
      if (i === 0) {
        d += "M" + x.toFixed(2) + "," + y.toFixed(2);
      } else if (Math.abs(x - prevX) > halfW) {
        d += "Z M" + x.toFixed(2) + "," + y.toFixed(2);
      } else {
        d += "L" + x.toFixed(2) + "," + y.toFixed(2);
      }
      prevX = x;
    }
    d += "Z";
    return d;
  }

  // map/src/math/horizon.js
  function clipToHorizon(projected, toView) {
    if (!projected || projected.length < 3) return "";
    function horizon(p1, p2) {
      const t = p1.z / (p1.z - p2.z);
      let x = (1 - t) * p1.x + t * p2.x;
      let y = (1 - t) * p1.y + t * p2.y;
      const r = Math.sqrt(x * x + y * y) || 1;
      return { x: x / r, y: y / r, z: 0 };
    }
    let d = "";
    let inSegment = false;
    function moveTo(p) {
      const [vx, vy] = toView(p);
      d += "M" + vx.toFixed(2) + "," + vy.toFixed(2);
      inSegment = true;
    }
    function lineTo(p) {
      const [vx, vy] = toView(p);
      d += "L" + vx.toFixed(2) + "," + vy.toFixed(2);
    }
    function close() {
      if (inSegment) {
        d += "Z";
        inSegment = false;
      }
    }
    for (let i = 0; i < projected.length; i++) {
      const p1 = projected[i];
      const p2 = projected[(i + 1) % projected.length];
      const v1 = p1.z >= 0;
      const v2 = p2.z >= 0;
      if (v1 && v2) {
        if (!inSegment) moveTo(p1);
        lineTo(p2);
      } else if (v1 && !v2) {
        const h = horizon(p1, p2);
        if (!inSegment) moveTo(p1);
        lineTo(h);
        close();
      } else if (!v1 && v2) {
        const h = horizon(p1, p2);
        moveTo(h);
        lineTo(p2);
      }
    }
    close();
    return d;
  }

  // map/src/renderers/svg.js
  var SVG_NS = "http://www.w3.org/2000/svg";
  var VIEWBOX = "0 0 2000 1000";
  var DEG2RAD = Math.PI / 180;
  function projectRingGlobe(arcRefs, arcs, rotLambdaRad, sinPhi0, cosPhi0, baseR, CX3, CY3) {
    let d = "";
    let inSegment = false;
    let prevX = 0, prevY = 0, prevZ = 0, prevVx = 0, prevVy = 0;
    let firstX = 0, firstY = 0, firstZ = 0;
    let count = 0;
    for (let aRef = 0; aRef < arcRefs.length; aRef++) {
      const idx = arcRefs[aRef];
      const reverse = idx < 0;
      const arc = arcs[reverse ? ~idx : idx];
      const len = arc.length;
      const skip = count > 0 ? 1 : 0;
      const i0 = reverse ? len - 1 - skip : skip;
      const i1 = reverse ? -1 : len;
      const step = reverse ? -1 : 1;
      for (let i = i0; i !== i1; i += step) {
        const pt = arc[i];
        const lambda = pt[0] * DEG2RAD;
        const phi = pt[1] * DEG2RAD;
        const dLam = lambda - rotLambdaRad;
        const cosLam = Math.cos(dLam);
        const sinLam = Math.sin(dLam);
        const cosPhi = Math.cos(phi);
        const sinPhi = Math.sin(phi);
        const x = cosPhi * sinLam;
        const y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosLam;
        const z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosLam;
        const vx = CX3 + x * baseR;
        const vy = CY3 - y * baseR;
        if (count === 0) {
          firstX = x;
          firstY = y;
          firstZ = z;
          prevX = x;
          prevY = y;
          prevZ = z;
          prevVx = vx;
          prevVy = vy;
          count = 1;
          continue;
        }
        const v1 = prevZ >= 0;
        const v2 = z >= 0;
        if (v1 && v2) {
          if (!inSegment) {
            d += "M" + prevVx.toFixed(2) + "," + prevVy.toFixed(2);
            inSegment = true;
          }
          d += "L" + vx.toFixed(2) + "," + vy.toFixed(2);
        } else if (v1 && !v2) {
          const t = prevZ / (prevZ - z);
          let hx = prevX + t * (x - prevX);
          let hy = prevY + t * (y - prevY);
          const r = Math.sqrt(hx * hx + hy * hy) || 1;
          hx /= r;
          hy /= r;
          if (!inSegment) {
            d += "M" + prevVx.toFixed(2) + "," + prevVy.toFixed(2);
            inSegment = true;
          }
          d += "L" + (CX3 + hx * baseR).toFixed(2) + "," + (CY3 - hy * baseR).toFixed(2) + "Z";
          inSegment = false;
        } else if (!v1 && v2) {
          const t = prevZ / (prevZ - z);
          let hx = prevX + t * (x - prevX);
          let hy = prevY + t * (y - prevY);
          const r = Math.sqrt(hx * hx + hy * hy) || 1;
          hx /= r;
          hy /= r;
          d += "M" + (CX3 + hx * baseR).toFixed(2) + "," + (CY3 - hy * baseR).toFixed(2) + "L" + vx.toFixed(2) + "," + vy.toFixed(2);
          inSegment = true;
        }
        prevX = x;
        prevY = y;
        prevZ = z;
        prevVx = vx;
        prevVy = vy;
        count++;
      }
    }
    if (count > 2) {
      const firstVx = CX3 + firstX * baseR;
      const firstVy = CY3 - firstY * baseR;
      const v1 = prevZ >= 0;
      const v2 = firstZ >= 0;
      if (v1 && v2) {
        if (!inSegment) {
          d += "M" + prevVx.toFixed(2) + "," + prevVy.toFixed(2);
          inSegment = true;
        }
        d += "L" + firstVx.toFixed(2) + "," + firstVy.toFixed(2);
      } else if (v1 && !v2) {
        const t = prevZ / (prevZ - firstZ);
        let hx = prevX + t * (firstX - prevX);
        let hy = prevY + t * (firstY - prevY);
        const r = Math.sqrt(hx * hx + hy * hy) || 1;
        hx /= r;
        hy /= r;
        if (!inSegment) {
          d += "M" + prevVx.toFixed(2) + "," + prevVy.toFixed(2);
          inSegment = true;
        }
        d += "L" + (CX3 + hx * baseR).toFixed(2) + "," + (CY3 - hy * baseR).toFixed(2);
      } else if (!v1 && v2) {
        const t = prevZ / (prevZ - firstZ);
        let hx = prevX + t * (firstX - prevX);
        let hy = prevY + t * (firstY - prevY);
        const r = Math.sqrt(hx * hx + hy * hy) || 1;
        hx /= r;
        hy /= r;
        d += "M" + (CX3 + hx * baseR).toFixed(2) + "," + (CY3 - hy * baseR).toFixed(2) + "L" + firstVx.toFixed(2) + "," + firstVy.toFixed(2);
        inSegment = true;
      }
    }
    if (inSegment) d += "Z";
    return d;
  }
  function createSvgRenderer({ state, events, theme, layers: layers2, getCountryName } = {}) {
    let svg = null;
    let target = null;
    let arcs = null;
    let geomByIso = /* @__PURE__ */ new Map();
    const builtLayers = [];
    let projectFn = null;
    const _flatPathCache = /* @__PURE__ */ new Map();
    const _flatRectCache = /* @__PURE__ */ new Map();
    const _flatLabelPos = /* @__PURE__ */ new Map();
    let _flatPathReady = false;
    let _lastMode = null;
    let _resizeBound = false;
    function init(targetEl, opts) {
      target = targetEl;
      svg = document.createElementNS(SVG_NS, "svg");
      svg.setAttribute("viewBox", VIEWBOX);
      svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
      svg.setAttribute("class", "wuys-map-svg");
      svg.style.cssText = "width:100%;height:100%;display:block;";
      target.appendChild(svg);
      const oceanRect = document.createElementNS(SVG_NS, "rect");
      oceanRect.setAttribute("class", "wuys-map-ocean wuys-map-ocean-rect");
      oceanRect.setAttribute("x", "0");
      oceanRect.setAttribute("y", "0");
      oceanRect.setAttribute("width", "2000");
      oceanRect.setAttribute("height", "1000");
      svg.appendChild(oceanRect);
      const oceanCircle = document.createElementNS(SVG_NS, "circle");
      oceanCircle.setAttribute("class", "wuys-map-ocean wuys-map-ocean-circle");
      oceanCircle.setAttribute("cx", "1000");
      oceanCircle.setAttribute("cy", "500");
      oceanCircle.setAttribute("r", "450");
      oceanCircle.setAttribute("display", "none");
      svg.appendChild(oceanCircle);
      arcs = state._topologyArcs;
      geomByIso = state._geomByIso;
      const buildCtx = {
        svg,
        state,
        theme,
        events
      };
      for (const layer of layers2) {
        layer.build(buildCtx);
        builtLayers.push(layer);
      }
      applyTheme();
      if (!_resizeBound) {
        _resizeBound = true;
        window.addEventListener("resize", () => {
          _flatPathReady = false;
          patch();
        });
      }
    }
    function applyTheme() {
      if (!svg) return;
      for (const [k, v] of Object.entries(theme.cssVars || {})) {
        svg.style.setProperty(k, v);
      }
      const oRect = svg.querySelector(".wuys-map-ocean-rect");
      const oCircle = svg.querySelector(".wuys-map-ocean-circle");
      if (oRect) {
        oRect.setAttribute("fill", theme.ocean.fill);
        oRect.setAttribute("stroke", "none");
      }
      if (oCircle) {
        oCircle.setAttribute("fill", theme.ocean.fill);
        oCircle.setAttribute("stroke", theme.ocean.stroke || "none");
        oCircle.setAttribute("stroke-width", theme.ocean.strokeWidth || 0);
      }
      for (const layer of builtLayers) if (layer.applyTheme) layer.applyTheme({ svg, state, theme });
      target.style.background = theme.background || "";
    }
    function patch() {
      doPatch();
    }
    function doPatch() {
      if (!svg) return;
      if (!Number.isFinite(state.nav.lon)) {
        state.nav.lon = 0;
        state.nav.tLon = 0;
      }
      if (!Number.isFinite(state.nav.lat)) {
        state.nav.lat = 0;
        state.nav.tLat = 0;
      }
      if (!Number.isFinite(state.nav.zoom) || state.nav.zoom <= 0) {
        state.nav.zoom = 1;
        state.nav.tZoom = 1;
      }
      const mode = state.view.mode;
      if (mode !== _lastMode) {
        _flatPathReady = false;
        _flatPathCache.clear();
        _flatRectCache.clear();
        _flatLabelPos.clear();
        _lastMode = mode;
      }
      const oRect = svg.querySelector(".wuys-map-ocean-rect");
      const oCircle = svg.querySelector(".wuys-map-ocean-circle");
      if (mode === "globe") {
        oRect.setAttribute("display", "none");
        oCircle.removeAttribute("display");
        const baseR = 450 * state.nav.zoom;
        oCircle.setAttribute("r", baseR);
        svg.setAttribute("viewBox", VIEWBOX);
      } else {
        oRect.removeAttribute("display");
        oCircle.setAttribute("display", "none");
        const rect = target.getBoundingClientRect();
        const aspect = rect.width && rect.height ? rect.width / rect.height : 2;
        let vbW, vbH, vbOffX, vbOffY;
        if (aspect > 2) {
          vbW = 1e3 * aspect;
          vbH = 1e3;
          vbOffX = (vbW - 2e3) / 2;
          vbOffY = 0;
        } else {
          vbW = 2e3;
          vbH = 2e3 / aspect;
          vbOffX = 0;
          vbOffY = (vbH - 1e3) / 2;
        }
        oRect.setAttribute("x", String(-vbOffX));
        oRect.setAttribute("y", String(-vbOffY));
        oRect.setAttribute("width", String(vbW));
        oRect.setAttribute("height", String(vbH));
        const w = vbW / state.nav.zoom;
        const h = vbH / state.nav.zoom;
        const [vbx, vby] = project(state.nav.lon, state.nav.lat);
        const cx = Math.max(w / 2 - vbOffX, Math.min(2e3 + vbOffX - w / 2, vbx));
        const cy = Math.max(h / 2 - vbOffY, Math.min(1e3 + vbOffY - h / 2, vby));
        svg.setAttribute("viewBox", cx - w / 2 + " " + (cy - h / 2) + " " + w + " " + h);
      }
      if (mode === "flat" && _flatPathReady) return;
      const ctx = buildPatchContext();
      for (const layer of builtLayers) layer.patch(ctx);
      if (mode === "flat") _flatPathReady = true;
    }
    function invalidateFlatPatch() {
      _flatPathReady = false;
    }
    if (events && events.on) {
      events.on("theme:change", invalidateFlatPatch);
      events.on("theme:flags", invalidateFlatPatch);
      events.on("theme:labels", invalidateFlatPatch);
      events.on("view:change", invalidateFlatPatch);
    }
    function buildPatchContext() {
      const mode = state.view.mode;
      const rotLon = state.nav.lon, rotLat = state.nav.lat, zoom = state.nav.zoom;
      const sinRotPhi = Math.sin(rotLat * Math.PI / 180);
      const cosRotPhi = Math.cos(rotLat * Math.PI / 180);
      const rotLambdaRad = rotLon * Math.PI / 180;
      const orthoProject = projector(rotLambdaRad, rotLat * Math.PI / 180);
      const baseR = 450 * zoom;
      const CX3 = 1e3, CY3 = 500;
      const _patchPathCache = /* @__PURE__ */ new Map();
      const _patchRectCache = /* @__PURE__ */ new Map();
      const _patchVisCache = /* @__PURE__ */ new Map();
      const _patchLabelCache = /* @__PURE__ */ new Map();
      function isVisibleAt(iso) {
        if (mode !== "globe") return true;
        if (_patchVisCache.has(iso)) return _patchVisCache.get(iso);
        const cm = state.data.countries.get(iso);
        if (!cm) {
          _patchVisCache.set(iso, false);
          return false;
        }
        const phi = cm.lat * Math.PI / 180;
        const lam = cm.lon * Math.PI / 180;
        const cosDist = Math.sin(phi) * sinRotPhi + Math.cos(phi) * cosRotPhi * Math.cos(lam - rotLambdaRad);
        const v = cosDist >= -0.3;
        _patchVisCache.set(iso, v);
        return v;
      }
      function pathForCountry(iso) {
        if (mode === "flat" && _flatPathCache.has(iso)) return _flatPathCache.get(iso);
        if (_patchPathCache.has(iso)) return _patchPathCache.get(iso);
        const geom = geomByIso.get(iso);
        if (!geom) return "";
        const polys = geom.type === "Polygon" ? [geom.arcs] : geom.type === "MultiPolygon" ? geom.arcs : null;
        if (!polys) return "";
        let d = "";
        if (mode === "flat") {
          for (const poly of polys) {
            for (const ring of poly) {
              const pts = ringFromArcs(ring, arcs);
              if (pts.length < 3) continue;
              const proj = pts.map(([lon, lat]) => project(lon, lat));
              d += splitOnAntimeridian(proj, 1e3);
            }
          }
          _flatPathCache.set(iso, d);
        } else {
          for (const poly of polys) {
            for (const ring of poly) {
              d += projectRingGlobe(ring, arcs, rotLambdaRad, sinRotPhi, cosRotPhi, baseR, CX3, CY3);
            }
          }
          _patchPathCache.set(iso, d);
        }
        return d;
      }
      function viewToCountryRect(iso) {
        if (mode === "flat" && _flatRectCache.has(iso)) return _flatRectCache.get(iso);
        if (_patchRectCache.has(iso)) return _patchRectCache.get(iso);
        const cm = state.data.countries.get(iso);
        if (!cm) return null;
        const aLon = cm.flagAnchorLon ?? cm.lon;
        const aLat = cm.flagAnchorLat ?? cm.lat;
        const radiusDeg = cm.flagAnchorRadius || 0.5;
        const ASPECT = 1.5;
        if (mode === "flat") {
          let h = radiusDeg * 2 * 5.555 * 0.95;
          const MIN_H = 18, MAX_H = 80;
          if (h < MIN_H) h = MIN_H;
          if (h > MAX_H) h = MAX_H;
          const w = h * ASPECT;
          const [cx, cy] = project(aLon, aLat);
          const rect = { x: cx - w / 2, y: cy - h / 2, w, h };
          _flatRectCache.set(iso, rect);
          return rect;
        } else {
          const pc = orthoProject(aLon, aLat);
          if (pc.z < -0.05) {
            _patchRectCache.set(iso, null);
            return null;
          }
          const unit = baseR / 57.3;
          let h = radiusDeg * 2 * unit * 0.95;
          const MIN_H = 10, MAX_H = 60;
          if (h < MIN_H) h = MIN_H;
          if (h > MAX_H) h = MAX_H;
          const w = h * ASPECT;
          const cx = CX3 + pc.x * baseR;
          const cy = CY3 - pc.y * baseR;
          const rect = { x: cx - w / 2, y: cy - h / 2, w, h };
          _patchRectCache.set(iso, rect);
          return rect;
        }
      }
      function projectedHeightOf(iso) {
        const cm = state.data.countries.get(iso);
        if (!cm) return 0;
        if (mode === "flat") return cm.latRange * 5.555;
        return cm.latRange * baseR / 57.3;
      }
      function screenForLabel(iso, label) {
        const cm = state.data.countries.get(iso);
        if (!cm) return null;
        const lon = label && label.lon != null ? label.lon : cm.lon;
        const lat = label && label.lat != null ? label.lat : cm.lat;
        if (mode === "flat") {
          const key = iso + ":" + lon + "," + lat;
          const cached = _flatLabelPos.get(key);
          if (cached) return cached;
          const [x, y] = project(lon, lat);
          const pos = { x, y };
          _flatLabelPos.set(key, pos);
          return pos;
        }
        const p = orthoProject(lon, lat);
        if (p.z < 0) return null;
        return { x: CX3 + p.x * baseR, y: CY3 - p.y * baseR };
      }
      function projectRing(points) {
        if (!points || points.length < 3) return "";
        if (mode === "flat") {
          const proj = points.map(([lon, lat]) => project(lon, lat));
          return splitOnAntimeridian(proj, 1e3);
        }
        const projected = points.map(([lon, lat]) => orthoProject(lon, lat));
        return clipToHorizon(projected, (p) => [CX3 + p.x * baseR, CY3 - p.y * baseR]);
      }
      function viewToPolyRect(poly) {
        const aLon = poly.anchorLon;
        const aLat = poly.anchorLat;
        const radiusDeg = poly.anchorRadius || 0.3;
        const ASPECT = 1.5;
        if (mode === "flat") {
          let h = radiusDeg * 2 * 5.555 * 0.95;
          const MIN_H = 12, MAX_H = 80;
          if (h < MIN_H) h = MIN_H;
          if (h > MAX_H) h = MAX_H;
          const w = h * ASPECT;
          const [cx, cy] = project(aLon, aLat);
          return { x: cx - w / 2, y: cy - h / 2, w, h };
        } else {
          const pc = orthoProject(aLon, aLat);
          if (pc.z < -0.05) return null;
          const unit = baseR / 57.3;
          let h = radiusDeg * 2 * unit * 0.95;
          const MIN_H = 7, MAX_H = 60;
          if (h < MIN_H) h = MIN_H;
          if (h > MAX_H) h = MAX_H;
          const w = h * ASPECT;
          const cx = CX3 + pc.x * baseR;
          const cy = CY3 - pc.y * baseR;
          return { x: cx - w / 2, y: cy - h / 2, w, h };
        }
      }
      function pathForPoly(poly) {
        if (!poly || !poly.points) return "";
        return projectRing(poly.points);
      }
      function viewToPolyAffine(poly) {
        if (!poly) return null;
        if (mode === "flat") {
          const tl = project(poly.lonMin, poly.latMax);
          const tr = project(poly.lonMax, poly.latMax);
          const bl = project(poly.lonMin, poly.latMin);
          if (!Number.isFinite(tl[0]) || !Number.isFinite(tr[0]) || !Number.isFinite(bl[0])) return null;
          return [tr[0] - tl[0], tr[1] - tl[1], bl[0] - tl[0], bl[1] - tl[1], tl[0], tl[1]];
        } else {
          const tlP = orthoProject(poly.lonMin, poly.latMax);
          const trP = orthoProject(poly.lonMax, poly.latMax);
          const blP = orthoProject(poly.lonMin, poly.latMin);
          if (tlP.z < -0.1 && trP.z < -0.1 && blP.z < -0.1) return null;
          const tlx = CX3 + tlP.x * baseR, tly = CY3 - tlP.y * baseR;
          const trx = CX3 + trP.x * baseR, tryv = CY3 - trP.y * baseR;
          const blx = CX3 + blP.x * baseR, bly = CY3 - blP.y * baseR;
          if (!Number.isFinite(tlx) || !Number.isFinite(trx) || !Number.isFinite(blx)) return null;
          return [trx - tlx, tryv - tly, blx - tlx, bly - tly, tlx, tly];
        }
      }
      function viewToPolyFullRect(poly) {
        if (!poly || !poly.points) return null;
        if (mode === "flat") {
          const ul = project(poly.lonMin, poly.latMax);
          const lr = project(poly.lonMax, poly.latMin);
          const w = lr[0] - ul[0], h = lr[1] - ul[1];
          if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) return null;
          return { x: ul[0], y: ul[1], w, h };
        } else {
          let minVx = Infinity, maxVx = -Infinity;
          let minVy = Infinity, maxVy = -Infinity;
          let any = false;
          const pts = poly.points;
          for (let i = 0; i < pts.length; i++) {
            const lon = pts[i][0], lat = pts[i][1];
            const p = orthoProject(lon, lat);
            if (p.z < -0.05) continue;
            any = true;
            const sx = CX3 + p.x * baseR;
            const sy = CY3 - p.y * baseR;
            if (sx < minVx) minVx = sx;
            if (sx > maxVx) maxVx = sx;
            if (sy < minVy) minVy = sy;
            if (sy > maxVy) maxVy = sy;
          }
          if (!any) return null;
          const w = maxVx - minVx, h = maxVy - minVy;
          if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) return null;
          return { x: minVx, y: minVy, w, h };
        }
      }
      function viewToFullCountryRect(iso) {
        const cm = state.data.countries.get(iso);
        if (!cm) return null;
        if (mode === "flat") {
          const minLon = cm.lonMin, maxLon = cm.lonMax;
          const minLat = cm.latMin, maxLat = cm.latMax;
          const ul = project(minLon, maxLat);
          const lr = project(maxLon, minLat);
          const w = lr[0] - ul[0], h = lr[1] - ul[1];
          if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) return null;
          return { x: ul[0], y: ul[1], w, h };
        } else {
          const lon = cm.lon, lat = cm.lat;
          const pc = orthoProject(lon, lat);
          if (pc.z < -0.05) return null;
          const cosLat = Math.cos(lat * Math.PI / 180) || 1;
          const unit = baseR / 57.3;
          const w = cm.lonRange * cosLat * unit;
          const h = cm.latRange * unit;
          if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) return null;
          const cx = CX3 + pc.x * baseR;
          const cy = CY3 - pc.y * baseR;
          return { x: cx - w / 2, y: cy - h / 2, w, h };
        }
      }
      function projectLatLonToScreen(lon, lat) {
        if (mode === "flat") {
          const [x, y] = project(lon, lat);
          if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
          return { x, y };
        } else {
          const p = orthoProject(lon, lat);
          if (p.z < -0.05) return null;
          return { x: CX3 + p.x * baseR, y: CY3 - p.y * baseR };
        }
      }
      function viewToPointRect(lon, lat, sizeHint) {
        const ASPECT = 1.5;
        const scale = sizeHint != null ? sizeHint : 1;
        if (mode === "flat") {
          const h = 28 * scale;
          const w = h * ASPECT;
          const [cx, cy] = project(lon, lat);
          if (!Number.isFinite(cx) || !Number.isFinite(cy)) return null;
          return { x: cx - w / 2, y: cy - h / 2, w, h };
        } else {
          const pc = orthoProject(lon, lat);
          if (pc.z < -0.05) return null;
          const unit = baseR / 57.3;
          const h = 5 * scale * unit;
          const w = h * ASPECT;
          const cx = CX3 + pc.x * baseR;
          const cy = CY3 - pc.y * baseR;
          return { x: cx - w / 2, y: cy - h / 2, w, h };
        }
      }
      return {
        state,
        theme,
        mode,
        pathForCountry,
        pathForPoly,
        viewToCountryRect,
        viewToPolyRect,
        viewToPointRect,
        viewToFullCountryRect,
        viewToPolyFullRect,
        viewToPolyAffine,
        projectLatLonToScreen,
        projectedHeightOf,
        screenForLabel,
        isVisibleAt,
        projectRing
      };
    }
    function worldToScreen({ lon, lat }) {
      const mode = state.view.mode;
      const rect = svg.getBoundingClientRect();
      const vb = svg.viewBox.baseVal;
      let vbx, vby;
      if (mode === "flat") {
        [vbx, vby] = project(lon, lat);
      } else {
        const p = project2(lon, lat, { rotLon: state.nav.lon, rotLat: state.nav.lat, zoom: state.nav.zoom });
        vbx = p.vbx;
        vby = p.vby;
      }
      const scale = Math.min(rect.width / vb.width, rect.height / vb.height);
      const renderedW = vb.width * scale, renderedH = vb.height * scale;
      const ox = (rect.width - renderedW) / 2, oy = (rect.height - renderedH) / 2;
      return { x: ox + (vbx - vb.x) * scale, y: oy + (vby - vb.y) * scale };
    }
    function screenToWorld({ x, y }) {
      const mode = state.view.mode;
      const rect = svg.getBoundingClientRect();
      const vb = svg.viewBox.baseVal;
      const scale = Math.min(rect.width / vb.width, rect.height / vb.height);
      const renderedW = vb.width * scale, renderedH = vb.height * scale;
      const ox = (rect.width - renderedW) / 2, oy = (rect.height - renderedH) / 2;
      const vbx = vb.x + (x - ox) / scale;
      const vby = vb.y + (y - oy) / scale;
      if (mode === "flat") return unproject(vbx, vby);
      return unproject2(vbx, vby, { rotLon: state.nav.lon, rotLat: state.nav.lat, zoom: state.nav.zoom });
    }
    function viewportToWorld(dxPx, dyPx) {
      const c = worldToScreen({ lon: state.nav.lon, lat: state.nav.lat });
      const target2 = screenToWorld({ x: c.x + dxPx, y: c.y + dyPx });
      return { dLon: target2.lon - state.nav.lon, dLat: target2.lat - state.nav.lat };
    }
    function getViewport() {
      const mode = state.view.mode;
      if (mode === "flat") {
        const vb = svg.viewBox.baseVal;
        const ul = unproject(vb.x, vb.y);
        const lr = unproject(vb.x + vb.width, vb.y + vb.height);
        return { lonMin: ul.lon, lonMax: lr.lon, latMin: lr.lat, latMax: ul.lat };
      }
      return { lonMin: state.nav.lon - 90, lonMax: state.nav.lon + 90, latMin: state.nav.lat - 90, latMax: state.nav.lat + 90 };
    }
    function hitTest(x, y) {
      if (!svg) return null;
      const el2 = document.elementFromPoint(x, y);
      if (!el2) return null;
      const path = el2.closest("path[data-iso]");
      if (!path) return null;
      return { iso: path.getAttribute("data-iso"), element: path };
    }
    function destroy() {
      for (const layer of builtLayers) layer.destroy && layer.destroy();
      builtLayers.length = 0;
      if (svg && svg.parentNode) svg.parentNode.removeChild(svg);
      svg = null;
    }
    const renderer = {
      init: (...a) => Promise.resolve(init(...a)),
      patch,
      destroy,
      hitTest,
      worldToScreen,
      screenToWorld,
      viewportToWorld,
      getViewport,
      applyTheme,
      getSvg: () => svg
    };
    return renderer;
  }
  registerRenderer("svg", (args) => createSvgRenderer(args));

  // map/src/renderers/canvas.js
  var VB_W2 = 2e3;
  var VB_H2 = 1e3;
  function createCanvasRenderer({ state, events, theme } = {}) {
    let target = null;
    let canvas = null, ctx2d = null;
    let hitCanvas = null, hitCtx = null;
    let dpr = 1;
    let arcs = null, geomByIso = null;
    const flagImages = /* @__PURE__ */ new Map();
    const idColors = /* @__PURE__ */ new Map();
    const colorToIso = /* @__PURE__ */ new Map();
    function init(targetEl) {
      target = targetEl;
      canvas = document.createElement("canvas");
      canvas.style.cssText = "width:100%;height:100%;display:block;";
      target.appendChild(canvas);
      ctx2d = canvas.getContext("2d");
      hitCanvas = document.createElement("canvas");
      hitCtx = hitCanvas.getContext("2d", { willReadFrequently: true });
      arcs = state._topologyArcs;
      geomByIso = state._geomByIso;
      assignIdColors();
      preloadFlags();
      resize();
      window.addEventListener("resize", resize);
    }
    function resize() {
      if (!target) return;
      const rect = target.getBoundingClientRect();
      dpr = window.devicePixelRatio || 1;
      canvas.width = Math.max(1, Math.floor(rect.width * dpr));
      canvas.height = Math.max(1, Math.floor(rect.height * dpr));
      hitCanvas.width = canvas.width;
      hitCanvas.height = canvas.height;
    }
    function assignIdColors() {
      let i = 0;
      for (const iso of state.data.countries.keys()) {
        i++;
        const r = i >> 16 & 255;
        const g = i >> 8 & 255;
        const b = i & 255;
        idColors.set(iso, [r, g, b]);
        colorToIso.set(r + "," + g + "," + b, iso);
      }
    }
    function preloadFlags() {
      for (const [iso, src] of state.data.flags) {
        if (!src) continue;
        const img = new Image();
        img.src = src;
        img.onload = () => requestPatch();
        flagImages.set(iso, img);
      }
    }
    let pendingPatch = false;
    function requestPatch() {
      if (pendingPatch) return;
      pendingPatch = true;
      requestAnimationFrame(() => {
        pendingPatch = false;
        patch();
      });
    }
    function patch() {
      if (!ctx2d) return;
      const w = canvas.width, h = canvas.height;
      const scale = Math.min(w / VB_W2, h / VB_H2);
      const ox = (w - VB_W2 * scale) / 2;
      const oy = (h - VB_H2 * scale) / 2;
      ctx2d.save();
      ctx2d.clearRect(0, 0, w, h);
      ctx2d.fillStyle = "#050210";
      ctx2d.fillRect(0, 0, w, h);
      ctx2d.translate(ox, oy);
      ctx2d.scale(scale, scale);
      const mode = state.view.mode;
      const rotLambdaRad = state.nav.lon * Math.PI / 180;
      const rotPhiRad = state.nav.lat * Math.PI / 180;
      const sinRotPhi = Math.sin(rotPhiRad), cosRotPhi = Math.cos(rotPhiRad);
      const orthoProject = projector(rotLambdaRad, rotPhiRad);
      const baseR = 450 * state.nav.zoom;
      const CX3 = 1e3, CY3 = 500;
      if (mode === "flat") {
        const z = state.nav.zoom;
        const [vbx, vby] = project(state.nav.lon, state.nav.lat);
        const w2 = VB_W2 / z, h2 = VB_H2 / z;
        const cx = Math.max(w2 / 2, Math.min(VB_W2 - w2 / 2, vbx));
        const cy = Math.max(h2 / 2, Math.min(VB_H2 - h2 / 2, vby));
        ctx2d.scale(z, z);
        ctx2d.translate(-(cx - w2 / 2), -(cy - h2 / 2));
      }
      if (mode === "flat") {
        ctx2d.fillStyle = theme.ocean.fill;
        ctx2d.fillRect(0, 0, VB_W2, VB_H2);
      } else {
        ctx2d.beginPath();
        ctx2d.arc(CX3, CY3, baseR, 0, Math.PI * 2);
        ctx2d.fillStyle = theme.ocean.fill;
        ctx2d.fill();
        if (theme.ocean.strokeWidth) {
          ctx2d.lineWidth = theme.ocean.strokeWidth;
          ctx2d.strokeStyle = theme.ocean.stroke || "transparent";
          ctx2d.stroke();
        }
      }
      function eachCountryRings(geom, cb) {
        const polys = geom.type === "Polygon" ? [geom.arcs] : geom.type === "MultiPolygon" ? geom.arcs : null;
        if (!polys) return;
        for (const poly of polys) for (const ring of poly) cb(ring);
      }
      function isVisible(cm) {
        if (mode !== "globe") return true;
        const phi = cm.lat * Math.PI / 180;
        const lam = cm.lon * Math.PI / 180;
        const cosDist = Math.sin(phi) * sinRotPhi + Math.cos(phi) * cosRotPhi * Math.cos(lam - rotLambdaRad);
        return cosDist >= -0.3;
      }
      const flagsOn = state.theme.flagsVisible && !theme.countries.hidesFlags;
      ctx2d.lineWidth = theme.countries.strokeWidth || 0.5;
      ctx2d.strokeStyle = theme.countries.stroke || "rgba(255,255,255,0.35)";
      for (const [iso, geom] of geomByIso) {
        const cm = state.data.countries.get(iso);
        if (!cm || !isVisible(cm)) continue;
        const path2d = buildCountryPath2D(iso, geom, mode, orthoProject, baseR, CX3, CY3);
        if (!path2d) continue;
        const flag = flagsOn ? flagImages.get(iso) : null;
        if (flag && flag.complete && flag.naturalWidth > 0) {
          const rect = countryRectFor(cm, mode, orthoProject, baseR, CX3, CY3);
          if (rect) {
            ctx2d.save();
            ctx2d.clip(path2d);
            ctx2d.drawImage(flag, rect.x, rect.y, rect.w, rect.h);
            ctx2d.restore();
          }
        } else {
          ctx2d.fillStyle = theme.countries.fillFallback || "#1f1430";
          ctx2d.fill(path2d);
        }
        ctx2d.stroke(path2d);
      }
      if (state.data.contestedRegions && state.layers.enabled.has("contested")) {
        ctx2d.save();
        ctx2d.lineWidth = theme.contested.strokeWidth || 1;
        ctx2d.fillStyle = theme.contested.fill;
        ctx2d.strokeStyle = theme.contested.stroke;
        if (theme.contested.strokeDasharray) {
          const dash = String(theme.contested.strokeDasharray).split(/\s+/).map(Number);
          ctx2d.setLineDash(dash);
        }
        for (const r of state.data.contestedRegions) {
          const path = buildRingPath2D(r.points, mode, orthoProject, baseR, CX3, CY3);
          if (path) {
            ctx2d.fill(path);
            ctx2d.stroke(path);
          }
        }
        ctx2d.restore();
      }
      if (state.layers.enabled.has("labels") && (!state.theme.flagsVisible || theme.countries.showsLabels)) {
        const f = theme.labels;
        ctx2d.font = (f.fontStyle || "normal") + " " + (f.fontWeight || 400) + " 16px " + (f.fontFamily || "serif");
        ctx2d.fillStyle = f.fill;
        ctx2d.strokeStyle = f.stroke || "transparent";
        ctx2d.lineWidth = f.strokeWidth || 0;
        ctx2d.textAlign = "center";
        ctx2d.textBaseline = "middle";
        for (const [iso, cm] of state.data.countries) {
          if (!isVisible(cm)) continue;
          const lbl = state.data.labels.get(iso) || null;
          const lon = lbl?.lon ?? cm.lon;
          const lat = lbl?.lat ?? cm.lat;
          const proj = projectPoint(lon, lat, mode, orthoProject, baseR, CX3, CY3);
          if (!proj) continue;
          const projH = mode === "flat" ? cm.latRange * 5.555 : cm.latRange * baseR / 57.3;
          if (projH < 10) continue;
          const sizeRatio = lbl?.sizeRatio || autoSizeRatio(cm);
          const fontSize = Math.max(5, Math.min(60, sizeRatio * projH));
          const name = cm.name || iso.toUpperCase();
          ctx2d.font = (f.fontStyle || "normal") + " " + (f.fontWeight || 400) + " " + fontSize.toFixed(1) + "px " + (f.fontFamily || "serif");
          if (lbl?.rotate) {
            ctx2d.save();
            ctx2d.translate(proj[0], proj[1]);
            ctx2d.rotate(lbl.rotate * Math.PI / 180);
            if (ctx2d.lineWidth > 0) ctx2d.strokeText(name, 0, 0);
            ctx2d.fillText(name, 0, 0);
            ctx2d.restore();
          } else {
            if (ctx2d.lineWidth > 0) ctx2d.strokeText(name, proj[0], proj[1]);
            ctx2d.fillText(name, proj[0], proj[1]);
          }
        }
      }
      if (state.layers.enabled.has("spotlights") && state.data.spotlights) {
        const sp = theme.spotlights;
        const zoom = state.nav.zoom;
        const fadeStart = 3, fadeEnd = 5.5;
        let alpha = 1;
        if (zoom > fadeStart) alpha = Math.max(0, 1 - (zoom - fadeStart) / (fadeEnd - fadeStart));
        if (alpha > 1e-3) {
          const dotR = Math.max(1.4, 3 / Math.max(0.7, Math.pow(zoom, 0.55)));
          const haloR = dotR * 3.2;
          ctx2d.globalAlpha = alpha;
          for (const s of state.data.spotlights) {
            const proj = projectPoint(s.lon, s.lat, mode, orthoProject, baseR, CX3, CY3);
            if (!proj) continue;
            ctx2d.beginPath();
            ctx2d.arc(proj[0], proj[1], haloR, 0, Math.PI * 2);
            ctx2d.strokeStyle = sp.glow;
            ctx2d.lineWidth = 1.5;
            ctx2d.globalAlpha = alpha * 0.6;
            ctx2d.stroke();
            ctx2d.globalAlpha = alpha;
            ctx2d.beginPath();
            ctx2d.arc(proj[0], proj[1], dotR, 0, Math.PI * 2);
            ctx2d.fillStyle = s.special ? sp.specialFill || sp.fill : sp.fill;
            ctx2d.fill();
            ctx2d.strokeStyle = s.special ? sp.specialBorder : sp.border;
            ctx2d.lineWidth = 1;
            ctx2d.stroke();
          }
          ctx2d.globalAlpha = 1;
        }
      }
      if (mode === "globe" && state.layers.enabled.has("atmosphere")) {
        const atmCol = theme.globe?.atmosphere?.color || "rgba(110,180,255,0.4)";
        const glowR = baseR * 1.16;
        const gradGlow = ctx2d.createRadialGradient(CX3, CY3, baseR * 0.8, CX3, CY3, glowR);
        gradGlow.addColorStop(0, "rgba(0,0,0,0)");
        gradGlow.addColorStop(0.5, atmCol);
        gradGlow.addColorStop(1, "rgba(0,0,0,0)");
        ctx2d.beginPath();
        ctx2d.arc(CX3, CY3, glowR, 0, Math.PI * 2);
        ctx2d.fillStyle = gradGlow;
        ctx2d.fill();
        const sunDx = -baseR * 0.35, sunDy = -baseR * 0.35;
        const gradShade = ctx2d.createRadialGradient(CX3 + sunDx, CY3 + sunDy, 0, CX3 + sunDx, CY3 + sunDy, baseR);
        gradShade.addColorStop(0, "rgba(255,255,255,0)");
        gradShade.addColorStop(0.55, "rgba(0,0,0,0)");
        gradShade.addColorStop(1, "rgba(0,0,0,0.55)");
        ctx2d.save();
        ctx2d.beginPath();
        ctx2d.arc(CX3, CY3, baseR, 0, Math.PI * 2);
        ctx2d.clip();
        ctx2d.fillStyle = gradShade;
        ctx2d.fillRect(0, 0, VB_W2, VB_H2);
        ctx2d.restore();
      }
      ctx2d.restore();
      paintIdBuffer(orthoProject, baseR, CX3, CY3, mode);
    }
    function paintIdBuffer(orthoProject, baseR, CX3, CY3, mode) {
      const w = hitCanvas.width, h = hitCanvas.height;
      const scale = Math.min(w / VB_W2, h / VB_H2);
      const ox = (w - VB_W2 * scale) / 2, oy = (h - VB_H2 * scale) / 2;
      hitCtx.save();
      hitCtx.clearRect(0, 0, w, h);
      hitCtx.translate(ox, oy);
      hitCtx.scale(scale, scale);
      if (mode === "flat") {
        const z = state.nav.zoom;
        const [vbx, vby] = project(state.nav.lon, state.nav.lat);
        const w2 = VB_W2 / z, h2 = VB_H2 / z;
        const cx = Math.max(w2 / 2, Math.min(VB_W2 - w2 / 2, vbx));
        const cy = Math.max(h2 / 2, Math.min(VB_H2 - h2 / 2, vby));
        hitCtx.scale(z, z);
        hitCtx.translate(-(cx - w2 / 2), -(cy - h2 / 2));
      }
      for (const [iso, geom] of geomByIso) {
        const cm = state.data.countries.get(iso);
        if (!cm) continue;
        if (mode === "globe") {
          const phi = cm.lat * Math.PI / 180;
          const lam = cm.lon * Math.PI / 180;
          const rotPhiRad = state.nav.lat * Math.PI / 180;
          const rotLambdaRad = state.nav.lon * Math.PI / 180;
          const cosDist = Math.sin(phi) * Math.sin(rotPhiRad) + Math.cos(phi) * Math.cos(rotPhiRad) * Math.cos(lam - rotLambdaRad);
          if (cosDist < -0.3) continue;
        }
        const path = buildCountryPath2D(iso, geom, mode, orthoProject, baseR, CX3, CY3);
        if (!path) continue;
        const [r, g, b] = idColors.get(iso);
        hitCtx.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
        hitCtx.fill(path);
      }
      hitCtx.restore();
    }
    function hitTest(clientX, clientY) {
      if (!canvas || !hitCtx) return null;
      const rect = canvas.getBoundingClientRect();
      const x = Math.floor((clientX - rect.left) * (hitCanvas.width / rect.width));
      const y = Math.floor((clientY - rect.top) * (hitCanvas.height / rect.height));
      if (x < 0 || y < 0 || x >= hitCanvas.width || y >= hitCanvas.height) return null;
      const px = hitCtx.getImageData(x, y, 1, 1).data;
      const key = px[0] + "," + px[1] + "," + px[2];
      const iso = colorToIso.get(key);
      return iso ? { iso } : null;
    }
    function buildCountryPath2D(iso, geom, mode, orthoProject, baseR, CX3, CY3) {
      const polys = geom.type === "Polygon" ? [geom.arcs] : geom.type === "MultiPolygon" ? geom.arcs : null;
      if (!polys) return null;
      let dStr = "";
      for (const poly of polys) {
        for (const ring of poly) {
          const pts = ringFromArcs(ring, arcs);
          if (pts.length < 3) continue;
          if (mode === "flat") {
            const proj = pts.map(([lon, lat]) => project(lon, lat));
            dStr += splitOnAntimeridian(proj, 1e3);
          } else {
            const projected = pts.map(([lon, lat]) => orthoProject(lon, lat));
            dStr += clipToHorizon(projected, (p) => [CX3 + p.x * baseR, CY3 - p.y * baseR]);
          }
        }
      }
      if (!dStr) return null;
      return new Path2D(dStr);
    }
    function buildRingPath2D(points, mode, orthoProject, baseR, CX3, CY3) {
      if (!points || points.length < 3) return null;
      let dStr = "";
      if (mode === "flat") {
        const proj = points.map(([lon, lat]) => project(lon, lat));
        dStr = splitOnAntimeridian(proj, 1e3);
      } else {
        const projected = points.map(([lon, lat]) => orthoProject(lon, lat));
        dStr = clipToHorizon(projected, (p) => [CX3 + p.x * baseR, CY3 - p.y * baseR]);
      }
      return dStr ? new Path2D(dStr) : null;
    }
    function countryRectFor(cm, mode, orthoProject, baseR, CX3, CY3) {
      const bMinLon = cm.mainLonMin ?? cm.lonMin;
      const bMaxLon = cm.mainLonMax ?? cm.lonMax;
      const bMinLat = cm.mainLatMin ?? cm.latMin;
      const bMaxLat = cm.mainLatMax ?? cm.latMax;
      const bLonRange = cm.mainLonRange ?? cm.lonRange;
      const bLatRange = cm.mainLatRange ?? cm.latRange;
      if (mode === "flat") {
        const ul = project(bMinLon, bMaxLat);
        const lr = project(bMaxLon, bMinLat);
        return { x: ul[0], y: ul[1], w: lr[0] - ul[0], h: lr[1] - ul[1] };
      }
      const pc = orthoProject(cm.lon, cm.lat);
      if (pc.z < -0.3) return null;
      const cosLat = Math.cos(cm.lat * Math.PI / 180);
      const w = 1.2 * Math.max(8, bLonRange * cosLat) * baseR / 57.3;
      const h = 1.2 * Math.max(8, bLatRange) * baseR / 57.3;
      const cx = CX3 + pc.x * baseR, cy = CY3 - pc.y * baseR;
      return { x: cx - w / 2, y: cy - h / 2, w, h };
    }
    function projectPoint(lon, lat, mode, orthoProject, baseR, CX3, CY3) {
      if (mode === "flat") {
        const [x, y] = project(lon, lat);
        return [x, y];
      }
      const p = orthoProject(lon, lat);
      if (p.z < 0) return null;
      return [CX3 + p.x * baseR, CY3 - p.y * baseR];
    }
    function autoSizeRatio(cm) {
      const aspect = (cm.lonRange || 30) / (cm.latRange || 30);
      if (aspect > 3) return 0.18;
      if (aspect < 0.4) return 0.14;
      return 0.22;
    }
    function worldToScreen({ lon, lat }) {
      const rect = canvas.getBoundingClientRect();
      let vbx, vby;
      if (state.view.mode === "flat") {
        [vbx, vby] = project(lon, lat);
        const z = state.nav.zoom;
        const [cvbx, cvby] = project(state.nav.lon, state.nav.lat);
        const w2 = VB_W2 / z, h2 = VB_H2 / z;
        const cx = Math.max(w2 / 2, Math.min(VB_W2 - w2 / 2, cvbx));
        const cy = Math.max(h2 / 2, Math.min(VB_H2 - h2 / 2, cvby));
        const left = cx - w2 / 2, top = cy - h2 / 2;
        const scale2 = Math.min(rect.width / w2, rect.height / h2);
        const ox2 = (rect.width - w2 * scale2) / 2, oy2 = (rect.height - h2 * scale2) / 2;
        return { x: ox2 + (vbx - left) * scale2, y: oy2 + (vby - top) * scale2 };
      }
      const p = project2(lon, lat, { rotLon: state.nav.lon, rotLat: state.nav.lat, zoom: state.nav.zoom });
      const scale = Math.min(rect.width / VB_W2, rect.height / VB_H2);
      const ox = (rect.width - VB_W2 * scale) / 2, oy = (rect.height - VB_H2 * scale) / 2;
      return { x: ox + p.vbx * scale, y: oy + p.vby * scale };
    }
    function screenToWorld({ x, y }) {
      const rect = canvas.getBoundingClientRect();
      if (state.view.mode === "flat") {
        const z = state.nav.zoom;
        const [cvbx, cvby] = project(state.nav.lon, state.nav.lat);
        const w2 = VB_W2 / z, h2 = VB_H2 / z;
        const cx = Math.max(w2 / 2, Math.min(VB_W2 - w2 / 2, cvbx));
        const cy = Math.max(h2 / 2, Math.min(VB_H2 - h2 / 2, cvby));
        const left = cx - w2 / 2, top = cy - h2 / 2;
        const scale2 = Math.min(rect.width / w2, rect.height / h2);
        const ox2 = (rect.width - w2 * scale2) / 2, oy2 = (rect.height - h2 * scale2) / 2;
        const vbx2 = left + (x - ox2) / scale2;
        const vby2 = top + (y - oy2) / scale2;
        return unproject(vbx2, vby2);
      }
      const scale = Math.min(rect.width / VB_W2, rect.height / VB_H2);
      const ox = (rect.width - VB_W2 * scale) / 2, oy = (rect.height - VB_H2 * scale) / 2;
      const vbx = (x - ox) / scale, vby = (y - oy) / scale;
      return unproject2(vbx, vby, { rotLon: state.nav.lon, rotLat: state.nav.lat, zoom: state.nav.zoom });
    }
    function viewportToWorld(dx, dy) {
      const c = worldToScreen({ lon: state.nav.lon, lat: state.nav.lat });
      const t = screenToWorld({ x: c.x + dx, y: c.y + dy });
      return { dLon: t.lon - state.nav.lon, dLat: t.lat - state.nav.lat };
    }
    function getViewport() {
      return { lonMin: -180, lonMax: 180, latMin: -90, latMax: 90 };
    }
    function destroy() {
      window.removeEventListener("resize", resize);
      if (canvas && canvas.parentNode) canvas.parentNode.removeChild(canvas);
      canvas = null;
      ctx2d = null;
      hitCanvas = null;
      hitCtx = null;
      flagImages.clear();
      idColors.clear();
      colorToIso.clear();
    }
    function applyTheme() {
      requestPatch();
    }
    return {
      init: (t) => Promise.resolve(init(t)),
      patch,
      destroy,
      hitTest,
      worldToScreen,
      screenToWorld,
      viewportToWorld,
      getViewport,
      applyTheme,
      getCanvas: () => canvas
    };
  }
  registerRenderer("canvas", (args) => createCanvasRenderer(args));

  // map/src/renderers/webgl.js
  var POLY_VERT_TARGET = 24;
  var VERTEX_SHADER = `#version 300 es
precision highp float;

in vec2 aLonLat;       // (lon\xB0, lat\xB0)
in vec2 aUV;           // flag-local (u, v) in [0..1]

out vec2 vUV;
out float vDepth;      // z component for back-hemisphere culling (globe)

uniform float uMode;             // 0 = flat (EE), 1 = globe (ortho)
uniform vec2  uRotation;         // (rotLonRad, rotLatRad)
uniform float uBaseR;             // 450 * zoom (viewBox units)
uniform vec2  uViewCenter;       // (CX, CY) \u2014 viewBox center
uniform vec4  uViewBox;          // (vbx, vby, vbw, vbh) \u2014 current viewBox

const float PI = 3.14159265358979323846;
const float DEG = PI / 180.0;

// === Equal Earth projection ==================================================
vec2 projectFlat(vec2 lonlat) {
  const float A1 = 1.340264;
  const float A2 = -0.081106;
  const float A3 = 0.000893;
  const float A4 = 0.003796;
  const float M  = 0.86602540378443864676;  // sqrt(3) / 2
  const float X_HALF = 2.7063831;
  const float Y_HALF = 1.3173627;
  const float SCALE  = min(1000.0 / X_HALF, 500.0 / Y_HALF);
  float lonR = lonlat.x * DEG;
  float latR = lonlat.y * DEG;
  float th = asin(M * sin(latR));
  float t2 = th * th;
  float t6 = t2 * t2 * t2;
  float Mp = A1 + 3.0 * A2 * t2 + t6 * (7.0 * A3 + 9.0 * A4 * t2);
  float Mt = A1 + A2 * t2 + t6 * (A3 + A4 * t2);
  float xn = 2.0 * sqrt(3.0) * lonR * cos(th) / (3.0 * Mp);
  float yn = Mt * th;
  return vec2(1000.0 + xn * SCALE, 500.0 - yn * SCALE);
}

// === Orthographic projection =================================================
vec3 projectGlobe(vec2 lonlat) {
  float lonR = lonlat.x * DEG;
  float latR = lonlat.y * DEG;
  float dLam = lonR - uRotation.x;
  float cosLam = cos(dLam);
  float sinLam = sin(dLam);
  float cosPhi = cos(latR);
  float sinPhi = sin(latR);
  float sinPhi0 = sin(uRotation.y);
  float cosPhi0 = cos(uRotation.y);
  float x = cosPhi * sinLam;
  float y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosLam;
  float z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosLam;
  return vec3(uViewCenter.x + x * uBaseR, uViewCenter.y - y * uBaseR, z);
}

void main() {
  vec2 vb;
  float z;
  if (uMode > 0.5) {
    vec3 p = projectGlobe(aLonLat);
    vb = p.xy;
    z = p.z;
  } else {
    vb = projectFlat(aLonLat);
    z = 1.0;
  }
  // viewBox (vbx, vby, vbw, vbh) \u2192 clip space [-1, +1].
  float clipX = ((vb.x - uViewBox.x) / uViewBox.z) * 2.0 - 1.0;
  float clipY = 1.0 - ((vb.y - uViewBox.y) / uViewBox.w) * 2.0;
  // Push back-hemisphere vertices off-screen (z < -0.05) by setting clip z
  // beyond the [-1, +1] range \u2014 they get clipped by the GPU.
  gl_Position = vec4(clipX, clipY, 0.0, 1.0);
  vUV = aUV;
  vDepth = z;
}
`;
  var FRAGMENT_SHADER = `#version 300 es
precision highp float;
in vec2 vUV;
in float vDepth;
out vec4 fragColor;
uniform sampler2D uFlag;
void main() {
  if (vDepth < -0.05) discard;             // back hemisphere
  fragColor = texture(uFlag, vUV);
}
`;
  function createWebGLRenderer({ state, events, theme, layers: layers2 } = {}) {
    let canvas = null;
    let target = null;
    let gl = null;
    let program = null;
    let attrLoc = null;
    let uniLoc = null;
    let oceanSvg = null;
    let oceanRect = null;
    let oceanCircle = null;
    const meshes = /* @__PURE__ */ new Map();
    let solidTex = null;
    let _resizeBound = false;
    function compileShader2(type, src) {
      const sh = gl.createShader(type);
      gl.shaderSource(sh, src);
      gl.compileShader(sh);
      if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
        const log = gl.getShaderInfoLog(sh);
        gl.deleteShader(sh);
        throw new Error("[wuys/map] WebGL shader compile failed: " + log);
      }
      return sh;
    }
    function linkProgram2() {
      const vs = compileShader2(gl.VERTEX_SHADER, VERTEX_SHADER);
      const fs = compileShader2(gl.FRAGMENT_SHADER, FRAGMENT_SHADER);
      const p = gl.createProgram();
      gl.attachShader(p, vs);
      gl.attachShader(p, fs);
      gl.linkProgram(p);
      if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
        const log = gl.getProgramInfoLog(p);
        gl.deleteProgram(p);
        throw new Error("[wuys/map] WebGL program link failed: " + log);
      }
      return p;
    }
    const TEX_TIMEOUT_MS = 5e3;
    async function loadTexture(src) {
      const timer = new Promise((_, reject) => setTimeout(() => reject(new Error("[wuys/map] flag timed out: " + src.slice(0, 64))), TEX_TIMEOUT_MS));
      const load = (async () => {
        const res = await fetch(src);
        if (!res.ok) throw new Error("HTTP " + res.status + " for " + src.slice(0, 64));
        const blob = await res.blob();
        const bitmap = await createImageBitmap(blob);
        const tex = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        bitmap.close && bitmap.close();
        return tex;
      })();
      return Promise.race([load, timer]);
    }
    function buildMesh(cm) {
      if (!cm || !cm.polys || cm.polys.length === 0) return null;
      const flatVerts = [];
      for (const poly of cm.polys) {
        if (!poly.points || poly.points.length < 3) continue;
        const lonR = poly.lonRange;
        const latR = poly.latRange;
        if (lonR <= 0 || latR <= 0) continue;
        const ring = simplifyToCount(poly.points, POLY_VERT_TARGET);
        const triangles = earClip(ring);
        for (const tri of triangles) {
          for (let i = 0; i < 3; i++) {
            const lon = tri[i][0];
            const lat = tri[i][1];
            const u = (lon - poly.lonMin) / lonR;
            const v = (poly.latMax - lat) / latR;
            flatVerts.push(lon, lat, u, v);
          }
        }
      }
      if (flatVerts.length === 0) return null;
      const vbo = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(flatVerts), gl.STATIC_DRAW);
      return {
        vbo,
        vertexCount: flatVerts.length / 4,
        lonMin: cm.lonMin,
        lonMax: cm.lonMax,
        latMin: cm.latMin,
        latMax: cm.latMax,
        lon: cm.lon,
        lat: cm.lat,
        flagTexture: null
      };
    }
    function sizeCanvas() {
      if (!canvas || !target) return;
      let rw = canvas.clientWidth, rh = canvas.clientHeight;
      if (!rw || !rh) {
        const r = target.getBoundingClientRect();
        rw = r.width;
        rh = r.height;
      }
      if (!rw || !rh) {
        rw = 1280;
        rh = 720;
      }
      const dpr = window.devicePixelRatio || 1;
      const W = Math.max(1, Math.floor(rw * dpr));
      const H = Math.max(1, Math.floor(rh * dpr));
      if (canvas.width !== W || canvas.height !== H) {
        canvas.width = W;
        canvas.height = H;
      }
    }
    function viewportForViewBox(vbw, vbh) {
      const cw = canvas.width, ch = canvas.height;
      const scale = Math.min(cw / vbw, ch / vbh);
      const w = vbw * scale;
      const h = vbh * scale;
      return { x: Math.floor((cw - w) / 2), y: Math.floor((ch - h) / 2), w: Math.floor(w), h: Math.floor(h) };
    }
    async function init(targetEl) {
      target = targetEl;
      const cs = getComputedStyle(target);
      if (cs.position === "static") target.style.position = "relative";
      const SVG_NS8 = "http://www.w3.org/2000/svg";
      oceanSvg = document.createElementNS(SVG_NS8, "svg");
      oceanSvg.setAttribute("viewBox", "0 0 2000 1000");
      oceanSvg.setAttribute("preserveAspectRatio", "xMidYMid meet");
      oceanSvg.style.cssText = "position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;";
      oceanRect = document.createElementNS(SVG_NS8, "rect");
      oceanRect.setAttribute("x", "0");
      oceanRect.setAttribute("y", "0");
      oceanRect.setAttribute("width", "2000");
      oceanRect.setAttribute("height", "1000");
      oceanRect.setAttribute("fill", theme.ocean && theme.ocean.fill || "#88a3a8");
      oceanSvg.appendChild(oceanRect);
      oceanCircle = document.createElementNS(SVG_NS8, "circle");
      oceanCircle.setAttribute("cx", "1000");
      oceanCircle.setAttribute("cy", "500");
      oceanCircle.setAttribute("r", "450");
      oceanCircle.setAttribute("fill", theme.ocean && theme.ocean.fill || "#88a3a8");
      oceanCircle.setAttribute("display", "none");
      oceanSvg.appendChild(oceanCircle);
      target.appendChild(oceanSvg);
      canvas = document.createElement("canvas");
      canvas.style.cssText = "position:absolute;inset:0;width:100%;height:100%;display:block;background:transparent;";
      target.appendChild(canvas);
      gl = canvas.getContext("webgl2", { antialias: true, alpha: true, premultipliedAlpha: false });
      if (!gl) {
        if (oceanSvg && oceanSvg.parentNode) oceanSvg.parentNode.removeChild(oceanSvg);
        if (canvas && canvas.parentNode) canvas.parentNode.removeChild(canvas);
        oceanSvg = null;
        canvas = null;
        throw new Error("[wuys/map] WebGL2 context unavailable");
      }
      try {
        program = linkProgram2();
        attrLoc = {
          aLonLat: gl.getAttribLocation(program, "aLonLat"),
          aUV: gl.getAttribLocation(program, "aUV")
        };
        uniLoc = {
          uMode: gl.getUniformLocation(program, "uMode"),
          uRotation: gl.getUniformLocation(program, "uRotation"),
          uBaseR: gl.getUniformLocation(program, "uBaseR"),
          uViewCenter: gl.getUniformLocation(program, "uViewCenter"),
          uViewBox: gl.getUniformLocation(program, "uViewBox"),
          uFlag: gl.getUniformLocation(program, "uFlag")
        };
        sizeCanvas();
        gl.clearColor(0, 0, 0, 0);
        gl.disable(gl.DEPTH_TEST);
        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        solidTex = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, solidTex);
        const f = theme.countries && theme.countries.fillFallback || "#1f1430";
        const rgb = parseHex(f);
        gl.texImage2D(
          gl.TEXTURE_2D,
          0,
          gl.RGBA,
          1,
          1,
          0,
          gl.RGBA,
          gl.UNSIGNED_BYTE,
          new Uint8Array([rgb[0], rgb[1], rgb[2], 255])
        );
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        const flags = state.data.flags;
        const texturePromises = [];
        let loadedFlags = 0;
        let failedFlags = 0;
        const firstFailures = [];
        for (const [iso, cm] of state.data.countries) {
          const mesh = buildMesh(cm);
          if (!mesh) continue;
          meshes.set(iso, mesh);
          const flagSrc = flags && flags.get ? flags.get(iso) : null;
          if (flagSrc) {
            texturePromises.push(
              loadTexture(flagSrc).then((tex) => {
                mesh.flagTexture = tex;
                loadedFlags++;
              }).catch((err) => {
                failedFlags++;
                if (firstFailures.length < 3) {
                  firstFailures.push({ iso, src: String(flagSrc).slice(0, 80), error: err && err.message });
                }
              })
            );
          }
        }
        await Promise.all(texturePromises);
        if (failedFlags > 0) {
          console.warn("[wuys/map/webgl] " + failedFlags + " of " + (loadedFlags + failedFlags) + " flag textures failed. Sample failures:");
          for (const f2 of firstFailures) console.warn("  ", f2);
        }
        if (loadedFlags === 0 && failedFlags > 0) {
          throw new Error("[wuys/map/webgl] all flag textures failed; aborting init so SVG fallback takes over");
        }
        if (!_resizeBound) {
          _resizeBound = true;
          window.addEventListener("resize", () => {
            sizeCanvas();
            patch();
          });
        }
      } catch (e) {
        if (canvas && canvas.parentNode) canvas.parentNode.removeChild(canvas);
        canvas = null;
        gl = null;
        throw e;
      }
    }
    function patch() {
      if (!gl || !program) return;
      if (!Number.isFinite(state.nav.lon)) {
        state.nav.lon = 0;
        state.nav.tLon = 0;
      }
      if (!Number.isFinite(state.nav.lat)) {
        state.nav.lat = 0;
        state.nav.tLat = 0;
      }
      if (!Number.isFinite(state.nav.zoom) || state.nav.zoom <= 0) {
        state.nav.zoom = 1;
        state.nav.tZoom = 1;
      }
      sizeCanvas();
      const mode = state.view.mode;
      const rotLonRad = state.nav.lon * Math.PI / 180;
      const rotLatRad = state.nav.lat * Math.PI / 180;
      const zoom = state.nav.zoom;
      const baseR = 450 * zoom;
      let vbx, vby, vbw, vbh;
      if (mode === "globe") {
        vbx = 0;
        vby = 0;
        vbw = 2e3;
        vbh = 1e3;
      } else {
        const r = target.getBoundingClientRect();
        const aspect = r.width && r.height ? r.width / r.height : 2;
        let viewW, viewH, offX, offY;
        if (aspect > 2) {
          viewW = 1e3 * aspect;
          viewH = 1e3;
          offX = (viewW - 2e3) / 2;
          offY = 0;
        } else {
          viewW = 2e3;
          viewH = 2e3 / aspect;
          offX = 0;
          offY = (viewH - 1e3) / 2;
        }
        const w = viewW / zoom;
        const h = viewH / zoom;
        const [px, py] = project(state.nav.lon, state.nav.lat);
        const cx = Math.max(w / 2 - offX, Math.min(2e3 + offX - w / 2, px));
        const cy = Math.max(h / 2 - offY, Math.min(1e3 + offY - h / 2, py));
        vbx = cx - w / 2;
        vby = cy - h / 2;
        vbw = w;
        vbh = h;
      }
      if (oceanRect && oceanCircle) {
        if (mode === "globe") {
          oceanRect.setAttribute("display", "none");
          oceanCircle.removeAttribute("display");
          oceanCircle.setAttribute("r", String(baseR));
        } else {
          oceanCircle.setAttribute("display", "none");
          oceanRect.removeAttribute("display");
        }
        if (oceanSvg) oceanSvg.setAttribute("viewBox", vbx + " " + vby + " " + vbw + " " + vbh);
      }
      const vp = viewportForViewBox(vbw, vbh);
      gl.viewport(vp.x, vp.y, vp.w, vp.h);
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.useProgram(program);
      gl.uniform1f(uniLoc.uMode, mode === "globe" ? 1 : 0);
      gl.uniform2f(uniLoc.uRotation, rotLonRad, rotLatRad);
      gl.uniform1f(uniLoc.uBaseR, baseR);
      gl.uniform2f(uniLoc.uViewCenter, 1e3, 500);
      gl.uniform4f(uniLoc.uViewBox, vbx, vby, vbw, vbh);
      gl.uniform1i(uniLoc.uFlag, 0);
      gl.activeTexture(gl.TEXTURE0);
      const showFlags = state.theme.flagsVisible === true;
      const sinRotPhi = Math.sin(rotLatRad);
      const cosRotPhi = Math.cos(rotLatRad);
      for (const [, mesh] of meshes) {
        if (mode === "globe") {
          const phi = mesh.lat * Math.PI / 180;
          const lam = mesh.lon * Math.PI / 180;
          const cosDist = Math.sin(phi) * sinRotPhi + Math.cos(phi) * cosRotPhi * Math.cos(lam - rotLonRad);
          if (cosDist < -0.3) continue;
        }
        const tex = showFlags && mesh.flagTexture ? mesh.flagTexture : solidTex;
        if (!tex) continue;
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.bindBuffer(gl.ARRAY_BUFFER, mesh.vbo);
        gl.enableVertexAttribArray(attrLoc.aLonLat);
        gl.vertexAttribPointer(attrLoc.aLonLat, 2, gl.FLOAT, false, 16, 0);
        gl.enableVertexAttribArray(attrLoc.aUV);
        gl.vertexAttribPointer(attrLoc.aUV, 2, gl.FLOAT, false, 16, 8);
        gl.drawArrays(gl.TRIANGLES, 0, mesh.vertexCount);
      }
    }
    function parseHex(hex) {
      const s = String(hex).replace("#", "");
      if (s.length === 3) {
        return [parseInt(s[0] + s[0], 16), parseInt(s[1] + s[1], 16), parseInt(s[2] + s[2], 16)];
      }
      return [parseInt(s.slice(0, 2), 16), parseInt(s.slice(2, 4), 16), parseInt(s.slice(4, 6), 16)];
    }
    function destroy() {
      if (gl) {
        for (const [, mesh] of meshes) {
          if (mesh.vbo) gl.deleteBuffer(mesh.vbo);
          if (mesh.flagTexture) gl.deleteTexture(mesh.flagTexture);
        }
        if (solidTex) gl.deleteTexture(solidTex);
        if (program) gl.deleteProgram(program);
      }
      meshes.clear();
      solidTex = null;
      if (canvas && canvas.parentNode) canvas.parentNode.removeChild(canvas);
      if (oceanSvg && oceanSvg.parentNode) oceanSvg.parentNode.removeChild(oceanSvg);
      canvas = null;
      gl = null;
      program = null;
      oceanSvg = null;
      oceanRect = null;
      oceanCircle = null;
    }
    function worldToScreen({ lon, lat }) {
      const mode = state.view.mode;
      const rect = canvas ? canvas.getBoundingClientRect() : { width: 0, height: 0 };
      let vb;
      if (mode === "flat") {
        const [x, y] = project(lon, lat);
        vb = { x, y };
      } else {
        const p = project2(lon, lat, { rotLon: state.nav.lon, rotLat: state.nav.lat, zoom: state.nav.zoom });
        vb = { x: p.vbx, y: p.vby };
      }
      return { x: vb.x / 2e3 * rect.width, y: vb.y / 1e3 * rect.height };
    }
    function screenToWorld({ x, y }) {
      const mode = state.view.mode;
      const rect = canvas ? canvas.getBoundingClientRect() : { width: 1, height: 1 };
      const vbx = x / rect.width * 2e3;
      const vby = y / rect.height * 1e3;
      if (mode === "flat") return unproject(vbx, vby);
      return unproject2(vbx, vby, { rotLon: state.nav.lon, rotLat: state.nav.lat, zoom: state.nav.zoom });
    }
    function viewportToWorld(dxPx, dyPx) {
      const c = worldToScreen({ lon: state.nav.lon, lat: state.nav.lat });
      const t = screenToWorld({ x: c.x + dxPx, y: c.y + dyPx });
      return { dLon: t.lon - state.nav.lon, dLat: t.lat - state.nav.lat };
    }
    function getViewport() {
      const mode = state.view.mode;
      if (mode === "flat") {
        const span = 180 / state.nav.zoom;
        return {
          lonMin: state.nav.lon - span,
          lonMax: state.nav.lon + span,
          latMin: state.nav.lat - span / 2,
          latMax: state.nav.lat + span / 2
        };
      }
      return { lonMin: state.nav.lon - 90, lonMax: state.nav.lon + 90, latMin: state.nav.lat - 90, latMax: state.nav.lat + 90 };
    }
    function hitTest() {
      return null;
    }
    function applyTheme() {
      if (target && theme.background) target.style.background = theme.background;
      if (oceanRect && theme.ocean && theme.ocean.fill) oceanRect.setAttribute("fill", theme.ocean.fill);
      if (oceanCircle && theme.ocean && theme.ocean.fill) oceanCircle.setAttribute("fill", theme.ocean.fill);
      if (gl && solidTex) {
        const f = theme.countries && theme.countries.fillFallback || "#1f1430";
        const rgb = parseHex(f);
        gl.bindTexture(gl.TEXTURE_2D, solidTex);
        gl.texImage2D(
          gl.TEXTURE_2D,
          0,
          gl.RGBA,
          1,
          1,
          0,
          gl.RGBA,
          gl.UNSIGNED_BYTE,
          new Uint8Array([rgb[0], rgb[1], rgb[2], 255])
        );
      }
    }
    return {
      init: (...a) => Promise.resolve(init(...a)),
      patch,
      destroy,
      hitTest,
      worldToScreen,
      screenToWorld,
      viewportToWorld,
      getViewport,
      applyTheme,
      getCanvas: () => canvas
    };
  }
  registerRenderer("webgl", (args) => createWebGLRenderer(args));

  // map/src/layers/countries.js
  var SVG_NS2 = "http://www.w3.org/2000/svg";
  function createCountriesLayer(opts = {}) {
    const id = opts.id || "countries";
    const els = /* @__PURE__ */ new Map();
    let group = null;
    let api = null;
    function build(ctx) {
      const { svg, state } = ctx;
      group = document.createElementNS(SVG_NS2, "g");
      group.setAttribute("id", "layer-" + id);
      svg.appendChild(group);
      for (const iso of state.data.countries.keys()) {
        const p = document.createElementNS(SVG_NS2, "path");
        p.setAttribute("data-iso", iso);
        p.setAttribute("data-name", state.data.countries.get(iso).name || iso);
        p.setAttribute("d", "M0,0");
        p.setAttribute("display", "none");
        group.appendChild(p);
        els.set(iso, p);
      }
      applyTheme(ctx);
    }
    function patch(ctx) {
      const { state, pathForCountry, isVisibleAt } = ctx;
      for (const [iso, el2] of els) {
        if (!isVisibleAt(iso)) {
          el2.setAttribute("display", "none");
          continue;
        }
        const d = pathForCountry(iso);
        if (!d) {
          el2.setAttribute("display", "none");
          continue;
        }
        el2.removeAttribute("display");
        el2.setAttribute("d", d);
      }
    }
    function applyTheme(ctx) {
      if (!group) return;
      const theme = ctx.theme;
      const c = theme.countries;
      group.style.setProperty("--country-stroke", c.stroke);
      group.style.setProperty("--country-stroke-width", c.strokeWidth);
      for (const [iso, el2] of els) {
        el2.setAttribute("fill", c.fillFallback || "#1f1430");
        el2.setAttribute("stroke", c.stroke);
        el2.setAttribute("stroke-width", c.strokeWidth);
        el2.setAttribute("vector-effect", "non-scaling-stroke");
      }
    }
    function setFill(iso, fill) {
      const el2 = els.get(iso);
      if (el2) el2.setAttribute("fill", fill);
    }
    function destroy() {
      if (group && group.parentNode) group.parentNode.removeChild(group);
      els.clear();
      group = null;
    }
    api = { id, build, patch, applyTheme, setFill, destroy, getElement: (iso) => els.get(iso) };
    return api;
  }

  // map/src/layers/labels.js
  var SVG_NS3 = "http://www.w3.org/2000/svg";
  function createLabelsLayer(opts = {}) {
    const texts = /* @__PURE__ */ new Map();
    let group = null;
    const getName = opts.getCountryName || ((iso, fallback) => fallback || iso.toUpperCase());
    function build(ctx) {
      const { svg, state } = ctx;
      group = document.createElementNS(SVG_NS3, "g");
      group.setAttribute("id", "layer-labels");
      group.setAttribute("class", "geo-country-labels");
      svg.appendChild(group);
      for (const iso of state.data.countries.keys()) {
        const t = document.createElementNS(SVG_NS3, "text");
        t.setAttribute("data-iso-text", iso);
        t.setAttribute("text-anchor", "middle");
        t.setAttribute("dominant-baseline", "middle");
        t.setAttribute("pointer-events", "none");
        t.setAttribute("display", "none");
        const cm = state.data.countries.get(iso);
        const name = getName(iso, cm.name);
        t.textContent = name;
        group.appendChild(t);
        texts.set(iso, t);
      }
      applyTheme(ctx);
    }
    function patch(ctx) {
      const { state, theme, screenForLabel, projectedHeightOf, isVisibleAt } = ctx;
      const showLabels = state.theme.labelsVisible === true;
      if (!showLabels) {
        if (group && group.style.display !== "none") group.style.display = "none";
        return;
      }
      if (group && group.style.display === "none") group.style.display = "";
      for (const [iso, t] of texts) {
        if (!isVisibleAt(iso)) {
          t.setAttribute("display", "none");
          continue;
        }
        const cm = state.data.countries.get(iso);
        const lbl = state.data.labels.get(iso) || null;
        const pos = screenForLabel(iso, lbl);
        if (!pos) {
          t.setAttribute("display", "none");
          continue;
        }
        const projH = projectedHeightOf(iso);
        if (!projH || projH < 10) {
          t.setAttribute("display", "none");
          continue;
        }
        const sizeRatio = lbl && lbl.sizeRatio || autoSizeRatio(cm, projH);
        const fontSize = Math.max(5, Math.min(60, sizeRatio * projH));
        t.removeAttribute("display");
        t.setAttribute("x", pos.x.toFixed(2));
        t.setAttribute("y", pos.y.toFixed(2));
        t.setAttribute("font-size", fontSize.toFixed(1));
        const rot = lbl && lbl.rotate || 0;
        if (rot) t.setAttribute("transform", "rotate(" + rot + " " + pos.x.toFixed(2) + " " + pos.y.toFixed(2) + ")");
        else t.removeAttribute("transform");
      }
    }
    function autoSizeRatio(cm, projH) {
      const aspect = (cm.lonRange || 30) / (cm.latRange || 30);
      if (aspect > 3) return 0.18;
      if (aspect < 0.4) return 0.14;
      return 0.22;
    }
    function applyTheme(ctx) {
      if (!group) return;
      const t = ctx.theme.labels;
      group.style.setProperty("--label-fill", t.fill);
      group.style.setProperty("--label-stroke", t.stroke || "transparent");
      group.style.setProperty("--label-stroke-width", t.strokeWidth || 0);
      group.style.setProperty("--label-font-family", t.fontFamily);
      group.style.setProperty("--label-font-style", t.fontStyle || "normal");
      group.style.setProperty("--label-font-weight", t.fontWeight || 400);
      group.style.setProperty("--label-letter-spacing", t.letterSpacing || "normal");
    }
    function destroy() {
      if (group && group.parentNode) group.parentNode.removeChild(group);
      texts.clear();
      group = null;
    }
    return { id: "labels", build, patch, applyTheme, destroy };
  }

  // map/src/data/bake.js
  var EE_W = 6144;
  var EE_H = 3072;
  var EQ_W = 6144;
  var EQ_H = 3072;
  var EE_SX = EE_W / 2e3;
  var EE_SY = EE_H / 1e3;
  function projectEE(lon, lat) {
    const [vbx, vby] = project(lon, lat);
    return [vbx * EE_SX, vby * EE_SY];
  }
  function projectEQ(lon, lat) {
    const x = (lon + 180) * (EQ_W / 360);
    const y = (90 - lat) * (EQ_H / 180);
    return [x, y];
  }
  function projectAndSplit(ring, project3, halfW) {
    const subs = [];
    let cur = [];
    let prevX = null;
    for (let i = 0; i < ring.length; i++) {
      const [x, y] = project3(ring[i][0], ring[i][1]);
      if (prevX !== null && Math.abs(x - prevX) > halfW) {
        if (cur.length > 2) subs.push(cur);
        cur = [];
      }
      cur.push([x, y]);
      prevX = x;
    }
    if (cur.length > 2) subs.push(cur);
    return subs;
  }
  function loadAllFlagImages(flagsMap) {
    const images = /* @__PURE__ */ new Map();
    const promises = [];
    for (const [iso, src] of flagsMap) {
      if (!src) continue;
      promises.push(new Promise((resolve) => {
        const img = new Image();
        img.onload = () => {
          images.set(iso, img);
          resolve();
        };
        img.onerror = () => resolve();
        img.decoding = "async";
        img.src = src;
      }));
    }
    return Promise.all(promises).then(() => images);
  }
  var _bakePromise = null;
  function bakeFlagMap(countries, flagsMap, geomByIso, arcs) {
    if (_bakePromise) return _bakePromise;
    _bakePromise = _doBake(countries, flagsMap, geomByIso, arcs);
    return _bakePromise;
  }
  async function _doBake(countries, flagsMap, geomByIso, arcs) {
    const t0 = performance.now();
    const images = await loadAllFlagImages(flagsMap);
    const t1 = performance.now();
    function makeCanvas(w, h) {
      const c = document.createElement("canvas");
      c.width = w;
      c.height = h;
      const cx = c.getContext("2d");
      cx.imageSmoothingEnabled = true;
      cx.imageSmoothingQuality = "high";
      cx.clearRect(0, 0, w, h);
      return { canvas: c, cx };
    }
    const ee = makeCanvas(EE_W, EE_H);
    const eq = makeCanvas(EQ_W, EQ_H);
    let painted = 0;
    for (const [iso] of countries) {
      const flagImg = images.get(iso);
      if (!flagImg) continue;
      const geom = geomByIso && geomByIso.get && geomByIso.get(iso);
      if (!geom) continue;
      const polysIn = geom.type === "Polygon" ? [geom.arcs] : geom.type === "MultiPolygon" ? geom.arcs : null;
      if (!polysIn) continue;
      for (const polyArcs of polysIn) {
        if (!polyArcs.length) continue;
        const rings = [];
        for (let r = 0; r < polyArcs.length; r++) {
          const ring = ringFromArcs(polyArcs[r], arcs);
          if (ring.length >= 3) rings.push(ring);
        }
        if (rings.length === 0) continue;
        paintPolyOntoCanvasMultiRing(ee.cx, rings, flagImg, projectEE, EE_W / 2);
        paintPolyOntoCanvasMultiRing(eq.cx, rings, flagImg, projectEQ, EQ_W / 2);
        painted++;
      }
    }
    const t2 = performance.now();
    console.log(
      "[wuys/map/bake] images:",
      (t1 - t0).toFixed(0),
      "ms; painted",
      painted,
      "polys in",
      (t2 - t1).toFixed(0),
      "ms (total",
      (t2 - t0).toFixed(0),
      "ms)"
    );
    return { ee: ee.canvas, eq: eq.canvas };
  }
  function paintPolyOntoCanvasMultiRing(cx, rings, flagImg, project3, halfW) {
    const path = new Path2D();
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    let hasAny = false;
    const outerSubs = projectAndSplit(rings[0], project3, halfW);
    for (const sub of outerSubs) {
      if (sub.length < 3) continue;
      addSubpath(path, sub);
      hasAny = true;
      for (const [x, y] of sub) {
        if (x < minX) minX = x;
        if (x > maxX) maxX = x;
        if (y < minY) minY = y;
        if (y > maxY) maxY = y;
      }
    }
    for (let r = 1; r < rings.length; r++) {
      const subs = projectAndSplit(rings[r], project3, halfW);
      for (const sub of subs) {
        if (sub.length < 3) continue;
        addSubpath(path, sub);
      }
    }
    if (!hasAny) return;
    const w = maxX - minX, h = maxY - minY;
    if (w <= 0 || h <= 0) return;
    cx.save();
    cx.clip(path, "evenodd");
    cx.drawImage(flagImg, minX, minY, w, h);
    cx.restore();
  }
  function addSubpath(path, sub) {
    path.moveTo(sub[0][0], sub[0][1]);
    for (let i = 1; i < sub.length; i++) path.lineTo(sub[i][0], sub[i][1]);
    path.closePath();
  }

  // map/src/layers/flags.js
  var SVG_NS4 = "http://www.w3.org/2000/svg";
  var XHTML_NS = "http://www.w3.org/1999/xhtml";
  var VERTEX_SHADER2 = `#version 300 es
precision highp float;
in vec2 aPos;            // clip-space quad corners (-1..+1)
out vec2 vClipPos;
void main() {
  vClipPos = aPos;
  gl_Position = vec4(aPos, 0.0, 1.0);
}`;
  var FRAGMENT_SHADER2 = `#version 300 es
precision highp float;
in vec2 vClipPos;
out vec4 fragColor;
uniform vec2  uRotation;     // (rotLonRad, rotLatRad)
uniform float uBaseR;
uniform vec2  uViewCenter;
uniform vec4  uViewBox;      // (vbx, vby, vbw, vbh)
uniform sampler2D uTex;
const float PI = 3.14159265358979323846;
void main() {
  // 1. clip-space \u2192 viewBox-space \u2192 unit-sphere coords (px, py).
  float vbx = uViewBox.x + (vClipPos.x + 1.0) * 0.5 * uViewBox.z;
  float vby = uViewBox.y + (1.0 - vClipPos.y) * 0.5 * uViewBox.w;
  float px = (vbx - uViewCenter.x) / uBaseR;
  float py = (uViewCenter.y - vby) / uBaseR;
  float rho2 = px * px + py * py;
  if (rho2 > 1.0) discard;                  // outside the sphere disc
  float pz = sqrt(1.0 - rho2);              // front-hemisphere z

  // 2. Inverse-rotate to sphere-fixed Cartesian, then to (lat, lon).
  //    Forward (vertex side): x = cosPhi*sinLam,
  //                           y = cosPhi0*sinPhi - sinPhi0*cosPhi*cosLam,
  //                           z = sinPhi0*sinPhi + cosPhi0*cosPhi*cosLam.
  //    Inverse \u2192 lat, lon (closed-form, no iteration):
  float sinPhi0 = sin(uRotation.y);
  float cosPhi0 = cos(uRotation.y);
  float lat = asin(sinPhi0 * pz + cosPhi0 * py);
  float lon = atan(px, cosPhi0 * pz - sinPhi0 * py) + uRotation.x;
  // Wrap lon to [-PI, PI].
  if (lon >  PI) lon -= 2.0 * PI;
  if (lon < -PI) lon += 2.0 * PI;

  // 3. Equirectangular UV.
  float u = (lon + PI) / (2.0 * PI);
  float v = (PI * 0.5 - lat) / PI;
  fragColor = texture(uTex, vec2(u, v));
}`;
  function createFlagsLayer() {
    let _svg = null;
    let _state = null;
    let flatImage = null;
    let globeForeign = null;
    let globeCanvas = null;
    let gl = null;
    let program = null;
    let attrLoc = null, uniLoc = null;
    let quadVbo = null;
    let texture = null;
    let lastMode = null;
    let _ready = false;
    function build(ctx) {
      const { svg, state } = ctx;
      _svg = svg;
      _state = state;
      flatImage = document.createElementNS(SVG_NS4, "image");
      flatImage.setAttribute("x", "0");
      flatImage.setAttribute("y", "0");
      flatImage.setAttribute("width", "2000");
      flatImage.setAttribute("height", "1000");
      flatImage.setAttribute("preserveAspectRatio", "xMidYMid meet");
      flatImage.setAttribute("pointer-events", "none");
      flatImage.setAttribute("display", "none");
      svg.appendChild(flatImage);
      globeForeign = document.createElementNS(SVG_NS4, "foreignObject");
      globeForeign.setAttribute("x", "-3000");
      globeForeign.setAttribute("y", "-3000");
      globeForeign.setAttribute("width", "8000");
      globeForeign.setAttribute("height", "7000");
      globeForeign.setAttribute("pointer-events", "none");
      globeForeign.setAttribute("display", "none");
      svg.appendChild(globeForeign);
      globeCanvas = document.createElementNS(XHTML_NS, "canvas");
      globeCanvas.style.cssText = "width:100%;height:100%;display:block;background:transparent;pointer-events:none;";
      globeCanvas.width = 2e3;
      globeCanvas.height = 1e3;
      globeForeign.appendChild(globeCanvas);
      bakeFlagMap(state.data.countries, state.data.flags, state._geomByIso, state._topologyArcs).then((bakes) => {
        try {
          window.__bakes = bakes;
        } catch (_) {
        }
        if (!flatImage || !globeCanvas) return;
        try {
          flatImage.setAttribute("href", bakes.ee.toDataURL("image/png"));
        } catch (e) {
          console.warn("[wuys/map/flags] flat bake toDataURL failed:", e && e.message);
        }
        try {
          gl = globeCanvas.getContext("webgl2", { antialias: true, alpha: true, premultipliedAlpha: false });
          if (gl) setupGlobeGL(bakes.eq);
        } catch (e) {
          console.warn("[wuys/map/flags] globe gl setup failed:", e && e.message);
        }
        _ready = true;
        patch({ state: _state });
      });
    }
    function setupGlobeGL(eqCanvas) {
      program = linkProgram(gl);
      if (!program) {
        gl = null;
        return;
      }
      attrLoc = {
        aPos: gl.getAttribLocation(program, "aPos")
      };
      uniLoc = {
        uRotation: gl.getUniformLocation(program, "uRotation"),
        uBaseR: gl.getUniformLocation(program, "uBaseR"),
        uViewCenter: gl.getUniformLocation(program, "uViewCenter"),
        uViewBox: gl.getUniformLocation(program, "uViewBox"),
        uTex: gl.getUniformLocation(program, "uTex")
      };
      gl.clearColor(0, 0, 0, 0);
      gl.disable(gl.DEPTH_TEST);
      gl.enable(gl.BLEND);
      gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
      quadVbo = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, quadVbo);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        -1,
        -1,
        1,
        -1,
        -1,
        1,
        1,
        -1,
        1,
        1,
        -1,
        1
      ]), gl.STATIC_DRAW);
      texture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
      gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, eqCanvas);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    }
    function patch(ctx) {
      if (!_svg) return;
      const state = ctx && ctx.state || _state;
      if (!state) return;
      if (!state.theme || !state.view) return;
      const showFlags = state.theme.flagsVisible === true;
      const mode = state.view.mode;
      if (mode !== lastMode) lastMode = mode;
      if (mode === "globe") {
        if (flatImage) flatImage.setAttribute("display", "none");
        if (globeForeign) globeForeign.setAttribute("display", "none");
        return;
      }
      if (!showFlags || !_ready) {
        if (flatImage) flatImage.setAttribute("display", "none");
        if (globeForeign) globeForeign.setAttribute("display", "none");
        return;
      }
      if (flatImage) flatImage.removeAttribute("display");
      if (globeForeign) globeForeign.setAttribute("display", "none");
    }
    function renderGlobe(state) {
      if (!gl || !program || !texture) return;
      const rect = _svg.getBoundingClientRect();
      const vb = _svg.viewBox.baseVal;
      const vbw = vb.width || 2e3, vbh = vb.height || 1e3;
      const dpr = window.devicePixelRatio || 1;
      const scale = Math.min(rect.width / vbw, rect.height / vbh);
      let cw = Math.max(1, Math.floor(8e3 * scale * dpr));
      let ch = Math.max(1, Math.floor(7e3 * scale * dpr));
      if (cw > 8192) {
        ch = Math.floor(ch * 8192 / cw);
        cw = 8192;
      }
      if (ch > 8192) {
        cw = Math.floor(cw * 8192 / ch);
        ch = 8192;
      }
      if (globeCanvas.width !== cw || globeCanvas.height !== ch) {
        globeCanvas.width = cw;
        globeCanvas.height = ch;
      }
      gl.viewport(0, 0, cw, ch);
      const rotLon = state.nav.lon, rotLat = state.nav.lat, zoom = state.nav.zoom;
      const baseR = 450 * zoom;
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.useProgram(program);
      gl.uniform2f(uniLoc.uRotation, rotLon * Math.PI / 180, rotLat * Math.PI / 180);
      gl.uniform1f(uniLoc.uBaseR, baseR);
      gl.uniform2f(uniLoc.uViewCenter, 1e3, 500);
      gl.uniform4f(uniLoc.uViewBox, -3e3, -3e3, 8e3, 7e3);
      gl.uniform1i(uniLoc.uTex, 0);
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.bindBuffer(gl.ARRAY_BUFFER, quadVbo);
      gl.enableVertexAttribArray(attrLoc.aPos);
      gl.vertexAttribPointer(attrLoc.aPos, 2, gl.FLOAT, false, 8, 0);
      gl.drawArrays(gl.TRIANGLES, 0, 6);
    }
    function destroy() {
      if (gl) {
        if (quadVbo) gl.deleteBuffer(quadVbo);
        if (texture) gl.deleteTexture(texture);
        if (program) gl.deleteProgram(program);
      }
      if (flatImage && flatImage.parentNode) flatImage.parentNode.removeChild(flatImage);
      if (globeForeign && globeForeign.parentNode) globeForeign.parentNode.removeChild(globeForeign);
      flatImage = null;
      globeForeign = null;
      globeCanvas = null;
      gl = null;
      program = null;
      quadVbo = null;
      texture = null;
    }
    return { id: "flags", build, patch, destroy };
  }
  function compileShader(gl, type, src) {
    const sh = gl.createShader(type);
    gl.shaderSource(sh, src);
    gl.compileShader(sh);
    if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
      console.warn("[wuys/map/flags] shader compile failed:", gl.getShaderInfoLog(sh));
      gl.deleteShader(sh);
      return null;
    }
    return sh;
  }
  function linkProgram(gl) {
    const vs = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER2);
    const fs = compileShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER2);
    if (!vs || !fs) return null;
    const p = gl.createProgram();
    gl.attachShader(p, vs);
    gl.attachShader(p, fs);
    gl.linkProgram(p);
    if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
      console.warn("[wuys/map/flags] program link failed:", gl.getProgramInfoLog(p));
      gl.deleteProgram(p);
      return null;
    }
    return p;
  }

  // map/src/layers/spotlights.js
  var SVG_NS5 = "http://www.w3.org/2000/svg";
  function createSpotlightsLayer(opts = {}) {
    const nodes = /* @__PURE__ */ new Map();
    let group = null;
    let hoverIso = null;
    function build(ctx) {
      const { svg, state, events } = ctx;
      group = document.createElementNS(SVG_NS5, "g");
      group.setAttribute("id", "layer-spotlights");
      group.setAttribute("class", "wuys-spotlights");
      svg.appendChild(group);
      for (const s of state.data.spotlights || []) {
        const g = document.createElementNS(SVG_NS5, "g");
        g.setAttribute("data-iso", s.iso);
        g.setAttribute("data-name", s.name);
        g.setAttribute("display", "none");
        if (s.special) g.setAttribute("data-special", "1");
        const halo = document.createElementNS(SVG_NS5, "circle");
        halo.setAttribute("class", "wuys-spotlight-halo");
        halo.setAttribute("r", "10");
        halo.setAttribute("fill", "transparent");
        halo.setAttribute("pointer-events", "none");
        g.appendChild(halo);
        const dot = document.createElementNS(SVG_NS5, "circle");
        dot.setAttribute("class", "wuys-spotlight-dot");
        dot.setAttribute("r", "3");
        dot.setAttribute("cursor", "pointer");
        g.appendChild(dot);
        const label = document.createElementNS(SVG_NS5, "text");
        label.setAttribute("class", "wuys-spotlight-label");
        label.setAttribute("text-anchor", "middle");
        label.setAttribute("font-size", "10");
        label.setAttribute("y", "-12");
        label.setAttribute("display", "none");
        label.setAttribute("pointer-events", "none");
        label.textContent = s.name;
        g.appendChild(label);
        group.appendChild(g);
        nodes.set(s.iso, { g, halo, dot, label, meta: s });
        dot.addEventListener("mouseenter", () => {
          hoverIso = s.iso;
          label.removeAttribute("display");
          events.emit("spotlight:hover", s);
        });
        dot.addEventListener("mouseleave", () => {
          if (hoverIso === s.iso) hoverIso = null;
          label.setAttribute("display", "none");
          events.emit("spotlight:leave", s);
        });
        dot.addEventListener("click", (ev) => {
          ev.stopPropagation();
          events.emit("spotlight:click", s);
          events.emit("country:click", s.iso);
        });
      }
      applyTheme(ctx);
    }
    function patch(ctx) {
      const { state, theme, mode, screenForLabel } = ctx;
      const zoom = state.nav.zoom;
      const fadeStart = 3, fadeEnd = 5.5;
      let alpha = 1;
      if (zoom > fadeStart) alpha = Math.max(0, 1 - (zoom - fadeStart) / (fadeEnd - fadeStart));
      if (alpha <= 1e-3) {
        group.setAttribute("display", "none");
        return;
      }
      group.removeAttribute("display");
      group.setAttribute("opacity", alpha.toFixed(3));
      const dotR = Math.max(1.4, 3 / Math.max(0.7, Math.pow(zoom, 0.55)));
      const haloR = dotR * 3.2;
      for (const [iso, node] of nodes) {
        const pos = screenForLabel(iso, node.meta);
        if (!pos) {
          node.g.setAttribute("display", "none");
          continue;
        }
        node.g.removeAttribute("display");
        node.g.setAttribute("transform", "translate(" + pos.x.toFixed(2) + "," + pos.y.toFixed(2) + ")");
        node.halo.setAttribute("r", haloR.toFixed(2));
        node.dot.setAttribute("r", dotR.toFixed(2));
      }
    }
    function applyTheme(ctx) {
      if (!group) return;
      const sp = ctx.theme.spotlights;
      for (const [iso, node] of nodes) {
        const isSpecial = !!node.meta.special;
        node.dot.setAttribute("fill", isSpecial ? sp.specialFill || sp.fill : sp.fill);
        node.dot.setAttribute("stroke", isSpecial ? sp.specialBorder : sp.border);
        node.dot.setAttribute("stroke-width", "1");
        node.halo.setAttribute("stroke", sp.glow);
        node.halo.setAttribute("stroke-width", "1.5");
        node.halo.setAttribute("opacity", "0.6");
        node.label.setAttribute("fill", ctx.theme.labels.fill);
        node.label.setAttribute("stroke", ctx.theme.labels.stroke || "transparent");
        node.label.setAttribute("stroke-width", "0.6");
        node.label.setAttribute("paint-order", "stroke");
        node.label.setAttribute("font-family", ctx.theme.labels.fontFamily);
      }
    }
    function destroy() {
      if (group && group.parentNode) group.parentNode.removeChild(group);
      nodes.clear();
      group = null;
    }
    return { id: "spotlights", build, patch, applyTheme, destroy };
  }

  // map/src/layers/contested.js
  var SVG_NS6 = "http://www.w3.org/2000/svg";
  function createContestedLayer(opts = {}) {
    const nodes = /* @__PURE__ */ new Map();
    let group = null;
    function build(ctx) {
      const { svg, state, events } = ctx;
      group = document.createElementNS(SVG_NS6, "g");
      group.setAttribute("id", "layer-contested");
      group.setAttribute("class", "wuys-contested");
      svg.appendChild(group);
      for (const r of state.data.contestedRegions || []) {
        const p = document.createElementNS(SVG_NS6, "path");
        p.setAttribute("data-iso", r.iso);
        p.setAttribute("data-name", r.name);
        p.setAttribute("data-contested", "1");
        p.setAttribute("d", "M0,0");
        p.setAttribute("display", "none");
        group.appendChild(p);
        nodes.set(r.iso, { path: p, meta: r });
        p.addEventListener("mouseenter", () => events.emit("contested:hover", r));
        p.addEventListener("mouseleave", () => events.emit("contested:leave", r));
        p.addEventListener("click", () => events.emit("contested:click", r));
      }
      applyTheme(ctx);
    }
    function patch(ctx) {
      const { state, theme, mode } = ctx;
      const helpers = ctx;
      for (const [iso, node] of nodes) {
        const d = projectRing(node.meta.points, helpers);
        if (!d) {
          node.path.setAttribute("display", "none");
          continue;
        }
        node.path.removeAttribute("display");
        node.path.setAttribute("d", d);
      }
    }
    function projectRing(points, ctx) {
      if (ctx.projectRing) return ctx.projectRing(points);
      if (ctx._projectRing) return ctx._projectRing(points);
      return "";
    }
    function applyTheme(ctx) {
      if (!group) return;
      const c = ctx.theme.contested;
      for (const [iso, node] of nodes) {
        node.path.setAttribute("fill", c.fill);
        node.path.setAttribute("stroke", c.stroke);
        node.path.setAttribute("stroke-width", c.strokeWidth);
        if (c.strokeDasharray) node.path.setAttribute("stroke-dasharray", c.strokeDasharray);
        else node.path.removeAttribute("stroke-dasharray");
        node.path.setAttribute("vector-effect", "non-scaling-stroke");
        node.path.setAttribute("pointer-events", "all");
        node.path.setAttribute("cursor", "pointer");
      }
    }
    function destroy() {
      if (group && group.parentNode) group.parentNode.removeChild(group);
      nodes.clear();
      group = null;
    }
    return { id: "contested", build, patch, applyTheme, destroy };
  }

  // map/src/layers/atmosphere.js
  var SVG_NS7 = "http://www.w3.org/2000/svg";
  function createAtmosphereLayer(opts = {}) {
    let defs = null, group = null;
    let glow = null, shade = null;
    let gradGlow = null, gradShade = null;
    const _id = "wuys-atm-" + Math.random().toString(36).slice(2, 8);
    function build(ctx) {
      const { svg, state, theme } = ctx;
      defs = svg.querySelector("defs");
      if (!defs) {
        defs = document.createElementNS(SVG_NS7, "defs");
        svg.insertBefore(defs, svg.firstChild);
      }
      gradGlow = document.createElementNS(SVG_NS7, "radialGradient");
      gradGlow.setAttribute("id", _id + "-glow");
      appendStops(gradGlow, [
        ["80%", "rgba(0,0,0,0)"],
        ["93%", theme.globe?.atmosphere?.color || "rgba(110,180,255,0.4)"],
        ["100%", "rgba(0,0,0,0)"]
      ]);
      defs.appendChild(gradGlow);
      gradShade = document.createElementNS(SVG_NS7, "radialGradient");
      gradShade.setAttribute("id", _id + "-shade");
      appendStops(gradShade, [
        ["0%", "rgba(255,255,255,0)"],
        ["55%", "rgba(0,0,0,0)"],
        ["100%", "rgba(0,0,0,0.55)"]
      ]);
      defs.appendChild(gradShade);
      group = document.createElementNS(SVG_NS7, "g");
      group.setAttribute("id", "layer-atmosphere");
      group.setAttribute("pointer-events", "none");
      svg.appendChild(group);
      glow = document.createElementNS(SVG_NS7, "circle");
      glow.setAttribute("cx", "1000");
      glow.setAttribute("cy", "500");
      glow.setAttribute("r", "0");
      glow.setAttribute("fill", "url(#" + _id + "-glow)");
      glow.setAttribute("display", "none");
      group.appendChild(glow);
      shade = document.createElementNS(SVG_NS7, "circle");
      shade.setAttribute("cx", "1000");
      shade.setAttribute("cy", "500");
      shade.setAttribute("r", "0");
      shade.setAttribute("fill", "url(#" + _id + "-shade)");
      shade.setAttribute("display", "none");
      group.appendChild(shade);
    }
    function patch(ctx) {
      const { state, mode } = ctx;
      if (mode !== "globe") {
        glow.setAttribute("display", "none");
        shade.setAttribute("display", "none");
        return;
      }
      const baseR = 450 * state.nav.zoom;
      const glowR = baseR * 1.16;
      glow.removeAttribute("display");
      glow.setAttribute("r", glowR.toFixed(2));
      shade.removeAttribute("display");
      shade.setAttribute("r", baseR.toFixed(2));
      const sunDx = -baseR * 0.35, sunDy = -baseR * 0.35;
      const cx = 1e3 + sunDx, cy = 500 + sunDy;
      gradShade.setAttribute("cx", cx);
      gradShade.setAttribute("cy", cy);
      gradShade.setAttribute("fx", cx);
      gradShade.setAttribute("fy", cy);
      gradShade.setAttribute("r", baseR.toFixed(2));
      gradShade.setAttribute("gradientUnits", "userSpaceOnUse");
      gradGlow.setAttribute("cx", "1000");
      gradGlow.setAttribute("cy", "500");
      gradGlow.setAttribute("r", glowR.toFixed(2));
      gradGlow.setAttribute("gradientUnits", "userSpaceOnUse");
    }
    function applyTheme(ctx) {
      if (!gradGlow) return;
      const col = ctx.theme.globe?.atmosphere?.color || "rgba(110,180,255,0.4)";
      while (gradGlow.firstChild) gradGlow.removeChild(gradGlow.firstChild);
      appendStops(gradGlow, [
        ["80%", "rgba(0,0,0,0)"],
        ["93%", col],
        ["100%", "rgba(0,0,0,0)"]
      ]);
    }
    function appendStops(grad, stops) {
      for (const [offset, color] of stops) {
        const stop = document.createElementNS(SVG_NS7, "stop");
        stop.setAttribute("offset", offset);
        stop.setAttribute("stop-color", color);
        grad.appendChild(stop);
      }
    }
    function destroy() {
      if (group && group.parentNode) group.parentNode.removeChild(group);
      if (gradGlow && gradGlow.parentNode) gradGlow.parentNode.removeChild(gradGlow);
      if (gradShade && gradShade.parentNode) gradShade.parentNode.removeChild(gradShade);
      group = null;
      glow = null;
      shade = null;
    }
    return { id: "atmosphere", build, patch, applyTheme, destroy };
  }

  // map/src/data/spotlights.js
  var SPOTLIGHTS = [
    // Microstates already in the 10m base but too small to see at world zoom.
    { iso: "va", name: "Vatican City", lon: 12.453, lat: 41.903, kind: "micro" },
    { iso: "mc", name: "Monaco", lon: 7.42, lat: 43.738, kind: "micro" },
    { iso: "sm", name: "San Marino", lon: 12.447, lat: 43.937, kind: "micro" },
    { iso: "li", name: "Liechtenstein", lon: 9.555, lat: 47.166, kind: "micro" },
    { iso: "ad", name: "Andorra", lon: 1.521, lat: 42.506, kind: "micro" },
    { iso: "mt", name: "Malta", lon: 14.375, lat: 35.937, kind: "micro" },
    { iso: "lu", name: "Luxembourg", lon: 6.13, lat: 49.815, kind: "micro" },
    { iso: "hk", name: "Hong Kong", lon: 114.17, lat: 22.32, kind: "micro" },
    { iso: "mo", name: "Macau", lon: 113.55, lat: 22.2, kind: "micro" },
    { iso: "sg", name: "Singapore", lon: 103.82, lat: 1.35, kind: "micro" },
    { iso: "bh", name: "Bahrain", lon: 50.55, lat: 26.07, kind: "micro" },
    { iso: "gi", name: "Gibraltar", lon: -5.353, lat: 36.14, kind: "micro" },
    { iso: "mv", name: "Maldives", lon: 73.22, lat: 3.2, kind: "micro" },
    { iso: "mu", name: "Mauritius", lon: 57.55, lat: -20.35, kind: "micro" },
    { iso: "sc", name: "Seychelles", lon: 55.49, lat: -4.68, kind: "micro" },
    { iso: "km", name: "Comoros", lon: 43.87, lat: -11.65, kind: "micro" },
    { iso: "cv", name: "Cape Verde", lon: -24.01, lat: 16, kind: "micro" },
    { iso: "st", name: "S\xE3o Tom\xE9", lon: 6.65, lat: 0.19, kind: "micro" },
    { iso: "ag", name: "Antigua & Barbuda", lon: -61.78, lat: 17.06, kind: "micro" },
    { iso: "bb", name: "Barbados", lon: -59.55, lat: 13.19, kind: "micro" },
    { iso: "dm", name: "Dominica", lon: -61.37, lat: 15.41, kind: "micro" },
    { iso: "gd", name: "Grenada", lon: -61.68, lat: 12.12, kind: "micro" },
    { iso: "kn", name: "St Kitts & Nevis", lon: -62.74, lat: 17.36, kind: "micro" },
    { iso: "lc", name: "St Lucia", lon: -60.99, lat: 13.91, kind: "micro" },
    { iso: "vc", name: "St Vincent", lon: -61.2, lat: 13.25, kind: "micro" },
    { iso: "ws", name: "Samoa", lon: -172.1, lat: -13.76, kind: "micro" },
    { iso: "to", name: "Tonga", lon: -175.2, lat: -21.18, kind: "micro" },
    { iso: "tv", name: "Tuvalu", lon: 179.2, lat: -7.48, kind: "micro" },
    { iso: "ki", name: "Kiribati", lon: -157.36, lat: 1.87, kind: "micro" },
    { iso: "nr", name: "Nauru", lon: 166.92, lat: -0.52, kind: "micro" },
    { iso: "fm", name: "Micronesia", lon: 158.21, lat: 6.89, kind: "micro" },
    { iso: "mh", name: "Marshall Islands", lon: 171.18, lat: 7.13, kind: "micro" },
    { iso: "pw", name: "Palau", lon: 134.58, lat: 7.51, kind: "micro" },
    // Tax havens / Crown Dependencies (in base map but visually trivial).
    { iso: "ky", name: "Cayman Islands", lon: -81.265, lat: 19.35, kind: "tax-haven" },
    { iso: "vg", name: "British Virgin Is.", lon: -64.64, lat: 18.42, kind: "tax-haven" },
    { iso: "bm", name: "Bermuda", lon: -64.781, lat: 32.293, kind: "tax-haven" },
    { iso: "je", name: "Jersey", lon: -2.106, lat: 49.214, kind: "tax-haven" },
    { iso: "gg", name: "Guernsey", lon: -2.585, lat: 49.45, kind: "tax-haven" },
    { iso: "im", name: "Isle of Man", lon: -4.481, lat: 54.236, kind: "tax-haven" },
    // Sovereign-adjacent power districts — sub-national, not in any country dataset.
    { iso: "_gb_lc", name: "City of London", lon: -0.0871, lat: 51.5142, special: true, kind: "power" },
    { iso: "_us_dc", name: "Washington DC", lon: -77.0369, lat: 38.9072, special: true, kind: "power" },
    { iso: "_geneva", name: "Geneva (Swiss financial)", lon: 6.143, lat: 46.204, special: true, kind: "power" },
    { iso: "_brussels", name: "Brussels (EU capital)", lon: 4.353, lat: 50.847, special: true, kind: "power" },
    { iso: "_wall_st", name: "Wall Street (NYSE)", lon: -74.011, lat: 40.706, special: true, kind: "power" },
    { iso: "_davos", name: "Davos (WEF)", lon: 9.838, lat: 46.802, special: true, kind: "power" },
    { iso: "_hague", name: "The Hague (ICC / ICJ)", lon: 4.3, lat: 52.07, special: true, kind: "power" },
    { iso: "_frankfurt", name: "Frankfurt (ECB)", lon: 8.682, lat: 50.11, special: true, kind: "power" },
    { iso: "_delaware", name: "Delaware (LLC haven)", lon: -75.524, lat: 39.158, special: true, kind: "power" },
    { iso: "_nevada", name: "Nevada (corporate haven)", lon: -117.224, lat: 39.876, special: true, kind: "power" },
    { iso: "_sdakota", name: "South Dakota (trust haven)", lon: -100.336, lat: 44.3, special: true, kind: "power" },
    // Contested / occupied / unrecognized territory anchors.
    { iso: "_tibet", name: "Tibet (occupied)", lon: 91.1, lat: 29.65, special: true, kind: "contested" },
    { iso: "_xinjiang", name: "Xinjiang / East Turkestan", lon: 87.62, lat: 43.79, special: true, kind: "contested" },
    { iso: "_crimea", name: "Crimea (annexed)", lon: 34, lat: 45, special: true, kind: "contested" },
    { iso: "_kurdistan", name: "Kurdistan (cross-border)", lon: 44.01, lat: 36.19, special: true, kind: "contested" },
    { iso: "_tigray", name: "Tigray (Ethiopia, war-affected)", lon: 39.47, lat: 13.5, special: true, kind: "contested" }
  ];

  // map/src/data/contested.js
  var CONTESTED_REGIONS = [
    {
      iso: "_tibet",
      name: "Tibet (TAR + ethnic regions)",
      shortName: "Tibet",
      points: [
        [78.5, 35.5],
        [80, 35.5],
        [84, 36.2],
        [88, 35],
        [92.5, 35],
        [96, 34.2],
        [99, 33],
        [99, 30.5],
        [97, 28.8],
        [92, 28],
        [88, 27.5],
        [85, 27.5],
        [82, 28.5],
        [80, 30],
        [78.5, 32]
      ]
    },
    {
      iso: "_xinjiang",
      name: "Xinjiang / East Turkestan",
      shortName: "Xinjiang",
      points: [
        [74, 48.5],
        [80, 49.2],
        [86, 49],
        [92, 47.5],
        [96, 45.5],
        [96, 42.5],
        [94.5, 40.5],
        [89, 38.5],
        [83, 37],
        [78, 37],
        [75.5, 38.5],
        [73.5, 41.5],
        [73.5, 45]
      ]
    },
    {
      iso: "_crimea",
      name: "Crimea (annexed)",
      shortName: "Crimea",
      points: [
        [33.5, 46.2],
        [35, 46.2],
        [36.5, 46.1],
        [36.7, 45.5],
        [36.5, 44.9],
        [35.5, 44.4],
        [34, 44.4],
        [33, 44.7],
        [32.5, 45.2],
        [32.7, 45.8]
      ]
    },
    {
      iso: "_kurdistan",
      name: "Kurdistan (cross-border)",
      shortName: "Kurdistan",
      points: [
        [37, 38],
        [42, 38.5],
        [45.5, 38],
        [47, 36.5],
        [46.5, 34.5],
        [45, 33.5],
        [43.5, 35.2],
        [41, 36.5],
        [38.5, 36.8],
        [36.5, 37.5]
      ]
    },
    {
      iso: "_tigray",
      name: "Tigray (Ethiopia)",
      shortName: "Tigray",
      points: [
        [36.5, 14.9],
        [38.5, 14.9],
        [40.5, 14.5],
        [40.5, 13],
        [39.5, 12.4],
        [38, 12.5],
        [36.8, 13.2],
        [36.5, 14]
      ]
    },
    // Western Sahara — disputed; Morocco controls most, Polisario controls a strip.
    {
      iso: "_w_sahara",
      name: "Western Sahara",
      shortName: "W. Sahara",
      points: [
        [-17.05, 27.67],
        [-13, 27.67],
        [-8.68, 27.67],
        [-8.68, 24],
        [-12, 23],
        [-13.5, 22],
        [-17.05, 20.77]
      ]
    }
    // Antarctica claims — too coarse for ring-level detail; defer until per-claim data added.
  ];

  // map/src/data/label-positions.js
  var LABEL_OFFSETS = {
    // Americas
    ar: { lon: 0.49, lat: -2.58 },
    cl: { lon: 0.62, lat: 6.44 },
    pe: { lon: -0.61, lat: -1.31 },
    ca: { lon: -10.68, lat: 0.84 },
    gy: { lon: -0.25, lat: 0.85 },
    sr: { lon: -0.16, lat: -0.07, fontSize: 3 },
    cu: { lon: -0.2, lat: 0.32, fontSize: 2 },
    ht: { lon: 0.02, lat: 0.2, fontSize: 2.73 },
    do: { lon: 0.49, lat: -0.04, fontSize: 2 },
    bz: { lon: -0.02, lat: 0, fontSize: 2 },
    gt: { lon: -0.04, lat: -0.13, fontSize: 2.45 },
    hn: { lon: -0.55, lat: 0, fontSize: 3.27 },
    ni: { lon: -0.25, lat: -0.22, fontSize: 2.73 },
    sv: { lon: 0.13, lat: -0.07, fontSize: 2 },
    cr: { lon: -0.22, lat: 0.42, fontSize: 2 },
    pa: { lon: -1.16, lat: 0, fontSize: 2 },
    // Western Europe
    gb: { lon: 0.97, lat: -1.49, fontSize: 2.86 },
    ie: { lon: 0.29, lat: 0.05, fontSize: 3 },
    be: { lon: -0.14, lat: 0.14, fontSize: 2 },
    nl: { lon: -0.07, lat: 0, fontSize: 2 },
    ch: { lon: -0.11, lat: 0.05, fontSize: 2 },
    it: { lon: 0.13, lat: -0.37, fontSize: 4.45 },
    pt: { lon: 0.34, lat: 1.82, fontSize: 2 },
    is: { lon: 0.25, lat: -0.23, fontSize: 3 },
    // Nordics + Baltics
    dk: { lon: 0, lat: -0.02, fontSize: 2.27 },
    no: { lon: -2.88, lat: -2.19, fontSize: 3.85 },
    se: { lon: -0.98, lat: -2.43, fontSize: 4.29 },
    fi: { lon: -0.11, lat: -1.81 },
    ee: { lon: 0, lat: 0, fontSize: 2.73 },
    lv: { lon: 0.63, lat: 0.08, fontSize: 2.73 },
    lt: { lon: 0.16, lat: 0.26, fontSize: 2.09 },
    // Central + Eastern Europe + Balkans
    hu: { lon: -0.12, lat: -0.21 },
    sk: { lon: -0.23, lat: 0.05, fontSize: 2.09 },
    cz: { lon: 0.31, lat: -0.19, fontSize: 2.45 },
    gr: { lon: -0.81, lat: 0.13, fontSize: 2.09 },
    bg: { lon: 0, lat: 0, fontSize: 3 },
    al: { lon: 0.1, lat: -0.4, fontSize: 2 },
    mk: { lon: 0.09, lat: 0.09, fontSize: 2 },
    me: { lon: -0.22, lat: -0.01, fontSize: 2 },
    xk: { lon: -0.01, lat: 0, fontSize: 2 },
    ba: { lon: 0, lat: 0, fontSize: 2 },
    rs: { lon: 0, lat: -0.01, fontSize: 3 },
    hr: { lon: 0.47, lat: 0.44, fontSize: 2 },
    si: { lon: -0.1, lat: -0.03, fontSize: 2 },
    md: { lon: 0.12, lat: 0, fontSize: 2 },
    // Middle East
    iq: { lon: -0.43, lat: -0.38 },
    jo: { lon: -0.33, lat: -0.68, fontSize: 2.45 },
    il: { lon: -0.17, lat: -0.38, fontSize: 2 },
    lb: { lon: 0.01, lat: 0.16, fontSize: 2 },
    kw: { lon: 0, lat: 0, fontSize: 2 },
    om: { lon: 1.1, lat: 0.86 },
    ae: { lon: -0.38, lat: -0.35, fontSize: 2 },
    sa: { lon: -0.1, lat: 0.27, fontSize: 5.36 },
    ge: { lon: 0.1, lat: -0.21, fontSize: 2.45 },
    az: { lon: 0.19, lat: -0.03, fontSize: 2.73 },
    am: { lon: -0.24, lat: 0.2, fontSize: 2 },
    // Africa
    er: { lon: -0.78, lat: 0.36, fontSize: 2.73 },
    dj: { lon: 0.34, lat: 0.28, fontSize: 2 },
    rw: { lon: 0, lat: 0, fontSize: 2 },
    bi: { lon: 0, lat: 0, fontSize: 2 },
    cd: { lon: 0, lat: 0, fontSize: 8.18 },
    zm: { lon: -1.37, lat: -1.49 },
    mw: { lon: -0.12, lat: -0.63, fontSize: 2.45 },
    na: { lon: -0.34, lat: 2.23, fontSize: 5.91 },
    sz: { lon: -0.19, lat: 0.07, fontSize: 2 },
    za: { lon: -1.86, lat: -3.04, fontSize: 6.73 },
    ls: { lon: 0.02, lat: -0.05, fontSize: 2 },
    mg: { lon: 0, lat: 0, fontSize: 2.82 },
    mz: { lon: 2.05, lat: 4.31, fontSize: 3.91 },
    gq: { lon: 0, lat: 0, fontSize: 2 },
    cg: { lon: 0.88, lat: 1.03, fontSize: 3.18 },
    cm: { lon: -0.69, lat: -0.31, fontSize: 3.73 },
    cf: { lon: 0, lat: 0, fontSize: 4.4 },
    ss: { lon: 0, lat: 0, fontSize: 5.55 },
    et: { lon: 0, lat: 0, fontSize: 5.18 },
    so: { lon: -1.14, lat: -1.21, fontSize: 4.73 },
    sl: { lon: -0.04, lat: 0.23, fontSize: 2 },
    lr: { lon: -0.46, lat: 0.22, fontSize: 2.73 },
    ci: { lon: 0, lat: 0, fontSize: 3 },
    gh: { lon: 0, lat: 0, fontSize: 3.64 },
    tg: { lon: 0.14, lat: 0.22, fontSize: 2 },
    bj: { lon: 0.03, lat: 1.03, fontSize: 2.73 },
    bf: { lon: 0.39, lat: -0.18, fontSize: 2.64 },
    gn: { lon: 0.35, lat: 0.49 },
    gw: { lon: 0, lat: 0, fontSize: 2 },
    sn: { lon: -0.28, lat: 0.34, fontSize: 3.08 },
    ml: { lon: 1.91, lat: 1.21 },
    mr: { lon: -0.36, lat: -0.32 },
    eh: { lon: -0.48, lat: -0.52, fontSize: 2.27 },
    ma: { lon: 1.86, lat: 1.8, fontSize: 6.36 },
    tn: { lon: 0.2, lat: -1.31, fontSize: 2.73 },
    // Central Asia
    tm: { lon: -0.83, lat: 0.36, fontSize: 4.18 },
    uz: { lon: -0.06, lat: 0.27, fontSize: 3.27 },
    tj: { lon: -0.84, lat: 0.29, fontSize: 2.45 },
    kg: { lon: -1.77, lat: -1.48, fontSize: 2.45 },
    // East Asia
    jp: { lon: 0.51, lat: -0.3 },
    kr: { lon: 0.14, lat: -0.22, fontSize: 2 },
    kp: { lon: -0.52, lat: 0.08, fontSize: 2 },
    tw: { lon: 0, lat: 0, fontSize: 2 },
    // SE / South Asia
    ph: { lon: -0.12, lat: 0.62, fontSize: 2.27 },
    vn: { lon: 2.06, lat: -2.37, fontSize: 2 },
    la: { lon: -0.97, lat: 1.62 },
    my: { lon: -1.4, lat: -1.06, fontSize: 2 },
    th: { lon: 0.85, lat: 0.65, fontSize: 4.55 },
    mm: { lon: -0.36, lat: 0.23, fontSize: 5.18 },
    bt: { lon: 0.04, lat: -0.06, fontSize: 2.73 },
    bd: { lon: -0.02, lat: 0.69, fontSize: 2 },
    lk: { lon: 0.12, lat: -0.26, fontSize: 2 },
    np: { lon: 2.73, lat: -1.21, fontSize: 2.73 },
    // Contested regions
    _tigray: { lon: 0, lat: 0, fontSize: 2 },
    _kurdistan: { lon: -0.7, lat: 0.69, fontSize: 2.73 },
    _crimea: { lon: 0.02, lat: 0, fontSize: 2.09 }
  };

  // map/src/data/labels-convert.js
  var VBOX_UNITS_PER_DEG = 5.555;
  function applyLabelOffsets(offsets, countriesMeta) {
    const out = /* @__PURE__ */ new Map();
    if (!offsets) return out;
    for (const iso of Object.keys(offsets)) {
      const o = offsets[iso] || {};
      const cm = countriesMeta.get(iso);
      const baseLon = cm ? cm.lon : o.lon ?? 0;
      const baseLat = cm ? cm.lat : o.lat ?? 0;
      const lon = cm ? baseLon + (o.lon || 0) : o.lon ?? baseLon;
      const lat = cm ? baseLat + (o.lat || 0) : o.lat ?? baseLat;
      let sizeRatio = o.sizeRatio;
      if (!sizeRatio && o.fontSize && cm) {
        const projH = (cm.latRange || 30) * VBOX_UNITS_PER_DEG;
        sizeRatio = projH > 0 ? Math.max(0.04, Math.min(0.5, o.fontSize / projH)) : void 0;
      } else if (!sizeRatio && o.fontSize) {
        sizeRatio = Math.max(0.05, Math.min(0.5, o.fontSize / 25));
      }
      out.set(iso, {
        lon,
        lat,
        sizeRatio,
        rotate: o.rotate || 0
      });
    }
    return out;
  }

  // map/src/themes/vector.js
  var vector_default = {
    id: "vector",
    name: "Vector",
    background: "radial-gradient(ellipse at center, #c4a679 0%, #8c6e44 55%, #4e3520 100%)",
    ocean: {
      fill: "#88a3a8",
      stroke: "#3a4a4d",
      strokeWidth: 0.4
    },
    countries: {
      fill: "#ecd3a0",
      fillFallback: "#ecd3a0",
      stroke: "#3e2a13",
      strokeWidth: 0.4,
      hoverFilter: "drop-shadow(0 0 4px rgba(94,63,31,0.85))",
      pendingFill: "#d8b988",
      pendingFilter: "none"
    },
    borders: { color: "#3e2a13", width: 0.4 },
    labels: {
      fill: "#2e1a0a",
      stroke: "rgba(255,245,215,0.9)",
      strokeWidth: 0.8,
      fontFamily: "'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif",
      fontStyle: "normal",
      fontWeight: 600,
      letterSpacing: "0.02em"
    },
    contested: {
      fill: "rgba(160,80,40,0.30)",
      stroke: "#5c3a1a",
      strokeWidth: 0.8,
      strokeDasharray: "3 3"
    },
    spotlights: {
      fill: "#d8b988",
      border: "#3e2a13",
      glow: "rgba(60,40,20,0.5)",
      specialBorder: "#2e1a0a",
      specialFill: "#c89352"
    },
    cssVars: {
      "--map-bg": "#8c6e44",
      "--map-ocean": "#88a3a8",
      "--map-stroke": "#3e2a13",
      "--map-stroke-covered": "#3e2a13"
    }
  };

  // map/src/themes/antique.js
  var antique_default = {
    id: "antique",
    name: "Antique",
    background: "radial-gradient(ellipse at center, #c4a679 0%, #8c6e44 55%, #4e3520 100%)",
    ocean: {
      fill: "#88a3a8",
      // calm aged sea-teal
      stroke: "#45575a",
      strokeWidth: 0.9
    },
    countries: {
      fill: "#ecd3a0",
      fillFallback: "#ecd3a0",
      stroke: "#5c3a1a",
      strokeWidth: 0.7,
      hoverFilter: "drop-shadow(0 0 6px rgba(94,63,31,0.7))",
      pendingFill: "#d8b988",
      pendingFilter: "none",
      hidesFlags: true,
      // antique mode hides flag images
      showsLabels: true
      // and shows country name labels
    },
    borders: { color: "#5c3a1a", width: 0.7 },
    labels: {
      fill: "#2e1a0a",
      stroke: "rgba(255,240,200,0.85)",
      strokeWidth: 1.2,
      fontFamily: "'Cormorant Garamond', 'IM Fell English', Georgia, serif",
      fontStyle: "italic",
      fontWeight: 600,
      letterSpacing: "0.05em"
    },
    contested: {
      fill: "rgba(160,80,40,0.35)",
      stroke: "#5c3a1a",
      strokeWidth: 1,
      strokeDasharray: "4 2"
    },
    spotlights: {
      fill: "#d8b988",
      border: "#5c3a1a",
      glow: "rgba(94,63,31,0.6)",
      specialBorder: "#2e1a0a",
      specialFill: "#c89352"
    },
    cssVars: {
      "--map-bg": "#8c6e44",
      "--map-ocean": "#88a3a8",
      "--map-stroke": "#5c3a1a",
      "--map-stroke-covered": "#5c3a1a"
    }
  };

  // map/src/themes/toon.js
  var toon_default = {
    id: "toon",
    name: "Toon",
    background: "radial-gradient(ellipse at center, #c4a679 0%, #8c6e44 55%, #4e3520 100%)",
    ocean: {
      fill: "#88a3a8",
      stroke: "#0d1518",
      strokeWidth: 2.4
    },
    countries: {
      fill: "#ecd3a0",
      fillFallback: "#ecd3a0",
      stroke: "#0d1518",
      strokeWidth: 2,
      hoverFilter: "brightness(1.08) drop-shadow(0 0 0 #0d1518) drop-shadow(0 2px 0 #0d1518)",
      pendingFill: "#d8b988",
      pendingFilter: "none"
    },
    borders: { color: "#0d1518", width: 2 },
    labels: {
      fill: "#0d1518",
      stroke: "#fff5d0",
      strokeWidth: 2.4,
      fontFamily: "'Bangers', 'Permanent Marker', 'Luckiest Guy', cursive",
      fontStyle: "normal",
      fontWeight: 800,
      letterSpacing: "0.05em"
    },
    contested: {
      fill: "rgba(255,90,90,0.40)",
      stroke: "#a01020",
      strokeWidth: 2.4,
      strokeDasharray: "6 3"
    },
    spotlights: {
      fill: "#ecd3a0",
      border: "#0d1518",
      glow: "rgba(13,21,24,0.55)",
      specialBorder: "#a01020",
      specialFill: "#ffb1b1"
    },
    cssVars: {
      "--map-bg": "#8c6e44",
      "--map-ocean": "#88a3a8",
      "--map-stroke": "#0d1518",
      "--map-stroke-covered": "#0d1518"
    }
  };

  // map/src/themes/pixel.js
  var pixel_default = {
    id: "pixel",
    name: "Pixel",
    background: "radial-gradient(ellipse at center, #c4a679 0%, #8c6e44 55%, #4e3520 100%)",
    ocean: {
      fill: "#88a3a8",
      stroke: "#1a0e08",
      strokeWidth: 1.4
    },
    countries: {
      fill: "#ecd3a0",
      fillFallback: "#ecd3a0",
      stroke: "#1a0e08",
      strokeWidth: 1.4,
      hoverFilter: "brightness(1.15) drop-shadow(0 0 2px #f8c060)",
      pendingFill: "#d8b988",
      pendingFilter: "none"
    },
    borders: { color: "#1a0e08", width: 1.4 },
    labels: {
      fill: "#1a0e08",
      stroke: "#fff5d0",
      strokeWidth: 1.4,
      fontFamily: "'Press Start 2P', 'VT323', 'Courier New', monospace",
      fontStyle: "normal",
      fontWeight: 700,
      letterSpacing: "0.06em"
    },
    contested: {
      fill: "rgba(255,200,80,0.35)",
      stroke: "#ff5cf2",
      strokeWidth: 1.4,
      strokeDasharray: "2 2"
    },
    spotlights: {
      fill: "#ecd3a0",
      border: "#1a0e08",
      glow: "#f8c060",
      specialBorder: "#ff5cf2",
      specialFill: "#c89352"
    },
    cssVars: {
      "--map-bg": "#8c6e44",
      "--map-ocean": "#88a3a8",
      "--map-stroke": "#1a0e08",
      "--map-stroke-covered": "#1a0e08"
    }
  };

  // map/src/interactions/mouse.js
  function attachMouse({ target, state, events, nav, renderer, animator }) {
    let dragging = false;
    let dragStart = null;
    let pixelMoved = 0;
    target.addEventListener("wheel", (e) => {
      e.preventDefault();
      const factor = e.deltaY < 0 ? 1.4 : 1 / 1.4;
      const next = Math.max(NAV_ZOOM_MIN, Math.min(NAV_ZOOM_MAX, state.nav.zoom * factor));
      if (next === state.nav.zoom) return;
      state.nav.zoom = next;
      state.nav.tZoom = next;
      animator.snap();
      renderer.patch();
      events.emit("nav:change", state.nav);
    }, { passive: false });
    target.addEventListener("mousedown", (e) => {
      if (e.button !== 0) return;
      if (e.target.closest("[data-no-drag]")) return;
      animator.snap();
      dragging = true;
      pixelMoved = 0;
      dragStart = { x: e.clientX, y: e.clientY, lon: state.nav.lon, lat: state.nav.lat };
      target.classList.add("wuys-map-dragging");
      state._isDragging = true;
    });
    window.addEventListener("mousemove", (e) => {
      if (!dragging) {
        const hit = renderer.hitTest(e.clientX, e.clientY);
        if (hit) {
          if (state.interactions.hoveredIso !== hit.iso) {
            const prev = state.interactions.hoveredIso;
            state.interactions.hoveredIso = hit.iso;
            if (prev) events.emit("country:leave", prev);
            events.emit("country:hover", hit.iso);
          }
        } else if (state.interactions.hoveredIso) {
          const prev = state.interactions.hoveredIso;
          state.interactions.hoveredIso = null;
          events.emit("country:leave", prev);
        }
        return;
      }
      const dx = e.clientX - dragStart.x;
      const dy = e.clientY - dragStart.y;
      pixelMoved = Math.max(pixelMoved, Math.hypot(dx, dy));
      if (state.view.mode === "flat") {
        const startScreen = renderer.worldToScreen({ lon: dragStart.lon, lat: dragStart.lat });
        const target2 = renderer.screenToWorld({ x: startScreen.x + dx, y: startScreen.y + dy });
        state.nav.lon = dragStart.lon - (target2.lon - dragStart.lon);
        state.nav.lat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, dragStart.lat - (target2.lat - dragStart.lat)));
      } else {
        const sensDeg = 0.28 / Math.max(0.5, state.nav.zoom);
        state.nav.lon = dragStart.lon - dx * sensDeg;
        state.nav.lat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, dragStart.lat + dy * sensDeg));
      }
      state.nav.tLon = state.nav.lon;
      state.nav.tLat = state.nav.lat;
      renderer.patch();
      events.emit("nav:change", state.nav);
    });
    window.addEventListener("mouseup", () => {
      if (!dragging) return;
      dragging = false;
      target.classList.remove("wuys-map-dragging");
      state._isDragging = false;
      renderer.patch();
      if (pixelMoved > 5) {
        const swallow = (ev) => {
          ev.stopPropagation();
          ev.preventDefault();
          target.removeEventListener("click", swallow, true);
        };
        target.addEventListener("click", swallow, true);
      }
    });
    target.addEventListener("click", (e) => {
      if (e.defaultPrevented) return;
      const hit = renderer.hitTest(e.clientX, e.clientY);
      if (hit) events.emit("country:click", hit.iso);
    });
  }

  // map/src/interactions/keyboard.js
  var ARROWS = /* @__PURE__ */ new Set(["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]);
  function attachKeyboard({ state, events, nav, renderer, themesAvail, isActive }) {
    const held = /* @__PURE__ */ new Set();
    let raf = 0;
    let last = 0;
    let modShift = false, modCtrl = false;
    function tick(now) {
      raf = 0;
      if (!last) last = now;
      const dt = Math.min(0.05, (now - last) / 1e3);
      last = now;
      let mult = 1;
      if (modShift) mult = 3;
      if (modCtrl) mult = 6;
      const speedDegPerSec = state.view.mode === "globe" ? 31 : 26;
      const step = speedDegPerSec * mult * dt / Math.max(1, state.nav.zoom * 0.4);
      if (held.has("ArrowLeft")) state.nav.tLon -= step;
      if (held.has("ArrowRight")) state.nav.tLon += step;
      if (held.has("ArrowUp")) state.nav.tLat = Math.min(NAV_LAT_MAX, state.nav.tLat + step);
      if (held.has("ArrowDown")) state.nav.tLat = Math.max(-NAV_LAT_MAX, state.nav.tLat - step);
      while (state.nav.tLon > 180) state.nav.tLon -= 360;
      while (state.nav.tLon < -180) state.nav.tLon += 360;
      state.nav.lon = state.nav.tLon;
      state.nav.lat = state.nav.tLat;
      if (renderer) renderer.patch();
      events.emit("nav:change", state.nav);
      if (held.size > 0) {
        raf = requestAnimationFrame(tick);
      } else {
        last = 0;
      }
    }
    window.addEventListener("keydown", (ev) => {
      modShift = ev.shiftKey;
      modCtrl = ev.ctrlKey || ev.metaKey;
      if (!isActive()) return;
      const tag = ev.target && ev.target.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA" || ev.target.isContentEditable) return;
      if (ARROWS.has(ev.key)) {
        ev.preventDefault();
        held.add(ev.key);
        if (!raf) {
          last = 0;
          raf = requestAnimationFrame(tick);
        }
        return;
      }
      if (ev.key === "+" || ev.key === "=") {
        nav.zoomBy(1.5);
      } else if (ev.key === "-") {
        nav.zoomBy(1 / 1.5);
      } else if (ev.key === " ") {
        ev.preventDefault();
        nav.reset();
      } else if (ev.key === "g" || ev.key === "G") {
        const next = state.view.mode === "flat" ? "globe" : "flat";
        events.emit("view:request", next);
      } else if (ev.key === "f" || ev.key === "F") {
        events.emit("flags:toggle", null);
      } else if (ev.key === "?") {
        events.emit("help:toggle", null);
      }
    });
    window.addEventListener("keyup", (ev) => {
      modShift = ev.shiftKey;
      modCtrl = ev.ctrlKey || ev.metaKey;
      held.delete(ev.key);
    });
    window.addEventListener("blur", () => held.clear());
  }

  // map/src/interactions/touch.js
  function attachTouch({ target, state, events, nav, renderer, animator }) {
    let mode = "idle";
    let panStart = null;
    let pinchStart = null;
    let lastTap = 0;
    let pixelMoved = 0;
    function touchMid(t1, t2) {
      return { x: (t1.clientX + t2.clientX) / 2, y: (t1.clientY + t2.clientY) / 2 };
    }
    function touchDist(t1, t2) {
      return Math.hypot(t1.clientX - t2.clientX, t1.clientY - t2.clientY);
    }
    target.addEventListener("touchstart", (e) => {
      if (e.target.closest && e.target.closest("[data-no-drag]")) return;
      animator.snap();
      if (e.touches.length === 1) {
        const t = e.touches[0];
        mode = "pan";
        pixelMoved = 0;
        panStart = { x: t.clientX, y: t.clientY, lon: state.nav.lon, lat: state.nav.lat };
        target.classList.add("wuys-map-dragging");
        state._isDragging = true;
      } else if (e.touches.length >= 2) {
        const [a, b] = [e.touches[0], e.touches[1]];
        mode = "pinch";
        const mid = touchMid(a, b);
        pinchStart = {
          dist: touchDist(a, b),
          midX: mid.x,
          midY: mid.y,
          zoom: state.nav.zoom,
          lon: state.nav.lon,
          lat: state.nav.lat
        };
      }
    }, { passive: true });
    target.addEventListener("touchmove", (e) => {
      if (mode === "pan" && e.touches.length === 1) {
        e.preventDefault();
        const t = e.touches[0];
        const dx = t.clientX - panStart.x;
        const dy = t.clientY - panStart.y;
        pixelMoved = Math.max(pixelMoved, Math.hypot(dx, dy));
        if (state.view.mode === "flat") {
          const startScreen = renderer.worldToScreen({ lon: panStart.lon, lat: panStart.lat });
          const targetW = renderer.screenToWorld({ x: startScreen.x + dx, y: startScreen.y + dy });
          state.nav.lon = panStart.lon - (targetW.lon - panStart.lon);
          state.nav.lat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, panStart.lat - (targetW.lat - panStart.lat)));
        } else {
          const sensDeg = 0.28 / Math.max(0.5, state.nav.zoom);
          state.nav.lon = panStart.lon + dx * sensDeg;
          state.nav.lat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, panStart.lat + dy * sensDeg));
        }
        state.nav.tLon = state.nav.lon;
        state.nav.tLat = state.nav.lat;
        renderer.patch();
        events.emit("nav:change", state.nav);
      } else if (mode === "pinch" && e.touches.length >= 2) {
        e.preventDefault();
        const [a, b] = [e.touches[0], e.touches[1]];
        const dist = touchDist(a, b);
        const factor = dist / pinchStart.dist;
        const nextZoom = Math.max(NAV_ZOOM_MIN, Math.min(NAV_ZOOM_MAX, pinchStart.zoom * factor));
        state.nav.zoom = nextZoom;
        state.nav.tZoom = nextZoom;
        renderer.patch();
        events.emit("nav:change", state.nav);
      }
    }, { passive: false });
    target.addEventListener("touchend", (e) => {
      if (mode === "pan") {
        target.classList.remove("wuys-map-dragging");
        if (pixelMoved < 8) {
          const now = Date.now();
          if (now - lastTap < 280) {
            state.nav.tZoom = Math.min(NAV_ZOOM_MAX, state.nav.tZoom * 1.8);
            animator.kick();
            lastTap = 0;
          } else {
            lastTap = now;
            const tch = e.changedTouches && e.changedTouches[0];
            if (tch) {
              const hit = renderer.hitTest(tch.clientX, tch.clientY);
              if (hit) events.emit("country:click", hit.iso);
            }
          }
        }
      }
      if (e.touches.length === 0) {
        mode = "idle";
        panStart = null;
        pinchStart = null;
        state._isDragging = false;
        renderer.patch();
      } else if (e.touches.length === 1 && mode === "pinch") {
        const t = e.touches[0];
        mode = "pan";
        pixelMoved = 0;
        panStart = { x: t.clientX, y: t.clientY, lon: state.nav.lon, lat: state.nav.lat };
      }
    }, { passive: true });
    target.addEventListener("touchcancel", () => {
      mode = "idle";
      panStart = null;
      pinchStart = null;
      target.classList.remove("wuys-map-dragging");
    }, { passive: true });
  }

  // map/src/ui/styles.js
  var STYLE_ID = "wuys-map-overlay-styles";
  var CSS = `
.wuys-map-overlay {
  position: absolute; inset: 0; pointer-events: none; z-index: 10;
  font: 14px/1.3 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: rgba(255,255,255,0.95);
}
.wuys-map-overlay > * { pointer-events: auto; }
.wuys-map-overlay-tl, .wuys-map-overlay-tr {
  position: absolute; top: 16px; display: flex; gap: 8px; align-items: center;
}
.wuys-map-overlay-tl { left: 16px; }
.wuys-map-overlay-tr { right: 16px; }

.wuys-map-btn {
  background: rgba(20,15,40,0.6);
  border: 1px solid rgba(255,255,255,0.18);
  color: rgba(255,255,255,0.92);
  height: 36px; min-width: 36px; padding: 0 12px;
  border-radius: 10px; cursor: pointer; font-size: 16px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  transition: background .15s, border-color .15s, transform .1s;
}
.wuys-map-btn:hover { background: rgba(40,28,70,0.75); border-color: rgba(255,255,255,0.32); }
.wuys-map-btn:active { transform: scale(0.97); }

.wuys-map-select {
  appearance: none; padding-right: 30px;
  background-image: linear-gradient(45deg, transparent 50%, rgba(255,255,255,0.7) 50%),
                    linear-gradient(135deg, rgba(255,255,255,0.7) 50%, transparent 50%);
  background-position: right 12px top 16px, right 7px top 16px;
  background-size: 5px 5px;
  background-repeat: no-repeat;
}

.wuys-map-search-wrap { position: relative; }
.wuys-map-search {
  background: rgba(20,15,40,0.6);
  border: 1px solid rgba(255,255,255,0.18);
  color: rgba(255,255,255,0.95);
  height: 36px; width: 220px; padding: 0 12px;
  border-radius: 10px; font-size: 14px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.wuys-map-search::placeholder { color: rgba(255,255,255,0.5); }
.wuys-map-search:focus { outline: none; border-color: rgba(183,104,255,0.7); }

.wuys-map-search-results {
  position: absolute; top: 44px; right: 0; min-width: 220px;
  background: rgba(20,15,40,0.92);
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 10px; padding: 4px;
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  display: flex; flex-direction: column;
}
.wuys-map-search-results:empty { display: none; }
.wuys-map-search-item {
  background: transparent; border: 0; color: inherit;
  text-align: left; padding: 8px 10px; border-radius: 6px; cursor: pointer;
  font: inherit;
}
.wuys-map-search-item:hover { background: rgba(183,104,255,0.25); }

.wuys-map-perf {
  position: absolute; right: 16px; bottom: 16px;
  background: rgba(0,0,0,0.5); color: #b8eaff;
  padding: 6px 10px; border-radius: 8px;
  font: 12px/1 ui-monospace, "SF Mono", Consolas, monospace;
}
.wuys-map-renderer-badge {
  position: absolute; left: 16px; bottom: 16px;
  background: rgba(0,0,0,0.45); color: rgba(255,255,255,0.7);
  padding: 4px 8px; border-radius: 8px;
  font: 11px/1 ui-monospace, "SF Mono", Consolas, monospace;
  display: inline-flex; align-items: center; gap: 6px;
}
.wuys-map-renderer-select {
  background: rgba(20,15,40,0.75);
  border: 1px solid rgba(255,255,255,0.18);
  color: rgba(255,255,255,0.92);
  padding: 2px 22px 2px 6px; border-radius: 5px;
  font: 11px ui-monospace, monospace;
  appearance: none; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, rgba(255,255,255,0.65) 50%),
                    linear-gradient(135deg, rgba(255,255,255,0.65) 50%, transparent 50%);
  background-position: right 9px center, right 4px center;
  background-size: 4px 4px; background-repeat: no-repeat;
}

.wuys-map-help {
  position: absolute; inset: 0; display: none;
  align-items: center; justify-content: center;
  background: rgba(0,0,0,0.6); backdrop-filter: blur(4px);
}
.wuys-map-help.open { display: flex; }
.wuys-map-help-card {
  background: rgba(20,15,40,0.95);
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 14px; padding: 24px; min-width: 320px; color: #fff;
}
.wuys-map-help-card h3 { margin: 0 0 12px 0; font-weight: 600; }
.wuys-map-help-card ul { list-style: none; padding: 0; margin: 0 0 16px 0; }
.wuys-map-help-card li { margin: 6px 0; color: rgba(255,255,255,0.85); }
.wuys-map-help-card kbd {
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.22);
  padding: 1px 6px; border-radius: 4px; font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 11px; margin: 0 2px;
}

.wuys-map-dragging { cursor: grabbing !important; }
`;
  function ensureStyles() {
    if (document.getElementById(STYLE_ID)) return;
    const s = document.createElement("style");
    s.id = STYLE_ID;
    s.textContent = CSS;
    document.head.appendChild(s);
  }

  // map/src/ui/overlays.js
  function attachOverlays({ map, target, ui = {} }) {
    ensureStyles();
    const overlay = document.createElement("div");
    overlay.className = "wuys-map-overlay";
    overlay.setAttribute("data-no-drag", "1");
    target.style.position = target.style.position || "relative";
    target.appendChild(overlay);
    const topL = el("div", "wuys-map-overlay-tl");
    overlay.appendChild(topL);
    if (ui.view !== false) {
      const btn = makeButton("\u{1F30D}", "Toggle flat/globe view (G)", () => map.view.toggle());
      btn.dataset.role = "view-toggle";
      map.on("view:change", (mode) => {
        btn.textContent = mode === "globe" ? "\u{1F5FA}" : "\u{1F30D}";
      });
      topL.appendChild(btn);
    }
    if (ui.flags !== false) {
      const btn = makeButton("\u2691", "Toggle flag fills (F)", () => map.theme.toggleFlags());
      topL.appendChild(btn);
    }
    if (ui.themes !== false) {
      const themeNames = Object.keys(map._internal ? map._internal.themes || {} : {});
      const sel = document.createElement("select");
      sel.className = "wuys-map-btn wuys-map-select";
      sel.title = "Theme";
      for (const k of themeNames.length ? themeNames : ["vector", "antique", "toon", "pixel"]) {
        const o = document.createElement("option");
        o.value = k;
        o.textContent = capitalize(k);
        sel.appendChild(o);
      }
      sel.value = map.theme.id;
      sel.addEventListener("change", () => map.theme.set(sel.value));
      map.on("theme:change", (id) => {
        sel.value = id;
      });
      topL.appendChild(sel);
    }
    const topR = el("div", "wuys-map-overlay-tr");
    overlay.appendChild(topR);
    if (ui.search !== false) {
      let ensureIndex = function() {
        if (countries.length || !map.state.data.countries) return;
        for (const [iso, cm] of map.state.data.countries) {
          countries.push({ iso, name: cm.name || iso, lon: cm.lon, lat: cm.lat });
        }
        for (const s of map.state.data.spotlights || []) {
          countries.push({ iso: s.iso, name: s.name, lon: s.lon, lat: s.lat });
        }
      }, render = function(matches) {
        results.innerHTML = "";
        for (const c of matches.slice(0, 8)) {
          const item = document.createElement("button");
          item.className = "wuys-map-search-item";
          item.textContent = c.name;
          item.addEventListener("click", () => {
            map.nav.flyTo({ lon: c.lon, lat: c.lat, zoom: 3.5 });
            results.innerHTML = "";
            input.value = "";
          });
          results.appendChild(item);
        }
      };
      const wrap = el("div", "wuys-map-search-wrap");
      const input = document.createElement("input");
      input.type = "search";
      input.placeholder = "Search country\u2026";
      input.className = "wuys-map-search";
      wrap.appendChild(input);
      const results = el("div", "wuys-map-search-results");
      wrap.appendChild(results);
      topR.appendChild(wrap);
      let countries = [];
      input.addEventListener("input", () => {
        ensureIndex();
        const q = input.value.trim().toLowerCase();
        if (!q) {
          results.innerHTML = "";
          return;
        }
        const matches = countries.filter((c) => c.name.toLowerCase().includes(q) || c.iso.toLowerCase().includes(q));
        render(matches);
      });
      input.addEventListener("blur", () => {
        setTimeout(() => results.innerHTML = "", 250);
      });
    }
    if (ui.help !== false) {
      const btn = makeButton("?", "Keyboard shortcuts (?)", () => helpModal.classList.toggle("open"));
      topR.appendChild(btn);
      const helpModal = el("div", "wuys-map-help");
      helpModal.innerHTML = `
      <div class="wuys-map-help-card">
        <h3>Map shortcuts</h3>
        <ul>
          <li><kbd>\u2191 \u2193 \u2190 \u2192</kbd> Pan (hold Shift = 3\xD7, Ctrl = 6\xD7)</li>
          <li><kbd>+</kbd> / <kbd>-</kbd> Zoom in / out</li>
          <li><kbd>Space</kbd> Reset view</li>
          <li><kbd>G</kbd> Toggle flat / globe</li>
          <li><kbd>F</kbd> Toggle flag fills</li>
          <li><kbd>?</kbd> This help</li>
        </ul>
        <button class="wuys-map-btn">Close</button>
      </div>`;
      overlay.appendChild(helpModal);
      helpModal.querySelector("button").addEventListener("click", () => helpModal.classList.remove("open"));
      map.on("help:toggle", () => helpModal.classList.toggle("open"));
    }
    if (ui.perf) {
      const perf = el("div", "wuys-map-perf");
      overlay.appendChild(perf);
      let frames = 0, last = performance.now(), fps = 0;
      map.on("nav:change", () => {
        frames++;
        const now = performance.now();
        if (now - last > 500) {
          fps = Math.round(frames * 1e3 / (now - last));
          frames = 0;
          last = now;
          perf.textContent = "fps " + fps + " \xB7 zoom " + map.nav.getCenter().zoom.toFixed(2);
        }
      });
    }
    if (ui.rendererBadge !== false) {
      const wrap = el("div", "wuys-map-renderer-badge");
      const label = document.createElement("span");
      const sel = document.createElement("select");
      sel.className = "wuys-map-renderer-select";
      const renderers = map.listRenderers && map.listRenderers() || ["svg"];
      for (const id of renderers) {
        const o = document.createElement("option");
        o.value = id;
        const isGpu = id === "webgl";
        o.textContent = id + " (" + (isGpu ? "gpu" : "cpu") + ")";
        sel.appendChild(o);
      }
      sel.value = map.getRendererId ? map.getRendererId() : "svg";
      sel.title = "Switch renderer (CPU/GPU)";
      sel.addEventListener("change", async () => {
        const prev = sel.value;
        try {
          if (map.setRenderer) await map.setRenderer(sel.value);
        } catch (err) {
          console.warn("[wuys/map] renderer swap failed:", err);
        }
      });
      label.textContent = "renderer: ";
      wrap.appendChild(label);
      wrap.appendChild(sel);
      overlay.appendChild(wrap);
      map.on("renderer:change", (id) => {
        sel.value = id;
      });
      map.on("renderer:error", ({ id, err }) => {
        console.warn("[wuys/map] renderer error:", id, err);
        sel.value = map.getRendererId();
      });
    }
    return {
      destroy() {
        if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
      },
      overlay
    };
  }
  function el(tag, cls) {
    const e = document.createElement(tag);
    if (cls) e.className = cls;
    return e;
  }
  function capitalize(s) {
    return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
  }
  function makeButton(label, title, onClick) {
    const b = document.createElement("button");
    b.className = "wuys-map-btn";
    b.title = title;
    b.textContent = label;
    b.addEventListener("click", onClick);
    return b;
  }

  // map/src/index.js
  var projections = { equalEarth: equal_earth_exports, orthographic: orthographic_exports };
  var themes = {
    vector: vector_default,
    antique: antique_default,
    toon: toon_default,
    pixel: pixel_default
  };
  var layers = {
    COUNTRIES: "countries",
    FLAGS: "flags",
    LABELS: "labels",
    SPOTLIGHTS: "spotlights",
    CONTESTED: "contested",
    ATMOSPHERE: "atmosphere"
  };
  async function createMap(opts = {}) {
    const target = opts.target;
    if (!target) throw new Error("[wuys/map] opts.target is required");
    const themeObj = opts.theme || vector_default;
    const topoData = await loadTopology(opts.data?.topology ? { url: opts.data.topology } : {});
    const flagsData = await loadFlags(opts.data?.flags ? { url: opts.data.flags } : {});
    const flagPositions = await loadFlagPositions(opts.data?.flagPositions ? { url: opts.data.flagPositions } : {});
    const numToIso = topoData.numToIso instanceof Map ? topoData.numToIso : tableFromDict(topoData.numToIso);
    const decoded = decodeTopojson(topoData.topo);
    const countryMeta = computeCountryMeta(decoded, numToIso);
    const geomByIso = /* @__PURE__ */ new Map();
    for (const geom of decoded.geometries) {
      const iso = numToIso.get(String(parseInt(geom.id, 10)));
      if (iso) geomByIso.set(iso, geom);
    }
    const initialPos = opts.position || {};
    const safeNum = (v, fallback) => Number.isFinite(v) ? v : fallback;
    const state = createState({
      lon: safeNum(initialPos.lon, 0),
      lat: safeNum(initialPos.lat, 0),
      zoom: safeNum(initialPos.zoom, 1),
      view: opts.view ?? "flat",
      theme: opts.theme && opts.theme.id || "vector",
      flagsVisible: opts.flagsVisible !== false,
      labelsVisible: opts.labelsVisible === true,
      layers: opts.layers ?? ["countries", "flags", "labels", "spotlights", "contested", "atmosphere"]
    });
    state.data.topology = decoded;
    state.data.countries = countryMeta;
    state.data.flags = new Map(Object.entries(flagsData || {}));
    state.data.flagPositions = new Map(Object.entries(flagPositions || {}));
    state.data.isoCodes.numToA2 = numToIso;
    state.data.spotlights = opts.data?.spotlights || SPOTLIGHTS;
    state.data.contestedRegions = opts.data?.contestedRegions || CONTESTED_REGIONS;
    const offsetTable = opts.data?.labelOffsets || LABEL_OFFSETS;
    state.data.labels = applyLabelOffsets(offsetTable, countryMeta);
    state.data.ready = true;
    state._topologyArcs = decoded.arcs;
    state._geomByIso = geomByIso;
    const events = createEventBus();
    let _activeRenderer = null;
    const rendererProxy = {
      init: (...a) => _activeRenderer.init(...a),
      patch: () => _activeRenderer && _activeRenderer.patch(),
      destroy: () => _activeRenderer && _activeRenderer.destroy(),
      hitTest: (x, y) => _activeRenderer.hitTest(x, y),
      worldToScreen: (p) => _activeRenderer.worldToScreen(p),
      screenToWorld: (p) => _activeRenderer.screenToWorld(p),
      viewportToWorld: (dx, dy) => _activeRenderer.viewportToWorld(dx, dy),
      getViewport: () => _activeRenderer.getViewport(),
      applyTheme: () => _activeRenderer && _activeRenderer.applyTheme && _activeRenderer.applyTheme()
    };
    const animator = createAnimator({
      getCurrent: () => ({ lon: state.nav.lon, lat: state.nav.lat, zoom: state.nav.zoom }),
      getTarget: () => ({ lon: state.nav.tLon, lat: state.nav.tLat, zoom: state.nav.tZoom }),
      setCurrent: (k, v) => {
        state.nav[k] = v;
      },
      adjustDelta: shortestLonDelta,
      tolerance: { lon: 0.01, lat: 0.01, zoom: 3e-3 },
      onStep: () => {
        normalizeLon(state);
        rendererProxy.patch();
        events.emit("nav:change", state.nav);
      },
      onSettle: () => {
        events.emit("nav:end", state.nav);
      }
    });
    function buildLayerSet() {
      const ls = [];
      if (state.layers.enabled.has("countries")) ls.push(createCountriesLayer());
      if (state.layers.enabled.has("flags")) ls.push(createFlagsLayer());
      if (state.layers.enabled.has("contested")) ls.push(createContestedLayer());
      if (state.layers.enabled.has("labels")) ls.push(createLabelsLayer({ getCountryName: opts.getCountryName }));
      if (state.layers.enabled.has("spotlights")) ls.push(createSpotlightsLayer());
      if (state.layers.enabled.has("atmosphere")) ls.push(createAtmosphereLayer());
      return ls;
    }
    let rendererId = opts.renderer && opts.renderer !== "auto" ? opts.renderer : "svg";
    async function tryRenderer(id) {
      const r = createRenderer(id, {
        state,
        events,
        theme: themeObj,
        layers: buildLayerSet(),
        getCountryName: opts.getCountryName
      });
      await r.init(target, { theme: themeObj });
      return r;
    }
    try {
      _activeRenderer = await tryRenderer(rendererId);
      state._activeRendererId = rendererId;
    } catch (err) {
      if (rendererId !== "svg") {
        console.warn("[wuys/map] renderer '" + rendererId + "' failed to init:", err && err.message);
        rendererId = "svg";
        _activeRenderer = await tryRenderer(rendererId);
        state._activeRendererId = rendererId;
      } else {
        throw err;
      }
    }
    const ctx = { viewportToWorld: (dx, dy) => rendererProxy.viewportToWorld(dx, dy) };
    const nav = createNav({
      state,
      events,
      animator,
      ctx,
      onTweenStep: () => rendererProxy.patch()
    });
    if (opts.hashSync !== false) attachHashSync({ state, events });
    attachMouse({ target, state, events, nav, renderer: rendererProxy, animator });
    attachTouch({ target, state, events, nav, renderer: rendererProxy, animator });
    attachKeyboard({
      state,
      events,
      nav,
      renderer: rendererProxy,
      themesAvail: themes,
      isActive: () => target.isConnected
    });
    events.on("view:request", (mode) => api.view.set(mode));
    events.on("flags:toggle", () => api.theme.toggleFlags());
    if (typeof requestAnimationFrame === "function") {
      requestAnimationFrame(() => rendererProxy.patch());
    } else {
      rendererProxy.patch();
    }
    const api = {
      get state() {
        return state;
      },
      get renderer() {
        return _activeRenderer;
      },
      nav,
      view: {
        set(mode) {
          if (mode !== "flat" && mode !== "globe") return;
          if (state.view.mode === mode) return;
          state.view.mode = mode;
          events.emit("view:change", mode);
          rendererProxy.patch();
        },
        get() {
          return state.view.mode;
        },
        toggle() {
          api.view.set(state.view.mode === "flat" ? "globe" : "flat");
        }
      },
      theme: {
        /** Set the visual theme (vector / antique / toon / pixel). Pure styling
         *  change — does NOT touch flag visibility or label visibility. */
        set(themeObjOrId) {
          const next = typeof themeObjOrId === "string" ? themes[themeObjOrId] : themeObjOrId;
          if (!next) return;
          Object.assign(themeObj, next);
          state.theme.id = next.id;
          rendererProxy.applyTheme();
          events.emit("theme:change", next.id);
          rendererProxy.patch();
        },
        get id() {
          return state.theme.id;
        },
        /** Toggle flag-image fills on/off. Independent of theme and labels. */
        toggleFlags() {
          state.theme.flagsVisible = !state.theme.flagsVisible;
          events.emit("theme:flags", state.theme.flagsVisible);
          rendererProxy.patch();
        },
        /** Toggle country-name labels on/off. Independent of theme and flags. */
        toggleLabels() {
          state.theme.labelsVisible = !state.theme.labelsVisible;
          events.emit("theme:labels", state.theme.labelsVisible);
          rendererProxy.patch();
        },
        get flagsVisible() {
          return state.theme.flagsVisible === true;
        },
        get labelsVisible() {
          return state.theme.labelsVisible === true;
        }
      },
      on: events.on,
      off: events.off,
      once: events.once,
      worldToScreen: (p) => rendererProxy.worldToScreen(p),
      screenToWorld: (p) => rendererProxy.screenToWorld(p),
      getViewport: () => rendererProxy.getViewport(),
      /** Hot-swap the active renderer. Destroys the current one, mounts the new. */
      async setRenderer(id) {
        if (id === state._activeRendererId) return;
        const next = createRenderer(id, {
          state,
          events,
          theme: themeObj,
          layers: buildLayerSet(),
          getCountryName: opts.getCountryName
        });
        try {
          await next.init(target, { theme: themeObj });
        } catch (err) {
          events.emit("renderer:error", { id, err });
          return;
        }
        const old = _activeRenderer;
        _activeRenderer = next;
        state._activeRendererId = id;
        old.destroy();
        next.patch();
        events.emit("renderer:change", id);
      },
      getRendererId() {
        return state._activeRendererId;
      },
      listRenderers,
      destroy() {
        rendererProxy.destroy();
        events.emit("destroy", null);
        events.clear();
      },
      _internal: { state, events, animator, renderer: rendererProxy, themes, math: { equalEarth: equal_earth_exports, orthographic: orthographic_exports } }
    };
    events.emit("ready", null);
    return api;
  }

  // map/src/integrations/mount.js
  async function mountMap(target, opts = {}) {
    const el2 = typeof target === "string" ? document.querySelector(target) : target;
    if (!el2) throw new Error("[wuys/map] mountMap: target not found: " + target);
    let themeObj = opts.theme;
    if (typeof themeObj === "string") themeObj = themes[themeObj] || themes.vector;
    else if (!themeObj) themeObj = themes.vector;
    const map = await createMap({
      target: el2,
      view: opts.view ?? "flat",
      theme: themeObj,
      renderer: opts.renderer ?? "svg",
      flagsVisible: opts.flagsVisible !== false,
      labelsVisible: opts.labelsVisible === true,
      layers: opts.layers,
      getCountryName: opts.getCountryName,
      data: opts.data,
      position: opts.position,
      hashSync: opts.hashSync === true
    });
    if (opts.overlays !== false) {
      attachOverlays({
        map,
        target: el2,
        ui: opts.ui ?? { view: true, themes: true, flags: true, search: true, help: true, perf: false, rendererBadge: true }
      });
    }
    return map;
  }
  return __toCommonJS(bundle_entry_exports);
})();
