<template>
  <div class="map">
    <canvas ref="canvasRef"></canvas>
  </div>
</template>
<script setup>
import { nextTick, ref, shallowRef, onBeforeUnmount, onMounted, watch } from "vue";
import { World } from "./map.js";
const props = defineProps({
  activeLayer: {
    type: Object,
    default: null,
  },
  mapRanking: {
    type: Object,
    default: null,
  },
  tileUrlTemplate: {
    type: String,
    default: "",
  },
  adminBoundaryLineScale: {
    type: Number,
    default: 1,
  },
});
const emit = defineEmits(["feature-loading", "feature-info", "layer-status", "tile-status", "draw-boundary", "scene-mounted"]);
const canvasRef = ref(null);
const canvasMap = shallowRef(null);
let resizeObserver = null;
let resizeFrame = null;
let resizeFallbackTimer = null;
let resizeCheckTimer = null;
let introFallbackTimer = null;
let activeLayerSignature = "";
let mapRankingSignature = "";
let hasPlayedIntro = false;
onMounted(() => {
  emit("scene-mounted");
});
onBeforeUnmount(() => {
  destroyMap();
});
function loadMap(assets) {
  if (canvasMap.value) return true;
  if (!canvasRef.value) return false;
  canvasMap.value = new World(canvasRef.value, assets, {
    onFeatureLoading: (loading) => emit("feature-loading", loading),
    onFeatureInfo: (payload) => emit("feature-info", payload),
    onLayerStatus: (status) => emit("layer-status", status),
    onTileStatus: (status) => emit("tile-status", status),
    onDrawBoundary: (payload) => emit("draw-boundary", payload),
    tileUrlTemplate: props.tileUrlTemplate,
    adminBoundaryLineScale: props.adminBoundaryLineScale,
  });
  canvasMap.value.time.pause();
  activeLayerSignature = getLayerSignature(props.activeLayer);
  mapRankingSignature = getMapRankingSignature(props.mapRanking);
  canvasMap.value.setTopicLayers(getActiveLayerStack(props.activeLayer), props.activeLayer);
  canvasMap.value.setTownRanking(props.mapRanking);
  connectMapResizeObserver();
  scheduleMapResize();
  return true;
}
function hasMap() {
  return Boolean(canvasMap.value);
}
async function play() {
  if (!canvasMap.value) return;
  resume();
  if (canvasMap.value.mapAnimationComplete) return;
  if (hasPlayedIntro) {
    canvasMap.value.animateTl?.play?.();
    scheduleIntroFallback();
    return;
  }
  hasPlayedIntro = true;
  canvasMap.value.animateTl?.timeScale?.(1); // 设置播放速度正常
  canvasMap.value.animateTl?.play?.();
  scheduleIntroFallback();
}
function resume() {
  if (!canvasMap.value) return;
  connectMapResizeObserver();
  scheduleMapResize();
  window.requestAnimationFrame(() => {
    canvasMap.value?.label3d?.resize?.();
    canvasMap.value?.label3d?.update?.();
  });
  canvasMap.value.resumeScene?.();
  if (!canvasMap.value.resumeScene) canvasMap.value.time.resume();
}
function pause() {
  if (!canvasMap.value) return;
  clearIntroFallback();
  canvasMap.value.animateTl?.pause?.();
  canvasMap.value.pauseScene?.();
  if (!canvasMap.value.pauseScene) canvasMap.value.time?.pause?.();
  cleanupMapResizeObserver();
}
function clearSelectedFeature() {
  canvasMap.value?.clearSelectedFeature?.();
}
function focusProject(projectName) {
  canvasMap.value?.focusProject?.(projectName);
}
function focusPasture(payload) {
  canvasMap.value?.focusPasture?.(payload);
}
function focusTown(townName, options) {
  return canvasMap.value?.focusTownByName?.(townName, options);
}
function focusYakIndustryPoint(payload) {
  return canvasMap.value?.focusYakIndustryPoint?.(payload);
}
function getYakIndustryPointScreen(payload) {
  return canvasMap.value?.getYakIndustryPointScreen?.(payload);
}
function setBoundaryDrawing(enabled) {
  canvasMap.value?.setBoundaryDrawing?.(enabled);
}
function finishBoundaryDrawing() {
  return canvasMap.value?.finishBoundaryDrawing?.();
}
function undoBoundaryPoint() {
  canvasMap.value?.undoBoundaryPoint?.();
}
function clearAnalysisBoundary() {
  canvasMap.value?.clearAnalysisBoundary?.();
}
function showAnalysisResult(payload) {
  canvasMap.value?.showAnalysisResult?.(payload);
}
function destroyMap() {
  clearIntroFallback();
  cleanupMapResizeObserver();
  canvasMap.value?.destroy?.();
  canvasMap.value = null;
}
function scheduleIntroFallback() {
  clearIntroFallback();
  introFallbackTimer = window.setTimeout(() => {
    introFallbackTimer = null;
    canvasMap.value?.ensureMapRevealed?.();
  }, 5200);
}
function clearIntroFallback() {
  if (!introFallbackTimer) return;
  window.clearTimeout(introFallbackTimer);
  introFallbackTimer = null;
}
function scheduleMapResize() {
  if (resizeFrame) window.cancelAnimationFrame(resizeFrame);
  resizeFrame = window.requestAnimationFrame(() => {
    resizeFrame = null;
    canvasMap.value?.sizes?.init?.();
    canvasMap.value?.resize?.();
  });
  if (resizeFallbackTimer) window.clearTimeout(resizeFallbackTimer);
  resizeFallbackTimer = window.setTimeout(() => {
    resizeFallbackTimer = null;
    canvasMap.value?.sizes?.init?.();
    canvasMap.value?.resize?.();
  }, 180);
}
function connectMapResizeObserver() {
  cleanupMapResizeObserver();
  const target = canvasRef.value?.parentNode;
  if (target && window.ResizeObserver) {
    resizeObserver = new ResizeObserver(() => scheduleMapResize());
    resizeObserver.observe(target);
  }
  resizeCheckTimer = window.setInterval(ensureMapCanvasSize, 1000);
  nextTick(scheduleMapResize);
}
function ensureMapCanvasSize() {
  if (!canvasMap.value || !canvasRef.value) return;
  const target = canvasRef.value.parentNode;
  const width = target?.offsetWidth || window.innerWidth || 1;
  const height = target?.offsetHeight || window.innerHeight || 1;
  const pixelRatio = canvasMap.value.sizes?.pixelRatio || 1;
  const expectedWidth = Math.floor(width * pixelRatio);
  const expectedHeight = Math.floor(height * pixelRatio);
  if (canvasRef.value.width !== expectedWidth || canvasRef.value.height !== expectedHeight) {
    scheduleMapResize();
  }
}
function cleanupMapResizeObserver() {
  if (resizeObserver) {
    resizeObserver.disconnect();
    resizeObserver = null;
  }
  if (resizeFrame) {
    window.cancelAnimationFrame(resizeFrame);
    resizeFrame = null;
  }
  if (resizeFallbackTimer) {
    window.clearTimeout(resizeFallbackTimer);
    resizeFallbackTimer = null;
  }
  if (resizeCheckTimer) {
    window.clearInterval(resizeCheckTimer);
    resizeCheckTimer = null;
  }
}
function refreshView() {
  if (!canvasMap.value) return;
  connectMapResizeObserver();
  const refresh = () => {
    if (!canvasMap.value) return;
    canvasMap.value?.resumeScene?.();
    if (!canvasMap.value.resumeScene) canvasMap.value.time?.resume?.();
    canvasMap.value?.sizes?.init?.();
    canvasMap.value?.resize?.();
    canvasMap.value?.label3d?.resize?.();
    canvasMap.value?.label3d?.update?.();
    canvasMap.value?.setTopicLayers?.(getActiveLayerStack(props.activeLayer), props.activeLayer);
    canvasMap.value?.setTownRanking?.(props.mapRanking);
    canvasMap.value?.ensureMapRevealed?.();
  };
  scheduleMapResize();
  window.requestAnimationFrame(refresh);
  window.setTimeout(() => {
    refresh();
  }, 80);
  window.setTimeout(() => {
    refresh();
  }, 220);
}
watch(
  () => props.activeLayer,
  (layer) => {
    const signature = getLayerSignature(layer);
    if (signature === activeLayerSignature) return;
    activeLayerSignature = signature;
    if (canvasMap.value) {
      canvasMap.value.setTopicLayers(getActiveLayerStack(layer), layer);
    }
  },
  { deep: true },
);
watch(
  () => props.mapRanking,
  (ranking) => {
    const signature = getMapRankingSignature(ranking);
    if (signature === mapRankingSignature) return;
    mapRankingSignature = signature;
    if (canvasMap.value) {
      canvasMap.value.setTownRanking(ranking);
    }
  },
  { deep: true },
);
defineExpose({
  loadMap,
  hasMap,
  play,
  resume,
  pause,
  clearSelectedFeature,
  focusProject,
  focusPasture,
  focusTown,
  focusYakIndustryPoint,
  getYakIndustryPointScreen,
  setBoundaryDrawing,
  finishBoundaryDrawing,
  undoBoundaryPoint,
  clearAnalysisBoundary,
  showAnalysisResult,
  refreshView,
  destroyMap,
  canvasMap,
});

function getLayerSignature(layer) {
  if (!layer) return "";
  return getActiveLayerStack(layer)
    .map((item) => [
      item.key || "",
      item.layerName || "",
      item.styles || "",
      item.cqlFilter || "",
      item.activeLegend?.key || "",
      item.timelineDate || "",
      item.timelineItem?.key || "",
      item.sourceType || "",
      item.dataPath || "",
      item.fallbackPath || "",
    ].join("|"))
    .join("||");
}

function getActiveLayerStack(layer) {
  if (!layer) return [];
  const overlayLayers = Array.isArray(layer.overlayLayers) ? layer.overlayLayers.filter(Boolean) : [];
  return [layer, ...overlayLayers];
}

function getMapRankingSignature(ranking) {
  if (!ranking) return "";
  return [
    ranking.status || "",
    ranking.topicKey || "",
    ranking.layerKey || "",
    ranking.title || "",
    ranking.metricLabel || "",
    ranking.scope || "",
    ranking.displayMode || "",
    ranking.showPointMarkers ?? "",
    ranking.showBars ?? "",
    ranking.showTownNames ?? "",
    ranking.showMapLabels ?? "",
    ranking.showFlowLines ?? "",
    ranking.onlyRankedTownLabels ?? "",
    ranking.showMissingTownStats ?? "",
    ranking.rows?.length || 0,
    ranking.rows?.map((row) => `${row.key || row.name || ""}:${row.value ?? ""}`).join(",") || "",
  ].join("|");
}
</script>

<style lang="scss">
.map {
  position: absolute;
  z-index: 1;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background-color: #000;
  canvas {
    display: block;
    width: 100%;
    height: 100%;
  }

  .yak-industry-point-label {
    width: 172px;
    min-height: 72px;
    margin-bottom: 28px;
    color: #f2feff;
    font-size: 12px;
    pointer-events: none;
    will-change: transform;
  }

  .yak-industry-point-label-wrap {
    position: relative;
    width: 172px;
    padding: 8px 10px 9px;
    border: 1px solid color-mix(in srgb, var(--yak-point-color, #24f6c4) 56%, transparent);
    border-radius: 4px;
    background:
      linear-gradient(120deg, rgba(6, 37, 49, 0.88), rgba(3, 16, 27, 0.76)),
      linear-gradient(90deg, color-mix(in srgb, var(--yak-point-color, #24f6c4) 18%, transparent), transparent 72%);
    box-shadow:
      0 0 18px color-mix(in srgb, var(--yak-point-color, #24f6c4) 26%, transparent),
      inset 0 0 14px rgba(48, 220, 255, 0.08);
  }

  .yak-industry-point-label-wrap::after {
    content: "";
    position: absolute;
    left: 14px;
    bottom: -9px;
    width: 14px;
    height: 14px;
    border-right: 1px solid color-mix(in srgb, var(--yak-point-color, #24f6c4) 52%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--yak-point-color, #24f6c4) 52%, transparent);
    background: rgba(3, 16, 27, 0.82);
    transform: rotate(45deg);
  }

  .yak-industry-point-label-head,
  .yak-industry-point-label-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
  }

  .yak-industry-point-label-head {
    color: rgba(218, 249, 255, 0.7);
    font-size: 10px;
  }

  .yak-industry-point-label-head i {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--yak-point-color, #24f6c4);
    box-shadow: 0 0 10px var(--yak-point-color, #24f6c4);
  }

  .yak-industry-point-label-head span,
  .yak-industry-point-label-name,
  .yak-industry-point-label-meta span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .yak-industry-point-label-name {
    margin-top: 5px;
    color: #ffffff;
    font-size: 13px;
    font-weight: 800;
    line-height: 1.35;
  }

  .yak-industry-point-label-meta {
    margin-top: 5px;
    color: rgba(211, 241, 246, 0.68);
    font-size: 10px;
  }

  .yak-industry-point-label-meta strong {
    flex: 0 0 auto;
    color: #ffe06b;
    font-size: 11px;
    font-weight: 800;
  }

  .info-point {
    color: #eaffff;
    font-size: 13px;
    width: 178px;
    min-height: 82px;
    margin-bottom: 24px;
    will-change: transform;
    &-wrap {
      position: relative;
      width: 178px;
      padding: 8px 10px 9px;
      border: 1px solid rgba(104, 236, 255, 0.38);
      border-radius: 5px;
      background:
        linear-gradient(120deg, rgba(6, 36, 48, 0.92), rgba(4, 18, 29, 0.78)),
        rgba(2, 16, 25, 0.82);
      box-shadow:
        0 0 18px rgba(48, 220, 255, 0.2),
        inset 0 0 18px rgba(39, 240, 192, 0.08);
      transform: translate(42%, -12%);
      &-inner {
        position: relative;
        &:after,
        &:before {
          position: absolute;
          left: -18px;
          content: "";
        }
        &:before {
          top: 42px;
          width: 16px;
          height: 1px;
          background: linear-gradient(90deg, rgba(104, 236, 255, 0), rgba(104, 236, 255, 0.88));
        }
        &:after {
          top: 38px;
          width: 7px;
          height: 7px;
          border-radius: 50%;
          background: #fff06a;
          box-shadow: 0 0 12px rgba(255, 240, 106, 0.9);
        }
      }
      &.is-prime {
        border-color: rgba(255, 224, 107, 0.52);
        background:
          linear-gradient(120deg, rgba(67, 48, 13, 0.72), rgba(4, 24, 34, 0.82)),
          rgba(2, 16, 25, 0.86);
      }
    }
    &-head {
      display: flex;
      align-items: center;
      gap: 7px;
      min-width: 0;
      .rank {
        flex: 0 0 auto;
        color: #ffe06b;
        font-size: 12px;
        line-height: 14px;
        font-weight: 900;
      }
      strong {
        overflow: hidden;
        color: #ffffff;
        font-size: 15px;
        line-height: 18px;
        font-weight: 900;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }
    &-metric {
      margin-top: 4px;
      overflow: hidden;
      color: rgba(166, 244, 255, 0.78);
      font-size: 11px;
      line-height: 13px;
      font-weight: 700;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    &-value {
      display: flex;
      align-items: baseline;
      gap: 4px;
      margin-top: 2px;
      span {
        color: #ffffff;
        font-size: 25px;
        line-height: 28px;
        font-weight: 900;
        text-shadow: 0 0 12px rgba(126, 251, 246, 0.6);
      }
      em {
        color: rgba(235, 252, 255, 0.78);
        font-size: 11px;
        line-height: 13px;
        font-style: normal;
        font-weight: 800;
      }
    }
  }
  .provinces-label {
    &-wrap {
      transform: translate(50%, 200%);
      opacity: 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 8px;
      padding: 8px 10px;
      width: 136px;
      min-height: 58px;
      border: 1px solid rgba(126, 251, 246, 0.22);
      border-radius: 8px 8px 8px 0px;
      background: rgba(1, 19, 30, 0.62);
      box-shadow: 0 0 16px rgba(48, 220, 255, 0.16);
    }
    .label-main {
      min-width: 0;
    }
    .label-title {
      max-width: 94px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      color: rgba(126, 251, 246, 0.82);
      font-size: 10px;
      line-height: 12px;
      font-weight: 500;
    }
    .number {
      color: #fff;
      font-size: 21px;
      line-height: 25px;
      font-weight: 700;

      .unit {
        color: #fff;
        font-size: 11px;
        font-weight: 400;
        opacity: 0.64;
        padding-left: 4px;
      }
    }
    .name {
      color: #fff;
      font-size: 12px;
      line-height: 14px;
      font-weight: 700;
      max-width: 94px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      span {
        display: block;
      }
      .en {
        color: #fff;
        font-size: 10px;
        opacity: 0.5;
        font-weight: 700;
      }
    }
    .no {
      color: #7efbf6;
      text-shadow: 0 0 5px #7efbf6, 0 0 10px #7efbf6;
      font-size: 22px;
      font-weight: 700;
      flex: 0 0 auto;
    }
    &.yellow {
      .no {
        color: #fef99e !important;
        text-shadow: 0 0 5px #fef99e, 0 0 10px #fef99e !important;
      }
    }
  }

  .epidemic-village-label {
    will-change: transform;

    .epidemic-village-card {
      position: relative;
      width: 142px;
      padding: 7px 9px 6px;
      border: 1px solid rgba(91, 236, 235, 0.42);
      border-radius: 4px;
      color: #eaffff;
      background:
        linear-gradient(90deg, rgba(5, 35, 45, 0.92), rgba(2, 18, 29, 0.78)),
        rgba(1, 17, 27, 0.86);
      box-shadow: 0 0 16px rgba(45, 222, 255, 0.22), inset 0 0 16px rgba(39, 240, 192, 0.08);
      transform: translate(48%, -12%);

      &:before {
        position: absolute;
        left: -10px;
        top: 50%;
        width: 10px;
        height: 1px;
        content: "";
        background: linear-gradient(90deg, rgba(91, 236, 235, 0), rgba(91, 236, 235, 0.78));
      }

      &:after {
        position: absolute;
        left: -14px;
        top: calc(50% - 3px);
        width: 6px;
        height: 6px;
        border-radius: 50%;
        content: "";
        background: #fff36d;
        box-shadow: 0 0 10px #fff36d;
      }

      &.is-small {
        transform: translate(42%, -8%) scale(0.92);
      }
    }

    .village-card-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 6px;
      min-width: 0;

      strong {
        overflow: hidden;
        color: #ffffff;
        font-size: 13px;
        line-height: 16px;
        font-weight: 800;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      span {
        flex: 0 0 auto;
        padding: 1px 5px 2px;
        border: 1px solid rgba(39, 240, 192, 0.28);
        border-radius: 2px;
        color: #79f8e5;
        font-size: 10px;
        line-height: 11px;
        background: rgba(39, 240, 192, 0.08);
      }
    }

    .village-card-body {
      display: flex;
      align-items: baseline;
      gap: 3px;
      margin-top: 4px;
      white-space: nowrap;

      .metric {
        color: rgba(201, 255, 251, 0.76);
        font-size: 10px;
        line-height: 12px;
      }

      .number {
        color: #ffffff;
        font-size: 20px;
        line-height: 22px;
        font-weight: 900;
        text-shadow: 0 0 10px rgba(126, 251, 246, 0.7);
      }

      .unit {
        color: rgba(232, 252, 255, 0.8);
        font-size: 10px;
        line-height: 12px;
        font-weight: 700;
      }
    }

    .village-card-foot {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 4px;
      margin-top: 3px;
      color: rgba(226, 250, 255, 0.62);
      font-size: 10px;
      line-height: 11px;
      white-space: nowrap;
    }
  }

  .trade-market-label,
  .trade-flow-label,
  .trade-national-label {
    color: #ffffff;
    will-change: transform;
    pointer-events: none;
  }

  .trade-national-name {
    padding: 2px 6px;
    border: 1px solid rgba(72, 231, 255, 0.24);
    border-radius: 3px;
    background: rgba(3, 23, 35, 0.42);
    color: rgba(255, 255, 255, 0.62);
    font-size: 10px;
    line-height: 12px;
    font-weight: 800;
    text-shadow: 0 0 6px rgba(48, 220, 255, 0.42);
    white-space: nowrap;
    transform: translate(-50%, -50%);
  }

  .trade-market-card {
    position: relative;
    width: 128px;
    padding: 6px 8px 6px;
    border: 1px solid rgba(255, 221, 117, 0.58);
    border-radius: 4px;
    background:
      linear-gradient(110deg, rgba(68, 44, 10, 0.72), rgba(4, 24, 34, 0.58)),
      rgba(5, 20, 30, 0.58);
    box-shadow: 0 0 10px rgba(255, 207, 98, 0.14), inset 0 0 10px rgba(255, 207, 98, 0.06);
    transform: translate(60%, 0);

    &:before {
      position: absolute;
      left: -14px;
      top: calc(50% - 1px);
      width: 14px;
      height: 1px;
      content: "";
      background: linear-gradient(90deg, rgba(255, 221, 117, 0), rgba(255, 221, 117, 0.92));
    }

    &:after {
      position: absolute;
      left: -18px;
      top: calc(50% - 4px);
      width: 8px;
      height: 8px;
      border-radius: 50%;
      content: "";
      background: #fff06a;
      box-shadow: 0 0 12px rgba(255, 240, 106, 0.9);
    }

    .market-title {
      overflow: hidden;
      color: #ffffff;
      font-size: 11px;
      line-height: 14px;
      font-weight: 900;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .market-total {
      display: flex;
      align-items: baseline;
      gap: 4px;
      margin-top: 3px;

      span {
        color: #ffffff;
        font-size: 15px;
        line-height: 18px;
        font-weight: 900;
        text-shadow: 0 0 10px rgba(255, 221, 117, 0.6);
      }

      em {
        color: rgba(255, 255, 255, 0.76);
        font-size: 10px;
        line-height: 12px;
        font-style: normal;
      }
    }
  }

  .trade-flow-card {
    position: relative;
    width: 96px;
    padding: 5px 7px 5px;
    border: 1px solid rgba(85, 226, 255, 0.32);
    border-radius: 3px;
    background:
      linear-gradient(105deg, rgba(4, 37, 50, 0.58), rgba(3, 17, 27, 0.48)),
      rgba(1, 17, 27, 0.46);
    box-shadow: 0 0 8px rgba(48, 220, 255, 0.12), inset 0 0 8px rgba(39, 240, 192, 0.05);
    transform: translate(60%, 0);

    &.is-left {
      transform: translate(-66%, 0);

      &:before {
        right: -10px;
        left: auto;
        background: linear-gradient(90deg, rgba(85, 226, 255, 0.84), rgba(85, 226, 255, 0));
      }

      &:after {
        right: -14px;
        left: auto;
      }
    }

    &.is-top {
      transform: translate(0, -108%);

      &:before {
        left: 50%;
        top: auto;
        bottom: -10px;
        width: 1px;
        height: 10px;
        background: linear-gradient(180deg, rgba(85, 226, 255, 0.84), rgba(85, 226, 255, 0));
      }

      &:after {
        left: calc(50% - 3px);
        top: auto;
        bottom: -14px;
      }
    }

    &.is-bottom {
      transform: translate(0, 82%);

      &:before {
        left: 50%;
        top: -10px;
        width: 1px;
        height: 10px;
        background: linear-gradient(180deg, rgba(85, 226, 255, 0), rgba(85, 226, 255, 0.84));
      }

      &:after {
        left: calc(50% - 3px);
        top: -14px;
      }
    }

    &:before {
      position: absolute;
      left: -10px;
      top: 50%;
      width: 10px;
      height: 1px;
      content: "";
      background: linear-gradient(90deg, rgba(85, 226, 255, 0), rgba(85, 226, 255, 0.84));
    }

    &:after {
      position: absolute;
      left: -14px;
      top: calc(50% - 3px);
      width: 6px;
      height: 6px;
      border-radius: 50%;
      content: "";
      background: #30dcff;
      box-shadow: 0 0 10px rgba(48, 220, 255, 0.8);
    }

    .flow-head {
      display: flex;
      align-items: center;
      gap: 6px;
      min-width: 0;

      span {
        flex: 0 0 auto;
        color: #ffe076;
        font-size: 10px;
        line-height: 12px;
        font-weight: 900;
      }

      strong {
        overflow: hidden;
        color: #ffffff;
        font-size: 10px;
        line-height: 13px;
        font-weight: 800;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }

    .flow-body {
      display: flex;
      align-items: baseline;
      gap: 3px;
      margin-top: 2px;

      em {
        color: #ffffff;
        font-size: 13px;
        line-height: 15px;
        font-style: normal;
        font-weight: 900;
        text-shadow: 0 0 10px rgba(48, 220, 255, 0.55);
      }

      b {
        color: rgba(255, 255, 255, 0.76);
        font-size: 10px;
        line-height: 12px;
      }
    }

  }

  .china-label {
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    text-shadow: 0 0 8px rgba(126, 251, 246, 0.8);
    will-change: transform;
    z-index: 30;
    .other-label {
      display: flex;
      align-items: center;
      padding: 6px 10px;
      border: 1px solid rgba(126, 251, 246, 0.35);
      border-radius: 3px;
      background: rgba(3, 21, 33, 0.78);
      box-shadow: 0 0 14px rgba(48, 220, 255, 0.2);
      will-change: transform;
      white-space: nowrap;
    }
    .town-stat-label {
      flex-direction: column;
      align-items: center;
      gap: 4px;
      min-width: 74px;
      padding: 5px 8px 4px;
      border-color: rgba(126, 251, 246, 0.3);
      background: linear-gradient(180deg, rgba(4, 33, 46, 0.66), rgba(3, 20, 32, 0.56));
      box-shadow: 0 0 10px rgba(48, 220, 255, 0.12);
      text-align: center;
      transform: translateY(-50%);
    }
    .town-name-row {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      min-height: 18px;
      line-height: 18px;
    }
    .town-name {
      color: #ffffff;
      font-size: 13px;
      line-height: 18px;
      font-weight: 800;
    }
    .town-rank {
      display: none;
      flex: 0 0 auto;
      padding: 1px 5px;
      border: 1px solid rgba(126, 251, 246, 0.32);
      border-radius: 2px;
      color: #7efbf6;
      font-size: 10px;
      line-height: 13px;
      font-weight: 700;
      text-shadow: none;
      background: rgba(126, 251, 246, 0.1);
    }
    .town-stat-row {
      display: none;
      align-items: baseline;
      justify-content: center;
      gap: 3px;
      min-height: 18px;
      line-height: 18px;
    }
    .town-stat-metric {
      max-width: 72px;
      overflow: hidden;
      color: rgba(126, 251, 246, 0.9);
      font-size: 10px;
      line-height: 12px;
      font-weight: 700;
      text-overflow: ellipsis;
      text-shadow: none;
      white-space: nowrap;
    }
    .town-stat-value {
      color: #ffffff;
      font-size: 18px;
      line-height: 18px;
      font-weight: 800;
      text-shadow: 0 0 10px rgba(126, 251, 246, 0.72);
    }
    .town-stat-unit {
      color: rgba(232, 252, 255, 0.76);
      font-size: 10px;
      line-height: 12px;
      font-weight: 600;
    }
    .town-stat-label.has-stat {
      min-width: 108px;

      .town-rank,
      .town-stat-row {
        display: flex;
      }
    }

    &.blur {
      filter: none;
      opacity: 1;
    }
    .label-icon {
      display: none;
      width: 20px;
      height: 20px;
      margin: 0 10px 0 0;
    }
  }
  .map-label {
    padding: 5px;
    color: #fff;
    will-change: transform;
    font-size: 36px;
    font-weight: bold;
    letter-spacing: 4.5px;
    -webkit-box-reflect: below 0 -webkit-linear-gradient(transparent, transparent
          20%, rgba(255, 255, 255, 0.3));
    .other-label {
      display: flex;
      flex-direction: column;
    }
    span {
      font-size: 46px;
      &:last-child {
        font-size: 12px;
        font-weight: normal;
        letter-spacing: 0px;
        color: #a7d5ef;
      }
    }
  }
  .decoration-label {
    // &.reflect {
    //   -webkit-box-reflect: below 0 -webkit-linear-gradient(transparent, transparent 20%, rgba(255, 255, 255, 0.3));
    // }
    // padding-bottom: 10px;
    .label-icon {
      display: block;
      width: 40px;
      height: 40px;
    }
  }
  .other-label {
    transform: translateY(200%);
    opacity: 0;
    background: none;
    will-change: transform;
  }
}
</style>
