<template>
  <div class="map-container">
    <div id="map"></div>

    <div class="map-title">大唐双龙传 - 势力分布地图</div>

    <!-- 势力图例 -->
    <div class="legend">
      <h3>势力图例</h3>
      <div
        v-for="faction in mergedFactions"
        :key="faction.id"
        class="legend-item"
        @click="showFactionInfo(faction)"
      >
        <div class="legend-dot" :style="{ background: faction.color }"></div>
        <span>{{ faction.name }}（{{ faction.leader }}）</span>
      </div>

      <div class="legend-section">
        <h3>人物路线</h3>
        <div
          v-for="route in visibleRouteDefs"
          :key="route.character"
          class="legend-item"
          @click="toggleRoute(route)"
        >
          <div
            class="legend-line"
            :style="{
              background: route.color,
              opacity: selectedRoutes.includes(route.character) ? 1 : 0.4
            }"
          ></div>
          <span :style="{ opacity: selectedRoutes.includes(route.character) ? 1 : 0.5 }">{{ route.character }}</span>
        </div>
      </div>
    </div>

    <!-- 卷册进度面板 -->
    <div class="chapter-panel">
      <h3>卷册进度</h3>
      <div class="vol-slider-wrap">
        <input
          type="range"
          class="chapter-slider"
          v-model.number="currentVol"
          :min="1"
          :max="40"
          @input="onVolSliderChange"
        >
        <div class="vol-ticks">
          <span
            v-for="tick in volTicks"
            :key="tick.vol"
            class="tick-label"
            :style="{ left: tick.pct + '%' }"
          >{{ tick.label }}</span>
        </div>
      </div>
      <div class="vol-info">
        当前：{{ currentVolLabel }} &nbsp;|&nbsp; 已加载 {{ loadedCount }} / 40 卷
      </div>

      <!-- 关键事件列表 -->
      <div class="events-section" v-if="currentKeyEvents.length">
        <div class="events-title">本卷关键事件</div>
        <div
          v-for="(ev, idx) in currentKeyEvents"
          :key="idx"
          class="event-item"
          @click="flyToEvent(ev)"
        >
          <span class="event-ch">第{{ ev.chapter }}章</span>
          <span class="event-text">{{ ev.event }}</span>
        </div>
      </div>
    </div>

    <!-- 势力详情面板 -->
    <div class="info-panel" v-if="selectedFaction">
      <span class="close-btn" @click="selectedFaction = null">&times;</span>
      <h3 :style="{ color: selectedFaction.color }">{{ selectedFaction.name }}</h3>
      <p><b>首领：</b>{{ selectedFaction.leader }}</p>
      <p>{{ selectedFaction.description }}</p>
      <p v-if="selectedFaction.territory && selectedFaction.territory.length">
        <b>据点：</b>{{ getTerritoryNames(selectedFaction.territory).join('、') }}
      </p>
      <p v-if="selectedFaction.key_figures && selectedFaction.key_figures.length">
        <b>关键人物：</b>{{ selectedFaction.key_figures.join('、') }}
      </p>
    </div>

    <!-- 加载进度条 -->
    <div class="loading-bar" v-if="isLoading">
      <div class="loading-inner" :style="{ width: (loadedCount / 40 * 100) + '%' }"></div>
      <span class="loading-text">加载数据中… {{ loadedCount }}/40 卷</span>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'

// ── 常量 ────────────────────────────────────────────────────
const TOTAL_VOLS = 40
const SPLIT_VOL  = 21   // 从该卷起寇仲/徐子陵分离

// 合并路线名称（卷 1-20 使用）
const MERGED_NAME  = '寇仲 & 徐子陵'
const MERGED_COLOR = '#FF6347'

// 分离路线定义（卷 21+ 使用）
const KOUZHONG_NAME  = '寇仲'
const KOUZHONG_COLOR = '#FF4500'
const XUZILING_NAME  = '徐子陵'
const XUZILING_COLOR = '#FFA07A'

// ── 响应式状态 ──────────────────────────────────────────────
const allData       = ref([])   // 原始卷数据，index 0 = vol01
const currentVol    = ref(1)    // 1-40
const selectedRoutes = ref([])
const selectedFaction = ref(null)
const isLoading     = ref(true)
const loadedCount   = ref(0)

// ── Leaflet 图层管理 ────────────────────────────────────────
let map = null
let layerGroups = {
  territories: null,
  locations:   null,
  routes:      null,
  events:      null
}

// ── 滑块刻度（每10卷一个）──────────────────────────────────
// 滑块范围 1-40，共 39 步
// pct = (vol - 1) / (40 - 1) * 100
const volTicks = [
  { vol: 1,  label: '卷一',   pct: 0                     },
  { vol: 10, label: '卷十',   pct: (9  / 39 * 100).toFixed(3) * 1 },
  { vol: 20, label: '卷二十', pct: (19 / 39 * 100).toFixed(3) * 1 },
  { vol: 30, label: '卷三十', pct: (29 / 39 * 100).toFixed(3) * 1 },
  { vol: 40, label: '卷四十', pct: 100                   }
]

// ── 计算属性：当前卷标签 ────────────────────────────────────
const currentVolLabel = computed(() => {
  const d = allData.value[currentVol.value - 1]
  return d ? d.volume : `卷${currentVol.value}`
})

// ── 跨卷合并数据（截至 currentVol）──────────────────────────

// 地点：按 id 去重，取最新卷定义
const mergedLocations = computed(() => {
  const locMap = new Map()
  for (let i = 0; i < currentVol.value; i++) {
    const d = allData.value[i]
    if (!d) continue
    for (const loc of (d.locations || [])) {
      locMap.set(loc.id, loc)
    }
  }
  return Array.from(locMap.values())
})

// 势力：按 id 去重，取最新卷定义
const mergedFactions = computed(() => {
  const facMap = new Map()
  for (let i = 0; i < currentVol.value; i++) {
    const d = allData.value[i]
    if (!d) continue
    for (const f of (d.factions || [])) {
      facMap.set(f.id, f)
    }
  }
  return Array.from(facMap.values())
})

/**
 * 合并人物路线，处理寇仲/徐子陵在 vol21 前后的分合逻辑：
 *
 * 卷 1-20：数据中是 "寇仲 & 徐子陵"，直接按名合并。
 *
 * 卷 21+（当 currentVol >= SPLIT_VOL）：
 *   - 数据中是独立的 "寇仲" 和 "徐子陵"。
 *   - 前20卷的 "寇仲 & 徐子陵" 路线同时挂到这两条线上，
 *     这样路线就连续（两人共同行走的历史 → 各自后续独立路线）。
 *
 * 当 currentVol < SPLIT_VOL：只展示合并路线。
 * 当 currentVol >= SPLIT_VOL：只展示分离路线，前20卷合并段分别并入两人。
 */
const mergedRoutes = computed(() => {
  const isSplit = currentVol.value >= SPLIT_VOL

  if (!isSplit) {
    // ── 模式一：合并路线（卷 1-20）──────────────────────────
    const charMap = new Map()
    for (let i = 0; i < currentVol.value; i++) {
      const d = allData.value[i]
      if (!d) continue
      const vol = i + 1
      for (const r of (d.character_routes || [])) {
        const name = r.character
        if (!charMap.has(name)) {
          charMap.set(name, { character: name, color: r.color, route: [] })
        }
        const entry = charMap.get(name)
        entry.color = r.color
        for (const pt of (r.route || [])) {
          entry.route.push({ ...pt, vol })
        }
      }
    }
    return Array.from(charMap.values())
  } else {
    // ── 模式二：分离路线（卷 21+）───────────────────────────
    // 先收集前20卷的合并路线路点
    const earlyMergedPts = []
    for (let i = 0; i < SPLIT_VOL - 1; i++) {
      const d = allData.value[i]
      if (!d) continue
      const vol = i + 1
      for (const r of (d.character_routes || [])) {
        if (r.character === MERGED_NAME) {
          for (const pt of (r.route || [])) {
            earlyMergedPts.push({ ...pt, vol })
          }
        }
      }
    }

    // 再按角色名合并 vol21+ 的独立路线
    const splitMap = new Map()

    // 初始化两条分离路线，并把早期合并路点作为前缀
    const initSplitRoute = (name, color) => {
      splitMap.set(name, {
        character: name,
        color,
        route: [...earlyMergedPts]  // 共享早期合并路点
      })
    }
    initSplitRoute(KOUZHONG_NAME,  KOUZHONG_COLOR)
    initSplitRoute(XUZILING_NAME,  XUZILING_COLOR)

    // 收集其他角色（非寇仲/徐子陵）在 vol21+ 中的路线
    const otherMap = new Map()

    for (let i = SPLIT_VOL - 1; i < currentVol.value; i++) {
      const d = allData.value[i]
      if (!d) continue
      const vol = i + 1
      for (const r of (d.character_routes || [])) {
        const name = r.character
        if (name === KOUZHONG_NAME || name === XUZILING_NAME) {
          const entry = splitMap.get(name)
          entry.color = r.color
          for (const pt of (r.route || [])) {
            entry.route.push({ ...pt, vol })
          }
        } else {
          // 其他角色正常合并
          if (!otherMap.has(name)) {
            otherMap.set(name, { character: name, color: r.color, route: [] })
          }
          const entry = otherMap.get(name)
          entry.color = r.color
          for (const pt of (r.route || [])) {
            entry.route.push({ ...pt, vol })
          }
        }
      }
    }

    // 同时也收集前20卷中其他角色（非寇仲&徐子陵合并）的路线
    const otherEarlyMap = new Map()
    for (let i = 0; i < SPLIT_VOL - 1; i++) {
      const d = allData.value[i]
      if (!d) continue
      const vol = i + 1
      for (const r of (d.character_routes || [])) {
        if (r.character === MERGED_NAME) continue
        const name = r.character
        if (!otherEarlyMap.has(name)) {
          otherEarlyMap.set(name, { character: name, color: r.color, route: [] })
        }
        const entry = otherEarlyMap.get(name)
        entry.color = r.color
        for (const pt of (r.route || [])) {
          entry.route.push({ ...pt, vol })
        }
      }
    }

    // 合并早期其他角色和后期其他角色
    const allOther = new Map([...otherEarlyMap])
    for (const [name, entry] of otherMap) {
      if (allOther.has(name)) {
        allOther.get(name).color = entry.color
        allOther.get(name).route.push(...entry.route)
      } else {
        allOther.set(name, entry)
      }
    }

    return [
      ...Array.from(splitMap.values()),
      ...Array.from(allOther.values())
    ]
  }
})

/**
 * 图例中展示的路线定义（用于点击切换显隐）
 * 与 mergedRoutes 同步，但只保留 character/color
 */
const visibleRouteDefs = computed(() =>
  mergedRoutes.value.map(r => ({ character: r.character, color: r.color }))
)

// 当前卷关键事件
const currentKeyEvents = computed(() => {
  const d = allData.value[currentVol.value - 1]
  return d ? (d.key_events || []) : []
})

// ── 生命周期 ────────────────────────────────────────────────
onMounted(async () => {
  initMap()
  await loadAllData()
  drawAll()
})

// 监听 currentVol 变化重绘
watch(currentVol, () => {
  syncSelectedRoutesOnModeChange()
  drawAll()
})

/**
 * 切换到卷21时，将已选中的 "寇仲 & 徐子陵" 替换为两个分离路线；
 * 切换回20以前时，将分离路线替换为合并路线。
 */
function syncSelectedRoutesOnModeChange() {
  const isSplit = currentVol.value >= SPLIT_VOL
  const hasMerged   = selectedRoutes.value.includes(MERGED_NAME)
  const hasKouzhong = selectedRoutes.value.includes(KOUZHONG_NAME)
  const hasXuziling = selectedRoutes.value.includes(XUZILING_NAME)

  if (isSplit && hasMerged) {
    // 合并 → 分离：把合并路线换成两条分离路线
    const idx = selectedRoutes.value.indexOf(MERGED_NAME)
    selectedRoutes.value.splice(idx, 1, KOUZHONG_NAME, XUZILING_NAME)
  } else if (!isSplit && (hasKouzhong || hasXuziling)) {
    // 分离 → 合并：把两条分离路线换成合并路线
    selectedRoutes.value = selectedRoutes.value.filter(
      n => n !== KOUZHONG_NAME && n !== XUZILING_NAME
    )
    if (!selectedRoutes.value.includes(MERGED_NAME)) {
      selectedRoutes.value.push(MERGED_NAME)
    }
  }
}

// ── 数据加载 ────────────────────────────────────────────────
async function loadAllData() {
  isLoading.value = true
  loadedCount.value = 0
  // 初始化数组长度，确保响应性
  allData.value = new Array(TOTAL_VOLS).fill(null)

  const promises = []
  for (let i = 1; i <= TOTAL_VOLS; i++) {
    const volStr = String(i).padStart(2, '0')
    promises.push(
      fetch(`/data/vol${volStr}.json`)
        .then(r => {
          if (!r.ok) throw new Error(`HTTP ${r.status}`)
          return r.json()
        })
        .then(data => {
          allData.value[i - 1] = data
          loadedCount.value++
        })
        .catch(err => {
          console.warn(`vol${volStr}.json 加载失败`, err)
          loadedCount.value++
        })
    )
  }
  await Promise.all(promises)
  isLoading.value = false
  // 触发响应式更新（替换整个数组引用）
  allData.value = [...allData.value]
}

// ── 地图初始化 ──────────────────────────────────────────────
function initMap() {
  map = L.map('map', {
    center: [33.0, 113.0],
    zoom: 6,
    zoomControl: true,
    attributionControl: false
  })

  L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
    maxZoom: 18,
    subdomains: 'abcd'
  }).addTo(map)

  L.control.attribution({ prefix: false, position: 'bottomright' })
    .addAttribution('大唐双龙传 - 黄易')
    .addTo(map)

  // 创建图层组
  layerGroups.territories = L.layerGroup().addTo(map)
  layerGroups.locations   = L.layerGroup().addTo(map)
  layerGroups.routes      = L.layerGroup().addTo(map)
  layerGroups.events      = L.layerGroup().addTo(map)
}

// ── 全量重绘 ────────────────────────────────────────────────
function drawAll() {
  if (!map) return
  clearLayerGroups()
  drawTerritories()
  drawLocations()
  drawRoutes()
}

function clearLayerGroups() {
  Object.values(layerGroups).forEach(lg => lg && lg.clearLayers())
}

// ── 绘制势力领地 ────────────────────────────────────────────
function drawTerritories() {
  const locs = mergedLocations.value
  mergedFactions.value.forEach(f => {
    if (!f.territory || !f.territory.length) return
    f.territory.forEach(tid => {
      const loc = locs.find(l => l.id === tid)
      if (!loc) return

      const circle = L.circle([loc.lat, loc.lng], {
        radius: 50000,
        color: f.color,
        fillColor: f.color,
        fillOpacity: 0.12,
        weight: 2,
        opacity: 0.5,
        dashArray: '6 4'
      })
      layerGroups.territories.addLayer(circle)

      const label = L.marker([loc.lat + 0.35, loc.lng], {
        icon: L.divIcon({
          className: '',
          html: `<div style="color:${f.color};font-size:11px;font-weight:bold;text-shadow:0 0 4px rgba(0,0,0,0.8);white-space:nowrap;text-align:center">${f.name}</div>`,
          iconAnchor: [30, 8]
        })
      })
      layerGroups.territories.addLayer(label)
    })
  })
}

// ── 绘制地点标记 ────────────────────────────────────────────
function drawLocations() {
  const locs = mergedLocations.value
  const facs = mergedFactions.value
  locs.forEach(loc => {
    let markerColor = '#aaa'
    for (const f of facs) {
      if (f.territory && f.territory.includes(loc.id)) {
        markerColor = f.color
        break
      }
    }

    const size = loc.type === 'city' ? 10 : loc.type === 'landmark' ? 8 : 6
    const icon = L.divIcon({
      className: '',
      html: `<div style="width:${size}px;height:${size}px;background:${markerColor};border:2px solid #fff;border-radius:50%;box-shadow:0 0 6px ${markerColor}"></div>`,
      iconSize: [size, size],
      iconAnchor: [size / 2, size / 2]
    })

    const marker = L.marker([loc.lat, loc.lng], { icon })
      .bindPopup(`<b>${loc.name}</b><br>${loc.description || ''}`)
    layerGroups.locations.addLayer(marker)

    const nameLabel = L.marker([loc.lat - 0.15, loc.lng], {
      icon: L.divIcon({
        className: '',
        html: `<div style="color:#ccc;font-size:11px;text-shadow:0 0 3px #000,0 0 6px #000;white-space:nowrap;text-align:center">${loc.name}</div>`,
        iconAnchor: [30, 0]
      })
    })
    layerGroups.locations.addLayer(nameLabel)
  })
}

// ── 绘制人物路线 ────────────────────────────────────────────
function drawRoutes() {
  const locs = mergedLocations.value
  mergedRoutes.value.forEach(route => {
    if (!selectedRoutes.value.includes(route.character)) return

    const points = route.route || []
    const resolved = points.map(p => {
      const coords = getPointCoords(p, locs)
      return coords ? { ...coords, chapter: p.chapter, vol: p.vol, event: p.event } : null
    }).filter(Boolean)

    for (let i = 0; i < resolved.length - 1; i++) {
      const p1 = resolved[i]
      const p2 = resolved[i + 1]
      const line = L.polyline([[p1.lat, p1.lng], [p2.lat, p2.lng]], {
        color: route.color,
        weight: 3,
        opacity: 0.8
      })
      layerGroups.routes.addLayer(line)

      const midLat = (p1.lat + p2.lat) / 2
      const midLng = (p1.lng + p2.lng) / 2
      const angle = Math.atan2(p2.lat - p1.lat, p2.lng - p1.lng) * (180 / Math.PI)

      const arrow = L.marker([midLat, midLng], {
        icon: L.divIcon({
          className: '',
          html: `<div style="color:${route.color};font-size:12px;font-weight:bold;transform:rotate(${angle}deg);transform-origin:center;text-shadow:0 0 3px rgba(0,0,0,0.8)">→</div>`,
          iconAnchor: [6, 6]
        })
      })
      layerGroups.routes.addLayer(arrow)
    }

    resolved.forEach((p, idx) => {
      const isEnd   = idx === resolved.length - 1
      const isStart = idx === 0
      const size    = (isStart || isEnd) ? 7 : 5

      const dot = L.circleMarker([p.lat, p.lng], {
        radius: size,
        color: route.color,
        fillColor: isEnd ? '#fff' : route.color,
        fillOpacity: 0.9,
        weight: 2
      }).bindPopup(`<b>${route.character}</b><br>卷${p.vol || '?'} 第${p.chapter}章<br>${p.event || ''}`)
      layerGroups.routes.addLayer(dot)
    })
  })
}

// ── 工具函数 ────────────────────────────────────────────────
function getPointCoords(point, locs) {
  if (point.lat && point.lng) {
    return { lat: point.lat, lng: point.lng }
  }
  if (point.location) {
    const loc = locs.find(l => l.id === point.location)
    if (loc) return { lat: loc.lat, lng: loc.lng }
  }
  return null
}

function toggleRoute(route) {
  const idx = selectedRoutes.value.indexOf(route.character)
  if (idx > -1) {
    selectedRoutes.value.splice(idx, 1)
  } else {
    selectedRoutes.value.push(route.character)
  }
  layerGroups.routes.clearLayers()
  drawRoutes()
}

function showFactionInfo(faction) {
  selectedFaction.value = faction
}

function getTerritoryNames(territoryIds) {
  return territoryIds.map(tid => {
    const loc = mergedLocations.value.find(l => l.id === tid)
    return loc ? loc.name : tid
  })
}

function onVolSliderChange() {
  // currentVol 已由 v-model 更新，watch 会触发 drawAll
}

function flyToEvent(ev) {
  const locs = mergedLocations.value
  let coords = null
  if (ev.lat && ev.lng) {
    coords = [ev.lat, ev.lng]
  } else if (ev.location) {
    const loc = locs.find(l => l.id === ev.location)
    if (loc) coords = [loc.lat, loc.lng]
  }
  if (coords && map) {
    map.flyTo(coords, 8, { duration: 1.2 })
  }
}
</script>

<style scoped>
.map-container {
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

#map {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}

.map-title {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  background: rgba(20, 20, 40, 0.9);
  padding: 8px 24px;
  border-radius: 8px;
  border: 1px solid #c9a96e;
  font-size: 20px;
  font-weight: bold;
  color: #c9a96e;
  letter-spacing: 4px;
  text-shadow: 0 0 10px rgba(201, 169, 110, 0.5);
  pointer-events: none;
  white-space: nowrap;
}

/* ====== 势力图例 ====== */
.legend {
  position: absolute;
  bottom: 20px;
  left: 10px;
  z-index: 1000;
  background: rgba(20, 20, 40, 0.92);
  padding: 12px 16px;
  border-radius: 8px;
  border: 1px solid #555;
  max-width: 220px;
  max-height: calc(100vh - 60px);
  overflow-y: auto;
  font-size: 13px;
  line-height: 1.6;
  color: #e0e0e0;
}

.legend h3 {
  color: #c9a96e;
  margin-bottom: 8px;
  font-size: 14px;
  border-bottom: 1px solid #444;
  padding-bottom: 4px;
}

.legend-item {
  display: flex;
  align-items: center;
  margin: 3px 0;
  cursor: pointer;
}

.legend-item:hover {
  opacity: 0.7;
}

.legend-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 8px;
  flex-shrink: 0;
  border: 1px solid rgba(255, 255, 255, 0.3);
}

.legend-line {
  width: 20px;
  height: 3px;
  margin-right: 8px;
  flex-shrink: 0;
  border-radius: 2px;
}

.legend-section {
  margin-top: 8px;
}

/* ====== 卷册面板 ====== */
.chapter-panel {
  position: absolute;
  bottom: 20px;
  right: 10px;
  z-index: 1000;
  background: rgba(20, 20, 40, 0.92);
  padding: 12px 16px;
  border-radius: 8px;
  border: 1px solid #555;
  width: 340px;
  max-height: calc(100vh - 60px);
  overflow-y: auto;
  color: #e0e0e0;
}

.chapter-panel h3 {
  color: #c9a96e;
  margin-bottom: 8px;
  font-size: 14px;
  border-bottom: 1px solid #444;
  padding-bottom: 4px;
}

.vol-slider-wrap {
  position: relative;
  padding-bottom: 24px;
  margin-bottom: 4px;
}

.chapter-slider {
  width: 100%;
  margin: 8px 0 0;
  accent-color: #c9a96e;
}

.vol-ticks {
  position: relative;
  height: 20px;
  margin-top: 2px;
}

.tick-label {
  position: absolute;
  transform: translateX(-50%);
  font-size: 11px;
  color: #888;
  white-space: nowrap;
}

.vol-info {
  font-size: 12px;
  color: #aaa;
  text-align: center;
  margin-bottom: 8px;
}

/* ====== 关键事件 ====== */
.events-section {
  border-top: 1px solid #444;
  padding-top: 8px;
  margin-top: 4px;
}

.events-title {
  font-size: 12px;
  color: #c9a96e;
  margin-bottom: 6px;
  font-weight: bold;
}

.event-item {
  display: flex;
  gap: 6px;
  margin: 4px 0;
  cursor: pointer;
  padding: 3px 4px;
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.5;
}

.event-item:hover {
  background: rgba(201, 169, 110, 0.12);
}

.event-ch {
  color: #c9a96e;
  white-space: nowrap;
  flex-shrink: 0;
}

.event-text {
  color: #ccc;
}

/* ====== 势力详情面板 ====== */
.info-panel {
  position: absolute;
  top: 60px;
  right: 10px;
  z-index: 1000;
  background: rgba(20, 20, 40, 0.92);
  padding: 12px 16px;
  border-radius: 8px;
  border: 1px solid #555;
  width: 340px;
  max-height: 50vh;
  overflow-y: auto;
  color: #e0e0e0;
}

.info-panel h3 {
  color: #c9a96e;
  margin-bottom: 6px;
  font-size: 15px;
}

.info-panel .close-btn {
  position: absolute;
  top: 8px;
  right: 12px;
  cursor: pointer;
  color: #aaa;
  font-size: 16px;
}

.info-panel .close-btn:hover {
  color: #fff;
}

.info-panel p {
  font-size: 13px;
  line-height: 1.6;
  margin: 4px 0;
}

/* ====== 加载进度条 ====== */
.loading-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: rgba(20, 20, 40, 0.6);
  z-index: 2000;
  overflow: hidden;
}

.loading-inner {
  height: 100%;
  background: #c9a96e;
  transition: width 0.3s ease;
}

.loading-text {
  position: absolute;
  top: 6px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 12px;
  color: #c9a96e;
  background: rgba(20, 20, 40, 0.85);
  padding: 2px 10px;
  border-radius: 4px;
  white-space: nowrap;
}
</style>
