<template>
  <div class="yak-industry-chain">
    <map-scene
      ref="mapSceneRef"
      class="industry-map-scene"
      :active-layer="industryMapLayer"
      :map-ranking="mapRanking"
      :admin-boundary-line-scale="2.4"
      @feature-info="handleMapFeatureInfo"
    />
    <div class="industry-map-scrim"></div>
    <div v-if="!mapReady" class="industry-map-loading">3D地图加载中</div>

    <div v-if="leftStagePanels.length" class="fgw-left industry-side-panel industry-side-panel--left">
      <div class="fgw-side-stack industry-side-stack is-left">
        <m-card
          v-for="panel in leftStagePanels"
          :key="panel.key"
          class="fgw-side-card industry-side-card left-card"
          :title="panel.title"
          :width="sideCardWidth"
          :height="panel.height"
        >
          <v-chart
            v-if="isTownColumnPanel(panel)"
            class="industry-town-column-chart"
            :option="createTownColumnOption(panel)"
            :autoresize="true"
          />
          <business-panel-renderer
            v-else-if="panel.renderer === 'business'"
            :panel="panel"
            compact
            @row-select="handleIndustryListSelect"
          />
          <div v-else-if="panel.renderer === 'ledger'" class="industry-ledger-list">
            <div v-for="item in panel.rows" :key="item.key" class="industry-ledger-row">
              <div>
                <strong>{{ item.name }}</strong>
                <span>{{ item.typeName }} / {{ item.town }}</span>
              </div>
              <em>{{ item.valueText }}</em>
            </div>
          </div>
        </m-card>
      </div>
    </div>

    <div v-if="rightStagePanels.length" class="fgw-right industry-side-panel industry-side-panel--right">
      <div class="fgw-side-stack industry-side-stack is-right">
        <m-card
          v-for="panel in rightStagePanels"
          :key="panel.key"
          class="fgw-side-card industry-side-card right-card"
          :title="panel.title"
          :width="sideCardWidth"
          :height="panel.height"
        >
          <v-chart
            v-if="isTownColumnPanel(panel)"
            class="industry-town-column-chart"
            :option="createTownColumnOption(panel)"
            :autoresize="true"
          />
          <business-panel-renderer
            v-else-if="panel.renderer === 'business'"
            :panel="panel"
            compact
            @row-select="handleIndustryListSelect"
          />
          <div v-else-if="panel.renderer === 'ledger'" class="industry-ledger-list">
            <div v-for="item in panel.rows" :key="item.key" class="industry-ledger-row">
              <div>
                <strong>{{ item.name }}</strong>
                <span>{{ item.typeName }} / {{ item.town }}</span>
              </div>
              <em>{{ item.valueText }}</em>
            </div>
          </div>
        </m-card>
      </div>
    </div>

    <div v-if="selectedPoint" class="industry-point-detail" :style="pointDetailStyle">
      <div class="industry-point-detail__head">
        <span>{{ selectedPointTitle }}</span>
        <button type="button" title="关闭" @click="clearSelectedPoint">×</button>
      </div>
      <div class="industry-point-detail__name">{{ selectedPointName }}</div>
      <div class="industry-point-detail__grid">
        <div v-for="row in selectedPointRows" :key="row.label" class="industry-point-detail__row">
          <span>{{ row.label }}</span>
          <strong>{{ row.value }}</strong>
        </div>
      </div>
    </div>

    <div class="industry-bottom-switcher">
      <topic-switcher
        :model-value="activeStageKey"
        :topics="stageSwitcherTopics"
        @update:model-value="handleStageSelect"
      />
    </div>
  </div>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
import VChart from "vue-echarts"
import mCard from "@/components/mCard/index.vue"
import MapScene from "@/views/forestGrassWet/map.vue"
import BusinessPanelRenderer from "@/views/forestGrassWet/components/BusinessPanelRenderer.vue"
import TopicSwitcher from "@/views/forestGrassWet/components/TopicSwitcher.vue"
import { Assets } from "@/views/forestGrassWet/assets"
import { getLayerByKey } from "@/config/layers"
import {
  getStageDef,
  getStagePanelConfig,
  getPointExtraRows,
  industryStageDefs,
  getStageTopStats,
} from "./data"

const props = defineProps({
  active: {
    type: Boolean,
    default: true,
  },
})
const emit = defineEmits(["module-stats", "loading-progress", "map-ready"])
const sideCardWidth = 398
const screenViewportHeight = ref(typeof window === "undefined" ? 1080 : window.innerHeight)
const sidePanelTop = 128
const sidePanelGap = 12
const sidePanelBottomGap = 8
const sidePanelMinHeight = 227
const sidePanelHeight = computed(() => {
  const viewportHeight = Math.max(1080, Number(screenViewportHeight.value) || 1080)
  const fittedHeight = Math.floor((viewportHeight - sidePanelTop - sidePanelBottomGap - sidePanelGap * 3) / 4)
  return Math.max(sidePanelMinHeight, fittedHeight)
})
const sidePanelDoubleHeight = computed(() => sidePanelHeight.value * 2 + sidePanelGap)
const sidePanelFullHeight = computed(() => sidePanelHeight.value * 4 + sidePanelGap * 3)
const activeStageKey = ref("family-pasture")
const mapSceneRef = ref(null)
const mapReady = ref(false)
const selectedPoint = ref(null)
const selectedPointScreen = ref({ x: 0, y: 0 })
let mapAssets = null
let mapAssetsReady = false
let mapLoadStarted = false
let mapLoadTimer = null
let destroyed = false

const activeStage = computed(() => getStageDef(activeStageKey.value))
const activePanelConfig = computed(() => getStagePanelConfig(activeStageKey.value))
const stagePanelGroups = computed(() => normalizeStagePanelGroups(activePanelConfig.value))
const leftStagePanels = computed(() => {
  const panels = stagePanelGroups.value.left
  return panels.map((panel) => normalizeStagePanelHeight(panel, panels.length))
})
const rightStagePanels = computed(() => {
  const panels = stagePanelGroups.value.right
  return panels.map((panel) => normalizeStagePanelHeight(panel, panels.length))
})
const industryMapLayer = computed(() => {
  const layer = getLayerByKey("yak-industry-chain", "yak-industry-points")
  const legend = layer?.legends?.find((item) => item.match?.value === activeStageKey.value)
  if (!layer) return layer
  return {
    ...layer,
    activeLegend: legend || {
      key: `yak-industry-empty-${activeStageKey.value}`,
      name: activeStage.value.name,
      fillColor: activeStage.value.color,
      strokeColor: activeStage.value.color,
      match: { field: "stageKey", value: activeStageKey.value },
    },
  }
})
const stageSwitcherTopics = computed(() =>
  industryStageDefs.map((stage) => ({
    key: stage.key,
    name: stage.name,
    color: stage.color,
    width: stage.width,
  }))
)
const mapRanking = computed(() => ({
  showTownNames: true,
  showBars: false,
  showPointMarkers: true,
  title: `${activeStage.value.name}排行`,
  metricLabel: activeStage.value.metricLabel,
  unit: activeStage.value.unit,
  rows: [],
}))

const selectedPointRows = computed(() => {
  const point = selectedPoint.value
  if (!point) return []
  return [
    { label: "所属环节", value: formatDisplayText(point.stageName) },
    { label: "主体类型", value: formatDisplayText(point.categoryName) },
    { label: "所在乡镇", value: formatDisplayText(point.town) },
    { label: "能力规模", value: formatCapacity(point) },
    { label: "运行状态", value: formatDisplayText(point.status) },
    ...getPointExtraRows(point),
  ].filter((row) => row.value !== undefined && row.value !== null && String(row.value).trim() !== "")
})
const selectedPointTitle = computed(() => formatDisplayText(selectedPoint.value?.categoryName || selectedPoint.value?.stageName))
const selectedPointName = computed(() => formatDisplayText(selectedPoint.value?.name))
const pointDetailStyle = computed(() => {
  const panelWidth = 430
  const panelHeight = 252
  const margin = 24
  const pointerGap = 28
  const point = {
    x: Math.max(margin, Math.min(1920 - margin, Number(selectedPointScreen.value.x || 0))),
    y: Math.max(margin, Math.min(1080 - margin, Number(selectedPointScreen.value.y || 0))),
  }
  const placeLeft = point.x > 1320
  const x = placeLeft
    ? Math.max(margin, point.x - panelWidth - pointerGap)
    : Math.min(1920 - panelWidth - margin, point.x + pointerGap)
  const y = Math.min(Math.max(point.y - panelHeight / 2, 128), 1080 - panelHeight - 116)
  const arrowTop = Math.min(Math.max(point.y - y, 42), panelHeight - 38)
  return {
    left: `${x}px`,
    top: `${y}px`,
    "--point-arrow-top": `${arrowTop}px`,
    "--point-arrow-side": placeLeft ? "100%" : "0px",
    "--point-arrow-offset": placeLeft ? "-1px" : "-9px",
    "--point-arrow-rotate": placeLeft ? "-45deg" : "135deg",
  }
})
onMounted(() => {
  updateScreenViewportHeight()
  window.addEventListener("resize", updateScreenViewportHeight)
  emitIndustryTopStats()
  initIndustryMap()
})

onBeforeUnmount(() => {
  destroyed = true
  window.removeEventListener("resize", updateScreenViewportHeight)
  if (mapLoadTimer) window.clearTimeout(mapLoadTimer)
  mapSceneRef.value?.destroyMap?.()
})

watch(
  () => props.active,
  (active) => {
    if (!mapSceneRef.value) return
    if (active) {
      scheduleIndustryMapLoad()
      mapSceneRef.value.resume?.()
    } else {
      mapSceneRef.value.pause?.()
    }
  },
)

function handleStageSelect(key) {
  if (key === activeStageKey.value) return
  activeStageKey.value = key
  clearSelectedPoint()
  emitIndustryTopStats(key)
}

function normalizeStagePanelGroups(config = {}) {
  const left = filterDisplayPanels(config.left)
  const right = filterDisplayPanels(config.right)
  if (right.length) {
    return { left, right }
  }
  const panels = [...left, ...right]
  if (panels.length <= 4) {
    return { left: panels, right: [] }
  }
  return { left, right }
}

function normalizeStagePanelHeight(panel, groupCount = 0) {
  if (panel?.type === "projectTable") {
    return {
      ...panel,
      height: getProjectTablePanelHeight(panel),
    }
  }
  const isDouble = Number(panel?.height || 0) >= 400 || panel?.span === 2
  return {
    ...panel,
    height: groupCount === 1 ? sidePanelFullHeight.value : isDouble ? sidePanelDoubleHeight.value : sidePanelHeight.value,
  }
}

function getProjectTablePanelHeight(panel = {}) {
  const rows = Array.isArray(panel.rows) ? panel.rows : []
  const headerHeight = 100
  const rowHeight = 32
  const preferredHeight = headerHeight + 25 + rows.length * rowHeight
  return Math.min(sidePanelDoubleHeight.value, Math.max(sidePanelHeight.value, preferredHeight))
}

function filterDisplayPanels(panels = []) {
  return panels.filter((panel) => panel?.type !== "iconMetric")
}

function isTownColumnPanel(panel = {}) {
  return panel?.renderer === "business" && panel?.type === "column" && panel?.variant === "townCompact"
}

function createTownColumnOption(panel = {}) {
  const rows = Array.isArray(panel.rows) ? panel.rows : []
  const values = rows.map((item) => Number(item?.zero ? 0 : item?.value || 0))
  const maxValue = Math.max(...values, 1)
  const unit = getSharedUnit(rows)
  return {
    animationDuration: 720,
    animationEasing: "cubicOut",
    grid: {
      top: 30,
      right: 8,
      bottom: 58,
      left: 8,
      containLabel: false,
    },
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "shadow",
        shadowStyle: {
          color: "rgba(48, 220, 255, 0.08)",
        },
      },
      confine: true,
      backgroundColor: "rgba(4, 24, 36, 0.94)",
      borderColor: "rgba(48, 220, 255, 0.42)",
      borderWidth: 1,
      padding: [7, 9],
      textStyle: {
        color: "#e8fcff",
        fontSize: 12,
      },
      formatter(params = []) {
        const data = params[0]?.data || {}
        const value = data.zero ? "0" : data.displayValue
        const valueUnit = data.unit || unit || ""
        const extra = data.extra ? `<br/><span style="color:rgba(232,252,255,.62)">${data.extra}</span>` : ""
        return `${data.name || ""}<br/><b style="color:#35f1ff">${value}${valueUnit}</b>${extra}`
      },
    },
    graphic: unit
      ? [{
          type: "text",
          right: 10,
          top: 4,
          silent: true,
          style: {
            text: `单位：${unit}`,
            fill: "rgba(232,252,255,0.68)",
            font: "10px PingFang SC, Arial",
          },
        }]
      : [],
    xAxis: {
      type: "category",
      data: rows.map((item) => item?.name || ""),
      axisTick: { show: false },
      axisLine: {
        show: true,
        lineStyle: {
          color: "rgba(68, 238, 255, 0.42)",
        },
      },
      axisLabel: {
        color: "rgba(255,255,255,0.9)",
        fontSize: 11,
        fontWeight: 700,
        lineHeight: 14,
        interval: 0,
        margin: 10,
        formatter: formatTownAxisLabel,
      },
    },
    yAxis: {
      type: "value",
      min: 0,
      max: Math.ceil(maxValue * 1.18),
      splitNumber: 3,
      axisLabel: { show: false },
      axisTick: { show: false },
      axisLine: { show: false },
      splitLine: {
        show: true,
        lineStyle: {
          color: "rgba(159, 220, 230, 0.14)",
          type: "dashed",
        },
      },
    },
    series: [{
      type: "bar",
      data: rows.map((item, index) => {
        const zero = Boolean(item?.zero || item?.isZero)
        const value = zero ? 0 : Number(item?.value || 0)
        return {
          name: item?.name,
          value,
          displayValue: item?.displayValue ?? formatChartNumber(value),
          unit: item?.unit || unit || "",
          extra: item?.extra || "",
          zero,
          itemStyle: {
            color: zero ? "rgba(48, 220, 255, 0.1)" : townColumnColor(index, item),
          },
        }
      }),
      barWidth: 14,
      barMinHeight: 3,
      showBackground: true,
      backgroundStyle: {
        color: "rgba(48, 220, 255, 0.06)",
        borderColor: "rgba(48, 220, 255, 0.08)",
        borderWidth: 1,
      },
      itemStyle: {
        borderRadius: [4, 4, 0, 0],
      },
      label: {
        show: false,
      },
      emphasis: {
        focus: "series",
        itemStyle: {
          shadowBlur: 14,
          shadowColor: "rgba(36, 246, 196, 0.42)",
        },
      },
    }],
  }
}

function getSharedUnit(rows = []) {
  const units = [...new Set(rows.map((item) => item?.unit).filter(Boolean))]
  return units.length === 1 ? units[0] : ""
}

function formatTownAxisLabel(name) {
  const text = String(name || "").replace(/\s+/g, "")
  return Array.from(text).slice(0, 3).join("\n")
}

function townColumnColor(index, item = {}) {
  if (item.color || item.fillColor) return item.color || item.fillColor
  const palette = [
    ["#35f1ff", "#13aebd"],
    ["#5cf5a6", "#18b978"],
    ["#f6d663", "#c69c28"],
    ["#7aa3ff", "#3a65d8"],
    ["#ff8b62", "#c25d3d"],
  ]
  const [top, bottom] = palette[index % palette.length]
  return {
    type: "linear",
    x: 0,
    y: 0,
    x2: 0,
    y2: 1,
    colorStops: [
      { offset: 0, color: "rgba(255,255,255,0.96)" },
      { offset: 0.14, color: top },
      { offset: 0.72, color: bottom },
      { offset: 1, color: "rgba(20, 244, 220, 0.28)" },
    ],
  }
}

function formatChartNumber(value) {
  const number = Number(value)
  if (!Number.isFinite(number)) return "--"
  return Number.isInteger(number) ? `${number}` : `${Number(number.toFixed(2))}`
}

function updateScreenViewportHeight() {
  screenViewportHeight.value = typeof window === "undefined" ? 1080 : window.innerHeight
}

function emitIndustryTopStats(stageKey = activeStageKey.value) {
  emit("module-stats", {
    moduleKey: "yak-industry-chain",
    items: getStageTopStats(stageKey),
  })
}

function initIndustryMap() {
  mapAssets = new Assets()
  mapAssetsReady = false
  emit("loading-progress", { progress: 0 })
  mapAssets.instance?.on?.("onProgress", (path, itemsLoaded, itemsTotal) => {
    const total = itemsTotal || 1
    emit("loading-progress", {
      progress: Math.floor((itemsLoaded / total) * 100),
      path,
      itemsLoaded,
      itemsTotal,
    })
  })
  mapAssets.ready?.then(() => {
    mapAssetsReady = true
    scheduleIndustryMapLoad()
  }).catch(() => {
    mapReady.value = true
    emit("map-ready")
  })
}

function scheduleIndustryMapLoad() {
  if (destroyed || !props.active || !mapAssetsReady || mapLoadStarted) return
  mapLoadStarted = true
  if (mapLoadTimer) window.clearTimeout(mapLoadTimer)
  mapLoadTimer = window.setTimeout(() => {
    mapLoadTimer = null
    loadIndustryMapOnce()
  }, 600)
}

async function loadIndustryMapOnce(attempt = 0) {
  if (destroyed) return
  if (!props.active) {
    mapLoadStarted = false
    return
  }
  await nextTick()
  if (!props.active) {
    mapLoadStarted = false
    return
  }
  const scene = mapSceneRef.value
  scene?.loadMap?.(mapAssets)
  if (scene?.canvasMap) {
    mapReady.value = true
    emit("loading-progress", { progress: 100 })
    emit("map-ready")
    scene.play?.()
    return
  }
  if (attempt < 24) {
    window.setTimeout(() => loadIndustryMapOnce(attempt + 1), 120)
    return
  }
  mapLoadStarted = false
  mapReady.value = true
  emit("map-ready")
}

function clearIndustryMapLoadTimer() {
  if (!mapLoadTimer) return
  window.clearTimeout(mapLoadTimer)
  mapLoadTimer = null
}

function refreshMapView() {
  mapSceneRef.value?.resume?.()
  mapSceneRef.value?.refreshView?.()
}

function resetMap() {
  clearSelectedPoint()
  if (mapSceneRef.value?.hasMap?.()) {
    mapReady.value = true
    refreshMapView()
    return
  }
  clearIndustryMapLoadTimer()
  mapReady.value = false
  mapLoadStarted = false
  nextTick(() => {
    scheduleIndustryMapLoad()
  })
}

defineExpose({
  refreshMapView,
  resetMap,
})

function handleMapFeatureInfo(payload = {}) {
  const properties = payload.feature?.properties || {}
  if (properties.stageKey) {
    if (String(properties.stageKey) !== String(activeStageKey.value)) {
      selectedPoint.value = null
      return
    }
    selectedPoint.value = properties
    selectedPointScreen.value = normalizeFeatureScreen(payload.screen)
    return
  }
  selectedPoint.value = null
}

async function handleIndustryListSelect(row = {}) {
  if (!row.canLocate) return
  const stageChanged = Boolean(row.stageKey && row.stageKey !== activeStageKey.value)
  if (stageChanged) {
    activeStageKey.value = row.stageKey
    emitIndustryTopStats(row.stageKey)
    await nextTick()
  }
  const point = buildListPoint(row)
  selectedPoint.value = point
  selectedPointScreen.value = { x: 960, y: 500 }
  window.setTimeout(() => {
    const focusResult = mapSceneRef.value?.focusYakIndustryPoint?.(point)
    syncSelectedPointScreen(point, focusResult)
    window.setTimeout(() => syncSelectedPointScreen(point), 760)
  }, stageChanged ? 320 : 80)
}

function clearSelectedPoint() {
  selectedPoint.value = null
  const canvasMap = mapSceneRef.value?.canvasMap?.value || mapSceneRef.value?.canvasMap
  canvasMap?.clearSelectedFeature?.()
}

function buildListPoint(row = {}) {
  const longitude = Number(row.longitude)
  const latitude = Number(row.latitude)
  return {
    ...row,
    id: row.key || row.id,
    name: row.fullName || row.name,
    stageKey: row.stageKey,
    stageName: row.stageName || getStageDef(row.stageKey)?.name,
    categoryName: row.categoryName || row.typeName,
    capacity: row.value,
    capacityUnit: row.unit,
    longitude,
    latitude,
    geometry: Number.isFinite(longitude) && Number.isFinite(latitude)
      ? { type: "Point", coordinates: [longitude, latitude] }
      : undefined,
    status: row.status,
  }
}

function syncSelectedPointScreen(point, focusResult = null) {
  const screen = mapSceneRef.value?.getYakIndustryPointScreen?.(point) || focusResult?.screen
  if (screen) selectedPointScreen.value = normalizeFeatureScreen(screen)
}

function formatCapacity(point) {
  if (point.capacity === undefined || point.capacity === null || point.capacity === "") return ""
  return `${point.capacity}${point.capacityUnit || ""}`
}

function formatDisplayText(value) {
  return String(value ?? "")
    .replace(/\bcxpt[-_./\s]*/gi, "")
    .replace(/\s+/g, " ")
    .trim()
}

function normalizeFeatureScreen(screen = {}) {
  const root = document.querySelector("#large-screen")
  const rect = root?.getBoundingClientRect?.()
  const scaleX = rect?.width ? 1920 / rect.width : 1
  const scaleY = rect?.height ? 1080 / rect.height : 1
  const left = rect?.left || 0
  const top = rect?.top || 0
  return {
    x: (Number(screen.x) - left) * scaleX,
    y: (Number(screen.y) - top) * scaleY,
  }
}

</script>

<style lang="scss">
.yak-industry-chain {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  color: #fff;
  background: #03121c;
}

.industry-map-scene {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.industry-map-scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 48%, rgba(4, 18, 28, 0) 0%, rgba(4, 18, 28, 0.05) 37%, rgba(2, 10, 17, 0.34) 78%, rgba(1, 7, 12, 0.62) 100%),
    linear-gradient(90deg, rgba(0, 12, 20, 0.42), transparent 26%, transparent 74%, rgba(0, 12, 20, 0.42));
}

.industry-map-loading {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 4;
  transform: translate(-50%, -50%);
  color: rgba(220, 250, 255, 0.78);
  font-size: 18px;
}

.industry-side-panel {
  position: absolute;
  top: 128px;
  bottom: 8px;
  z-index: 6;
  width: 398px;
  perspective: 700px;
  perspective-origin: 50% 50%;
  pointer-events: all;
}

.industry-side-panel--left {
  left: 32px;
}

.industry-side-panel--right {
  right: 32px;
}

.industry-side-stack {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  transform-style: preserve-3d;

  &.is-left {
    transform: translate3d(0, 0, 0) rotateY(6deg);
    transform-origin: left center;
  }

  &.is-right {
    transform: translate3d(0, 0, 0) rotateY(-6deg);
    transform-origin: right center;
  }
}

.industry-side-card {
  flex: 0 0 auto;

  .m-card-bd-content {
    top: 40px;
    bottom: 10px;
    color: #f3feff;
    overflow: hidden;
  }

  .m-card-bd-bg {
    filter: brightness(1.08) saturate(1.04);
  }

  .m-card-bd-bg::after {
    content: "";
    position: absolute;
    inset: 4px 5px 6px;
    z-index: 0;
    pointer-events: none;
    border: 1px solid rgba(48, 220, 255, 0.14);
    background:
      linear-gradient(180deg, rgba(17, 73, 92, 0.16), rgba(6, 29, 44, 0.14)),
      linear-gradient(90deg, rgba(48, 220, 255, 0.1), transparent 42%, rgba(48, 220, 255, 0.06));
    box-shadow:
      inset 0 0 0 1px rgba(48, 220, 255, 0.06),
      inset 0 12px 28px rgba(48, 220, 255, 0.04);
  }

  .industry-town-column-chart {
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
    min-height: 0;
  }

  .business-columns.is-variant-plain {
    grid-template-columns: repeat(auto-fit, minmax(34px, 1fr));
    gap: 8px;
    padding-right: 12px;
    padding-left: 12px;
  }

  .business-columns.is-variant-plain .business-column-item {
    grid-template-rows: 16px minmax(62px, 1fr) 32px;
  }

  .business-columns.is-variant-plain .business-column-item span {
    display: -webkit-box;
    max-width: 58px;
    color: rgba(240, 253, 255, 0.86);
    line-height: 13px;
    white-space: normal;
    word-break: keep-all;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }

  .business-columns.is-variant-plain .business-column-track {
    width: 18px;
  }

  .business-columns.is-variant-townCompact {
    grid-template-columns: repeat(10, minmax(0, 1fr));
    gap: 5px;
    padding: 35px 13px 10px;
  }

  .business-columns.is-variant-townCompact::before {
    left: 13px;
    right: 13px;
    top: 55px;
    bottom: 40px;
    opacity: 0.46;
    background:
      linear-gradient(to bottom, transparent calc(100% - 1px), rgba(48, 220, 255, 0.58) calc(100% - 1px)),
      repeating-linear-gradient(
        to bottom,
        rgba(159, 220, 230, 0.16) 0 1px,
        transparent 1px 21px
      );
  }

  .business-columns.is-variant-townCompact::after {
    content: "";
    position: absolute;
    left: 13px;
    right: 13px;
    bottom: 39px;
    height: 1px;
    pointer-events: none;
    background: linear-gradient(90deg, transparent, rgba(60, 244, 255, 0.72), transparent);
    box-shadow: 0 0 10px rgba(48, 220, 255, 0.24);
  }

  .business-columns.is-variant-townCompact .business-column-item {
    position: relative;
    z-index: 1;
    grid-template-rows: 17px minmax(74px, 1fr) 29px;
    min-width: 0;
  }

  .business-columns.is-variant-townCompact .business-column-item strong {
    width: 100%;
    color: rgba(244, 252, 255, 0.94);
    font-size: 10px;
    line-height: 14px;
    text-align: center;
    text-shadow: 0 0 8px rgba(255, 255, 255, 0.18);
  }

  .business-columns.is-variant-townCompact .business-column-item span {
    display: -webkit-box;
    align-self: start;
    width: 100%;
    max-width: 36px;
    height: 25px;
    margin-top: 3px;
    color: rgba(232, 252, 255, 0.82);
    font-size: 10px;
    line-height: 12px;
    text-align: center;
    white-space: normal;
    word-break: break-all;
    writing-mode: horizontal-tb;
    text-orientation: initial;
    letter-spacing: 0;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }

  .business-columns.is-variant-townCompact .business-column-track {
    width: 24px;
    height: 100%;
    min-height: 74px;
    align-self: end;
    border: 0;
    border-radius: 2px 2px 0 0;
    background:
      linear-gradient(180deg, rgba(48, 220, 255, 0.12), rgba(36, 246, 196, 0.015)),
      linear-gradient(90deg, rgba(48, 220, 255, 0.1), rgba(48, 220, 255, 0.02) 48%, rgba(48, 220, 255, 0.1)),
      rgba(4, 26, 38, 0.22);
    box-shadow:
      inset 0 -1px 0 rgba(48, 220, 255, 0.2),
      inset 0 0 10px rgba(48, 220, 255, 0.06);
  }

  .business-columns.is-variant-townCompact .business-column-track::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: -5px;
    width: 34px;
    height: 7px;
    border-radius: 50%;
    background: radial-gradient(ellipse at center, rgba(36, 246, 196, 0.28), rgba(36, 246, 196, 0) 68%);
    transform: translateX(-50%);
  }

  .business-columns.is-variant-townCompact .business-column-track i {
    left: 4px;
    right: 4px;
    min-height: 5px;
    border-radius: 3px 3px 0 0;
    box-shadow:
      0 0 12px rgba(36, 246, 196, 0.32),
      inset 0 1px 0 rgba(255, 255, 255, 0.26);
  }

  .business-columns.is-variant-townCompact .business-column-track i::before,
  .business-columns.is-variant-townCompact .business-column-track i::after {
    display: none;
  }

  .business-columns.is-variant-townCompact .business-column-item.is-zero strong {
    opacity: 0;
  }

  .business-columns.is-variant-townCompact .business-column-item.is-zero .business-column-track i {
    height: 4px !important;
    opacity: 0.16;
    box-shadow: none;
  }

  .business-bars.is-rank-bar {
    padding-right: 12px;
    padding-left: 12px;
  }

  .business-bars.is-rank-bar .business-bar-name {
    font-size: 12px;
    line-height: 14px;
  }
}

.industry-ledger-list {
  display: grid;
  gap: 7px;
}

.industry-ledger-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  min-height: 42px;
  padding: 7px 8px;
  border: 1px solid rgba(48, 220, 255, 0.1);
  border-radius: 4px;
  background: rgba(4, 20, 31, 0.42);

  div {
    min-width: 0;
  }

  strong,
  span {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  strong {
    color: rgba(241, 254, 255, 0.92);
    font-size: 13px;
    font-weight: 760;
  }

  span {
    margin-top: 4px;
    color: rgba(201, 237, 244, 0.58);
    font-size: 11px;
  }

  em {
    color: #ffe06b;
    font-size: 12px;
    font-style: normal;
    font-weight: 800;
    white-space: nowrap;
  }
}

.industry-point-detail {
  position: absolute;
  z-index: 7;
  width: 430px;
  padding: 18px 20px 20px;
  border: 1px solid rgba(48, 220, 255, 0.42);
  background:
    linear-gradient(180deg, rgba(12, 54, 70, 0.86), rgba(5, 24, 37, 0.9)),
    radial-gradient(circle at 20% 0%, rgba(36, 246, 196, 0.18), transparent 46%);
  box-shadow:
    0 0 24px rgba(48, 220, 255, 0.18),
    inset 0 0 18px rgba(48, 220, 255, 0.08);
  color: #efffff;
  pointer-events: all;
}

.industry-point-detail::after {
  content: "";
  position: absolute;
  left: var(--point-arrow-side, 0);
  top: var(--point-arrow-top, 50%);
  width: 16px;
  height: 16px;
  border-right: 1px solid rgba(48, 220, 255, 0.42);
  border-bottom: 1px solid rgba(48, 220, 255, 0.42);
  background: rgba(5, 24, 37, 0.9);
  transform: translate(var(--point-arrow-offset, -9px), -50%) rotate(var(--point-arrow-rotate, 135deg));
}

.industry-point-detail__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  color: #ffe06b;
  font-size: 16px;
  font-weight: 700;

  button {
    width: 26px;
    height: 26px;
    border: 1px solid rgba(48, 220, 255, 0.36);
    border-radius: 0;
    background: rgba(6, 28, 43, 0.72);
    color: rgba(239, 255, 255, 0.88);
    cursor: pointer;
    font-size: 20px;
    line-height: 20px;
  }
}

.industry-point-detail__name {
  margin-top: 12px;
  color: #ffffff;
  font-size: 22px;
  font-weight: 800;
  line-height: 1.35;
}

.industry-point-detail__grid {
  display: grid;
  gap: 8px;
  margin-top: 14px;
}

.industry-point-detail__row {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 12px;
  min-height: 32px;
  align-items: center;
  border-bottom: 1px solid rgba(48, 220, 255, 0.12);

  span {
    color: rgba(196, 237, 244, 0.74);
    font-size: 14px;
  }

  strong {
    overflow: hidden;
    color: rgba(246, 255, 255, 0.95);
    font-size: 15px;
    font-weight: 700;
    line-height: 1.35;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.industry-bottom-switcher {
  position: absolute;
  left: 50%;
  bottom: 48px;
  z-index: 8;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: translateX(-50%);
  pointer-events: all;

  .topic-switcher {
    width: 1060px;
  }

  .topic-switcher-item {
    width: var(--topic-item-width, 112px);
  }

  .topic-switcher-item .topic-name {
    font-size: 14px;
  }

  .topic-switcher-item .topic-title {
    font-size: 14px;
    line-height: 16px;
  }

  .topic-switcher-item .topic-name small {
    font-size: 10px;
    line-height: 11px;
  }
}
</style>
