"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { APIProvider, AdvancedMarker, Map as GoogleMap, useMap } from "@vis.gl/react-google-maps";
import {
  Footprints,
  Undo2,
  HeartHandshake,
  Waves,
  Home,
  Play,
  Pause,
  SkipBack,
  SkipForward,
  X,
  ChevronLeft,
  ChevronRight,
  MapPinned,
} from "lucide-react";
import ArtifactChat from "../../components/ArtifactChat";
import ArtifactImage from "../../components/ArtifactImage";
import RouteLayer from "../../components/RouteLayer";
import {
  API_URL,
  CATEGORY_LABELS,
  CATEGORY_MARKS,
  DYNASTY_OPTIONS,
  LEVEL_LABELS,
  REPATRIATION_LABELS,
  themeOf,
  markerSizeFor,
  type MapPoint,
  type RouteDetail,
  type RouteSummary,
  type Theme,
} from "../../lib/artifact";

const API_KEY = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? "";

interface MapStats {
  total_artifacts: number;
  total_institutions: number;
  total_locations: number;
}

interface ArtifactGroup {
  key: string;
  institution_name: string;
  city: string;
  lat: number;
  lng: number;
  theme: Theme;
  points: MapPoint[];
}


interface MarkersLayerProps {
  groups: ArtifactGroup[];
  selected: MapPoint | null;
  selectedGroup: ArtifactGroup | null;
  zoom: number;
  onGroupClick: (g: ArtifactGroup) => void;
  onSingleClick: (p: MapPoint) => void;
}

function MarkersLayer({ groups, selected, selectedGroup, zoom, onGroupClick, onSingleClick }: MarkersLayerProps) {
  return (
    <>
      {groups.map((group) => {
        const count = group.points.length;
        const isMulti = count > 1;
        const isGroupActive = selectedGroup?.key === group.key;
        const hasAnyActive = group.points.some((p) => p.id === selected?.id);
        const isActive = isGroupActive || hasAnyActive;
        const first = group.points[0];
        const size = markerSizeFor(zoom, count);
        const fontSize = Math.max(9, Math.round(size * 0.44));
        const showLabel = zoom >= 5 || isActive;
        const label = isMulti ? group.institution_name : first.name;
        // 国内馆藏=琥珀金，流失海外=青瓷绿，已回归=回归金绿
        const palette =
          group.theme === "overseas"
            ? isActive
              ? "border-[#cdeee2] bg-[#3f8d7e] text-[#06140f] ring-[#cdeee2]/45 ring-offset-[#0c0a06]"
              : "border-[#bfe3d6]/70 bg-[#2f6b60] text-[#e2f7ef] ring-[#8fc7b8]/30 ring-offset-[#0c0a06] hover:border-[#cdeee2] hover:bg-[#3f8d7e]"
            : group.theme === "repatriated"
            ? isActive
              ? "border-[#d6f5c9] bg-[#3f9e6a] text-[#06140f] ring-[#d6f5c9]/45 ring-offset-[#0c0a06]"
              : "border-[#bfe7b8]/70 bg-[#2f7d52] text-[#e6ffe0] ring-[#8fc79a]/30 ring-offset-[#0c0a06] hover:border-[#d6f5c9] hover:bg-[#3f9e6a]"
            : isActive
            ? "border-[#ffe2a0] bg-[#d99b3d] text-[#1a1005] ring-[#ffe2a0]/45 ring-offset-[#0c0a06]"
            : "border-[#ffe2a0]/70 bg-[#b9832f] text-[#1f140a] ring-[#c9a55a]/30 ring-offset-[#0c0a06] hover:border-[#ffe2a0] hover:bg-[#d99b3d]";
        return (
          <AdvancedMarker
            key={group.key}
            position={{ lat: group.lat, lng: group.lng }}
            onClick={() => {
              if (isMulti) onGroupClick(group);
              else onSingleClick(first);
            }}
            title={isMulti ? `${group.institution_name}\uff08${count} \u4ef6\uff09` : first.name}
          >
            <div className="flex cursor-pointer flex-col items-center gap-1">
              <div
                className={`flex items-center justify-center rounded-full border-[1.5px] font-serif font-bold leading-none shadow-[0_8px_28px_rgba(0,0,0,0.6)] ring-1 ring-offset-[1.5px] transition-colors ${palette}`}
                style={{ width: size, height: size, fontSize }}
              >
                {count}
              </div>
              {showLabel && (
                <span
                  className={`max-w-28 truncate rounded-[2px] px-1.5 py-0.5 text-[10px] font-medium shadow ${
                    isActive ? "bg-[#d99b3d] text-[#1a1005]" : "bg-[#100e09]/92 text-[#d6aa5b]"
                  }`}
                >
                  {label}
                </span>
              )}
            </div>
          </AdvancedMarker>
        );
      })}
    </>
  );
}

function MapIdleListener({ onIdle, onZoomChange }: { onIdle: () => void; onZoomChange?: (z: number) => void }) {
  const map = useMap();

  useEffect(() => {
    if (!map) return;
    const listener = map.addListener("idle", () => {
      onIdle();
      if (onZoomChange) onZoomChange(map.getZoom() ?? 4);
    });
    return () => listener.remove();
  }, [map, onIdle, onZoomChange]);

  return null;
}

export default function MapPage() {
  const [points, setPoints] = useState<MapPoint[]>([]);
  const [stats, setStats] = useState<MapStats | null>(null);
  const [selected, setSelected] = useState<MapPoint | null>(null);
  const [pointsLoading, setPointsLoading] = useState(false);
  const [pointsError, setPointsError] = useState(false);
  const pointsCacheRef = useRef<Map<string, MapPoint[]>>(new Map());
  const fetchAbortRef = useRef<AbortController | null>(null);
  const fetchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const [filterCategory, setFilterCategory] = useState("");
  const [filterDynasty, setFilterDynasty] = useState("");
  const [filterCity, setFilterCity] = useState("");
  const [cityPage, setCityPage] = useState(0);
  const [sameInstOpen, setSameInstOpen] = useState(false);
  const [routes, setRoutes] = useState<RouteSummary[]>([]);
  const [activeRoute, setActiveRoute] = useState<RouteDetail | null>(null);
  const [routeStep, setRouteStep] = useState(1);
  const [routePlaying, setRoutePlaying] = useState(false);
  const [showWelfare, setShowWelfare] = useState(false);
  const [showRepatriationPicker, setShowRepatriationPicker] = useState(false);
  const [repatPickerCat, setRepatPickerCat] = useState("");
  const [searchQ, setSearchQ] = useState("");
  const [selectedGroup, setSelectedGroup] = useState<ArtifactGroup | null>(null);
  const [zoom, setZoom] = useState(4);
  const onZoomChange = useCallback((z: number) => setZoom(z), []);
  const [rightWidth, setRightWidth] = useState(320);
  const [leftCollapsed, setLeftCollapsed] = useState(false);
  const [rightCollapsed, setRightCollapsed] = useState(false);
  const leftW = leftCollapsed ? 0 : 288;
  const effRightW = rightCollapsed ? 0 : rightWidth;

  // 初始化：右栏宽度取屏幕宽度的 1/3
  useEffect(() => {
    setRightWidth(Math.round(window.innerWidth / 3));
  }, []);
  const [chatStarted, setChatStarted] = useState(false);
  const [infoCollapsed, setInfoCollapsed] = useState(false);

  const handleConversationChange = useCallback((active: boolean) => {
    setChatStarted(active);
    setInfoCollapsed(active);
  }, []);

  // 切换文物时恢复信息展开
  useEffect(() => {
    setChatStarted(false);
    setInfoCollapsed(false);
    setSameInstOpen(false);
  }, [selected?.id]);

  const startResize = useCallback((e: React.MouseEvent) => {
    e.preventDefault();
    const onMove = (ev: MouseEvent) => {
      const w = window.innerWidth - ev.clientX;
      setRightWidth(Math.max(300, Math.min(Math.round(window.innerWidth * 0.6), w)));
    };
    const onUp = () => {
      document.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseup", onUp);
      document.body.style.userSelect = "";
      document.body.style.cursor = "";
    };
    document.body.style.userSelect = "none";
    document.body.style.cursor = "col-resize";
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  }, []);

  useEffect(() => {
    fetch(`${API_URL}/api/v1/map/stats`)
      .then((r) => r.json())
      .then(setStats)
      .catch(() => null);
  }, []);

  // 加载叙事路线列表（南迁 / 回归）
  useEffect(() => {
    fetch(`${API_URL}/api/v1/routes`)
      .then((r) => r.json())
      .then((d) => setRoutes(Array.isArray(d) ? d : []))
      .catch(() => null);
  }, []);

  const loadRoute = useCallback(async (code: string): Promise<RouteDetail | null> => {
    try {
      const res = await fetch(`${API_URL}/api/v1/routes/${code}`);
      const data: RouteDetail = await res.json();
      setActiveRoute(data);
      setRouteStep(1);
      setRoutePlaying(false);
      return data;
    } catch {
      setActiveRoute(null);
      return null;
    }
  }, []);

  const toggleRoute = useCallback(
    async (code: string) => {
      setRoutePlaying(false);
      if (activeRoute?.code === code) {
        setActiveRoute(null);
        return;
      }
      await loadRoute(code);
    },
    [activeRoute, loadRoute]
  );

  // 选择某件回归国宝：激活其路线 + 右栏定位到该文物
  const selectRepatriation = useCallback(
    async (route: RouteSummary) => {
      setShowRepatriationPicker(false);
      const detail = await loadRoute(route.code);
      const aid = detail?.artifact_id ?? route.artifact_id;
      if (aid) {
        const p = points.find((x) => x.id === aid);
        if (p) {
          setSelected(p);
          setSelectedGroup(null);
        }
      }
    },
    [loadRoute, points]
  );

  // 路线自动播放：逐站推进
  useEffect(() => {
    if (!routePlaying || !activeRoute) return;
    const total = activeRoute.stops?.length ?? 0;
    if (routeStep >= total) {
      setRoutePlaying(false);
      return;
    }
    const t = setTimeout(() => setRouteStep((s) => Math.min(total, s + 1)), 1500);
    return () => clearTimeout(t);
  }, [routePlaying, routeStep, activeRoute]);

  const fetchPoints = useCallback(async () => {
    const params = new URLSearchParams();
    if (filterCategory) params.set("category", filterCategory);
    if (filterDynasty) params.set("dynasty", filterDynasty);
    const key = params.toString();

    // 命中缓存直接用，避免地图移动时重复请求
    const cached = pointsCacheRef.current.get(key);
    if (cached) {
      setPoints(cached);
      setPointsError(false);
      return;
    }

    fetchAbortRef.current?.abort();
    const ctrl = new AbortController();
    fetchAbortRef.current = ctrl;
    setPointsLoading(true);
    setPointsError(false);
    try {
      const res = await fetch(`${API_URL}/api/v1/map/points?${key}`, { signal: ctrl.signal });
      const data: unknown = await res.json();
      const arr = Array.isArray(data) ? (data as MapPoint[]) : [];
      pointsCacheRef.current.set(key, arr);
      setPoints(arr);
    } catch (e) {
      if ((e as Error).name !== "AbortError") {
        setPoints([]);
        setPointsError(true);
      }
    } finally {
      setPointsLoading(false);
    }
  }, [filterCategory, filterDynasty]);

  // 地图移动后的取数做防抖，避免频繁触发
  const scheduleFetch = useCallback(() => {
    if (fetchTimerRef.current) clearTimeout(fetchTimerRef.current);
    fetchTimerRef.current = setTimeout(() => {
      void fetchPoints();
    }, 250);
  }, [fetchPoints]);

  useEffect(() => {
    fetchPoints();
  }, [fetchPoints]);

  const cityStats = useMemo(() => {
    const count = points.reduce<Record<string, number>>((acc, point) => {
      const city = point.city || point.province || "未知";
      acc[city] = (acc[city] ?? 0) + 1;
      return acc;
    }, {});

    return Object.entries(count)
      .sort((a, b) => b[1] - a[1])
      .map(([city, total]) => ({ city, total }));
  }, [points]);

  const CITY_PAGE_SIZE = 6;
  const cityTotalPages = Math.max(1, Math.ceil(cityStats.length / CITY_PAGE_SIZE));
  const citySafePage = Math.min(cityPage, cityTotalPages - 1);
  const pagedCities = cityStats.slice(
    citySafePage * CITY_PAGE_SIZE,
    citySafePage * CITY_PAGE_SIZE + CITY_PAGE_SIZE
  );

  const visiblePoints = useMemo(() => {
    return points.filter((point) => {
      const hitKeyword = searchQ
        ? point.name.includes(searchQ) ||
          point.institution_name?.includes(searchQ) ||
          point.dynasty?.includes(searchQ) ||
          point.city?.includes(searchQ)
        : true;
      const hitCity = filterCity ? point.city === filterCity : true;
      return hitKeyword && hitCity;
    });
  }, [filterCity, points, searchQ]);

  const groupedPoints = useMemo(() => {
    const useCity = zoom <= 7;
    const map = new Map<string, ArtifactGroup>();
    visiblePoints.forEach((p) => {
      const key = useCity
        ? (p.city || p.province || `${p.lat.toFixed(1)},${p.lng.toFixed(1)}`)
        : (p.institution_name || `${p.lat.toFixed(3)},${p.lng.toFixed(3)}`);
      const label = useCity
        ? (p.city || p.province || "未知城市")
        : (p.institution_name || "未知机构");
      if (!map.has(key)) {
        map.set(key, {
          key,
          institution_name: label,
          city: p.city || p.province || "未知",
          lat: p.lat,
          lng: p.lng,
          theme: themeOf(p),
          points: [],
        });
      }
      const grp = map.get(key)!;
      grp.points.push(p);
      // 组内只要有海外/回归则整体按该主题着色（海外优先）
      const t = themeOf(p);
      if (t === "overseas") grp.theme = "overseas";
      else if (t === "repatriated" && grp.theme !== "overseas") grp.theme = "repatriated";
    });
    if (useCity) {
      for (const g of map.values()) {
        if (g.points.length > 1) {
          g.lat = g.points.reduce((s, p) => s + p.lat, 0) / g.points.length;
          g.lng = g.points.reduce((s, p) => s + p.lng, 0) / g.points.length;
        }
      }
    }
    return Array.from(map.values());
  }, [visiblePoints, zoom]);

  const institutionCount = useMemo(() => {
    return new Set(visiblePoints.map((p) => p.institution_name).filter(Boolean)).size;
  }, [visiblePoints]);

  const selectedCityText = selected?.city || selected?.province || "未知城市";
  const noKey = !API_KEY;

  return (
    <div className="relative h-screen w-full overflow-hidden bg-[#090806] text-[#f6eddc]">
      <div className="pointer-events-none absolute inset-0 z-0 bg-[radial-gradient(circle_at_top_left,rgba(190,137,55,0.18),transparent_32%),radial-gradient(circle_at_bottom_right,rgba(50,101,91,0.18),transparent_36%)]" />

      <header className="absolute left-0 right-0 top-0 z-50 flex h-16 items-center border-b border-[#d6aa5b]/15 bg-[#080705]/92 px-6 shadow-[0_18px_40px_rgba(0,0,0,0.45)] backdrop-blur-xl">
        <div className="flex items-center gap-4">
          <div className="flex h-9 w-9 items-center justify-center rounded-full border border-[#d6aa5b]/35 bg-[#d6aa5b]/10 text-sm text-[#f2cf83]">
            文
          </div>
          <div>
            <div className="font-serif text-base font-semibold tracking-[0.28em] text-[#f2cf83]">
              中华文明全图鉴
            </div>
            <div className="mt-0.5 text-[10px] uppercase tracking-[0.35em] text-[#9b8b6a]">
              Cultural Heritage Atlas
            </div>
          </div>
        </div>
        <div className="ml-10 hidden items-center gap-6 text-xs text-[#a89d88] lg:flex">
          <span className="text-[#f2cf83]">文物全图</span>
          <span>机构直供</span>
          <span>文明路线</span>
          <span>知识图谱</span>
        </div>
        <div className="flex-1" />
        <div className="hidden items-center gap-5 text-xs text-[#8f8066] md:flex">
          <span>{stats?.total_artifacts ?? points.length} 件文物</span>
          <span className="h-3 w-px bg-[#d6aa5b]/20" />
          <span>{cityStats.length} 个城市</span>
          <span className="h-3 w-px bg-[#d6aa5b]/20" />
          <span>{stats?.total_institutions ?? 0} 家机构</span>
        </div>
      </header>

      <main className="absolute bottom-11 top-16 overflow-hidden border-x border-[#d6aa5b]/10 bg-[#0e1110] transition-all duration-300" style={{ left: leftW, right: effRightW }}>
        {noKey ? (
          <div className="flex h-full flex-col items-center justify-center gap-4 bg-[linear-gradient(135deg,#17110b,#0b1514)]">
            <div className="rounded-full border border-[#d6aa5b]/20 bg-[#d6aa5b]/10 px-5 py-4 text-[#f2cf83]">
              <MapPinned size={40} />
            </div>
            <div className="text-center">
              <p className="text-sm font-medium text-[#f2cf83]">请配置 Google Maps API Key</p>
              <code className="mt-3 block rounded-lg border border-[#d6aa5b]/20 bg-black/35 px-4 py-2 text-xs text-[#dcb96f]">
                NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=你的Key
              </code>
              <p className="mt-3 text-xs text-[#8f8066]">
                API 数据已就绪：{visiblePoints.length} 个点位可展示
              </p>
            </div>
          </div>
        ) : (
          <APIProvider apiKey={API_KEY}>
            <GoogleMap
              defaultCenter={{ lat: 34.4, lng: 108.9 }}
              defaultZoom={4}
              mapId="wenwumap-main"
              gestureHandling="greedy"
              disableDefaultUI
              zoomControl
              clickableIcons={false}
              colorScheme="DARK"
              style={{ width: "100%", height: "100%" }}
            >
              <MapIdleListener onIdle={scheduleFetch} onZoomChange={onZoomChange} />
              {!activeRoute && (
                <MarkersLayer
                  groups={groupedPoints}
                  selected={selected}
                  selectedGroup={selectedGroup}
                  zoom={zoom}
                  onGroupClick={(g) => { setSelectedGroup(g); setSelected(null); }}
                  onSingleClick={(p) => { setSelected(p); setSelectedGroup(null); }}
                />
              )}
              {activeRoute && activeRoute.stops?.length > 0 && (
                <RouteLayer
                  stops={activeRoute.stops}
                  color={activeRoute.color ?? "#e0b15a"}
                  visibleCount={routeStep}
                  activeIndex={routeStep - 1}
                  onStopClick={(i) => {
                    setRoutePlaying(false);
                    setRouteStep(i + 1);
                  }}
                />
              )}
            </GoogleMap>
          </APIProvider>
        )}
        <div className="pointer-events-none absolute inset-0 bg-[linear-gradient(90deg,rgba(8,7,5,0.24),transparent_12%,transparent_82%,rgba(8,7,5,0.18)),linear-gradient(180deg,rgba(8,7,5,0.22),transparent_18%,transparent_80%,rgba(8,7,5,0.2))]" />

        {(pointsLoading || pointsError) && (
          <div className="pointer-events-none absolute left-1/2 top-3 z-10 -translate-x-1/2">
            {pointsError ? (
              <button
                onClick={() => fetchPoints()}
                className="pointer-events-auto rounded-full border border-[#c94b4b]/40 bg-[#1a0c0c]/90 px-3 py-1 text-[11px] text-[#f0b9b9] backdrop-blur transition hover:border-[#c94b4b]/70"
              >
                文物点位加载失败 · 点击重试
              </button>
            ) : (
              <span className="flex items-center gap-1.5 rounded-full border border-[#d6aa5b]/25 bg-[#0c0a06]/85 px-3 py-1 text-[11px] text-[#d6aa5b] backdrop-blur">
                <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-[#d6aa5b] [animation-delay:-0.2s]" />
                <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-[#d6aa5b] [animation-delay:-0.1s]" />
                <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-[#d6aa5b]" />
                <span className="ml-1">加载文物点位…</span>
              </span>
            )}
          </div>
        )}
        {activeRoute && activeRoute.stops?.length > 0 && (
          <div className="absolute bottom-4 left-1/2 z-20 w-[min(720px,90%)] -translate-x-1/2 rounded-2xl border border-[#d6aa5b]/25 bg-[#0c0a06]/94 px-5 py-3.5 shadow-[0_12px_44px_rgba(0,0,0,0.55)] backdrop-blur-xl">
            <div className="mb-2.5 flex items-center justify-between">
              <span
                className="flex items-center gap-2 font-serif text-sm font-semibold"
                style={{ color: activeRoute.color ?? "#e0b15a" }}
              >
                {activeRoute.type === "migration" ? <Footprints size={16} /> : <Undo2 size={16} />}
                {activeRoute.title}
              </span>
              <button
                onClick={() => setActiveRoute(null)}
                className="flex items-center gap-1 rounded-full border border-[#d6aa5b]/20 px-2.5 py-1 text-[11px] text-[#8f8066] transition hover:border-[#d6aa5b]/40 hover:text-[#f2cf83]"
              >
                <X size={12} /> 退出路线
              </button>
            </div>

            {/* 可点击时间轴 */}
            <div className="no-scrollbar mb-2.5 flex items-center gap-0 overflow-x-auto pb-1">
              {activeRoute.stops.map((s, i) => {
                const active = i === routeStep - 1;
                const past = i < routeStep - 1;
                const c = activeRoute.color ?? "#e0b15a";
                return (
                  <div key={s.seq} className="flex flex-shrink-0 items-center">
                    {i > 0 && (
                      <span
                        className="h-px w-7"
                        style={{ background: past || active ? c : "rgba(214,170,91,0.2)" }}
                      />
                    )}
                    <button
                      onClick={() => {
                        setRoutePlaying(false);
                        setRouteStep(i + 1);
                      }}
                      className="group flex flex-col items-center gap-1"
                      title={s.event ?? s.name}
                    >
                      <span
                        className="flex h-6 w-6 items-center justify-center rounded-full border text-[11px] font-bold transition-all"
                        style={{
                          background: active || past ? c : "transparent",
                          borderColor: c,
                          color: active || past ? "#1a1005" : c,
                          transform: active ? "scale(1.18)" : "scale(1)",
                        }}
                      >
                        {s.seq}
                      </span>
                      <span
                        className={`max-w-[64px] truncate text-[10px] ${active ? "text-[#f2cf83]" : "text-[#8f8066]"}`}
                      >
                        {s.year_label || s.name}
                      </span>
                    </button>
                  </div>
                );
              })}
            </div>

            {/* 播放控制 + 当前事件 */}
            <div className="flex items-center gap-3">
              <div className="flex flex-shrink-0 items-center gap-1">
                <button
                  onClick={() => {
                    setRoutePlaying(false);
                    setRouteStep((s) => Math.max(1, s - 1));
                  }}
                  disabled={routeStep <= 1}
                  aria-label="上一站"
                  className="flex h-7 w-7 items-center justify-center rounded-full border border-[#d6aa5b]/25 text-[#d6aa5b] transition hover:bg-[#d6aa5b]/12 disabled:opacity-30"
                >
                  <SkipBack size={13} />
                </button>
                <button
                  onClick={() => {
                    if (routeStep >= activeRoute.stops.length) setRouteStep(1);
                    setRoutePlaying((v) => !v);
                  }}
                  aria-label={routePlaying ? "暂停" : "播放"}
                  className="flex h-8 w-8 items-center justify-center rounded-full text-[#1a1005] transition hover:brightness-110"
                  style={{ background: activeRoute.color ?? "#e0b15a" }}
                >
                  {routePlaying ? <Pause size={15} /> : <Play size={15} />}
                </button>
                <button
                  onClick={() => {
                    setRoutePlaying(false);
                    setRouteStep((s) => Math.min(activeRoute.stops.length, s + 1));
                  }}
                  disabled={routeStep >= activeRoute.stops.length}
                  aria-label="下一站"
                  className="flex h-7 w-7 items-center justify-center rounded-full border border-[#d6aa5b]/25 text-[#d6aa5b] transition hover:bg-[#d6aa5b]/12 disabled:opacity-30"
                >
                  <SkipForward size={13} />
                </button>
              </div>
              <p className="min-w-0 flex-1 text-[12px] leading-5 text-[#d8cbb0]">
                <span className="text-[#f2cf83]">
                  {activeRoute.stops[routeStep - 1]?.year_label} {activeRoute.stops[routeStep - 1]?.name}
                </span>
                {activeRoute.stops[routeStep - 1]?.event ? ` —— ${activeRoute.stops[routeStep - 1]?.event}` : ""}
              </p>
            </div>
          </div>
        )}
      </main>

      {/* 左栏收起/展开 */}
      <button
        onClick={() => setLeftCollapsed((v) => !v)}
        title={leftCollapsed ? "展开左栏" : "收起左栏"}
        className="absolute top-1/2 z-50 flex h-14 w-5 -translate-y-1/2 items-center justify-center rounded-r-md border border-l-0 border-[#d6aa5b]/20 bg-[#0c0a06]/90 text-sm text-[#d6aa5b] backdrop-blur transition-all duration-300 hover:bg-[#d6aa5b]/15 hover:text-[#f2cf83]"
        style={{ left: leftW }}
      >
        {leftCollapsed ? <ChevronRight size={14} /> : <ChevronLeft size={14} />}
      </button>

      {/* 右栏收起/展开 */}
      <button
        onClick={() => setRightCollapsed((v) => !v)}
        title={rightCollapsed ? "展开右栏" : "收起右栏"}
        className="absolute top-1/2 z-50 flex h-14 w-5 -translate-y-1/2 items-center justify-center rounded-l-md border border-r-0 border-[#d6aa5b]/20 bg-[#0c0a06]/90 text-sm text-[#d6aa5b] backdrop-blur transition-all duration-300 hover:bg-[#d6aa5b]/15 hover:text-[#f2cf83]"
        style={{ right: effRightW }}
      >
        {rightCollapsed ? <ChevronLeft size={14} /> : <ChevronRight size={14} />}
      </button>

      <aside className="absolute bottom-11 left-0 top-16 z-40 flex w-72 flex-col border-r border-[#d6aa5b]/15 bg-[#080705]/94 shadow-[18px_0_48px_rgba(0,0,0,0.38)] backdrop-blur-xl transition-transform duration-300" style={{ transform: leftCollapsed ? "translateX(-100%)" : "none" }}>
        <div className="shrink-0 border-b border-[#d6aa5b]/12 p-5">
          <div className="text-xs uppercase tracking-[0.3em] text-[#8f8066]">Explore</div>
          <h1 className="mt-2 font-serif text-xl font-semibold text-[#f6eddc]">文物地图探索</h1>
          <p className="mt-2 text-xs leading-5 text-[#9d927c]">
            按城市、年代与门类追踪文物的保存位置，观察文明记忆在今日中国的分布。
          </p>
          <div className="mt-4 grid grid-cols-3 gap-2">
            <div className="rounded-xl border border-[#d6aa5b]/12 bg-[#d6aa5b]/8 p-3">
              <div className="text-lg font-semibold text-[#f2cf83]">
                {stats?.total_artifacts ?? points.length}
              </div>
              <div className="mt-1 text-[10px] text-[#8f8066]">文物</div>
            </div>
            <div className="rounded-xl border border-[#d6aa5b]/12 bg-white/[0.03] p-3">
              <div className="text-lg font-semibold text-[#f2cf83]">{cityStats.length}</div>
              <div className="mt-1 text-[10px] text-[#8f8066]">城市</div>
            </div>
            <div className="rounded-xl border border-[#d6aa5b]/12 bg-white/[0.03] p-3">
              <div className="text-lg font-semibold text-[#f2cf83]">
                {stats?.total_institutions ?? 0}
              </div>
              <div className="mt-1 text-[10px] text-[#8f8066]">机构</div>
            </div>
          </div>
        </div>

        <div className="no-scrollbar flex-1 min-h-0 space-y-5 overflow-y-auto p-5 pb-8">
          <input
            value={searchQ}
            onChange={(e) => setSearchQ(e.target.value)}
            type="text"
            placeholder="搜索文物、机构、城市…"
            className="w-full rounded-xl border border-[#d6aa5b]/16 bg-[#11100d] px-3 py-2 text-xs text-[#f6eddc] outline-none transition placeholder:text-[#6f6656] focus:border-[#d6aa5b]/55 focus:bg-[#161310]"
          />

          <section>
            <p className="mb-2 text-[11px] font-medium uppercase tracking-[0.24em] text-[#a99566]">
              专题叙事
            </p>
            <div className="space-y-1.5">
              {routes
                .filter((r) => r.type === "migration")
                .map((r) => (
                  <button
                    key={r.code}
                    onClick={() => toggleRoute(r.code)}
                    title={r.summary ?? ""}
                    className={`flex w-full items-center justify-between rounded-lg border px-3 py-2 text-xs transition ${
                      activeRoute?.code === r.code
                        ? "border-[#d6aa5b]/55 bg-[#d6aa5b]/16 text-[#f2cf83]"
                        : "border-transparent bg-white/[0.03] text-[#c7baa0] hover:border-[#d6aa5b]/24 hover:text-[#f2cf83]"
                    }`}
                  >
                    <span className="flex items-center gap-2">
                      <Footprints size={14} />
                      {r.title}
                    </span>
                    <span className="text-[10px] text-[#8f8066]">{activeRoute?.code === r.code ? "进行中" : "路线"}</span>
                  </button>
                ))}
              <button
                onClick={() => setShowRepatriationPicker(true)}
                className={`flex w-full items-center justify-between rounded-lg border px-3 py-2 text-xs transition ${
                  activeRoute?.type === "repatriation"
                    ? "border-[#3f9e6a]/55 bg-[#3f9e6a]/16 text-[#aef0c6]"
                    : "border-transparent bg-white/[0.03] text-[#c7baa0] hover:border-[#3f9e6a]/30 hover:text-[#aef0c6]"
                }`}
              >
                <span className="flex items-center gap-2">
                  <Undo2 size={14} /> 国宝海外回归
                </span>
                <span className="text-[10px] text-[#8f8066]">
                  {routes.filter((r) => r.type === "repatriation").length} 件
                </span>
              </button>
              <button
                onClick={() => setShowWelfare(true)}
                className="flex w-full items-center justify-between rounded-lg border border-[#c94b4b]/25 bg-[#c94b4b]/8 px-3 py-2 text-xs text-[#f0c0b3] transition hover:border-[#c94b4b]/45 hover:text-[#f7d8cf]"
              >
                <span className="flex items-center gap-2"><HeartHandshake size={14} /> 支持文保公益</span>
                <span className="text-[10px] text-[#8f8066]">了解</span>
              </button>
            </div>
          </section>

          <section>
            <div className="mb-2 flex items-center justify-between">
              <p className="text-[11px] font-medium uppercase tracking-[0.24em] text-[#a99566]">
                城市分布
              </p>
              <div className="flex items-center gap-2">
                {filterCity && (
                  <button
                    onClick={() => setFilterCity("")}
                    className="text-[11px] text-[#8f8066] hover:text-[#f2cf83]"
                  >
                    清除
                  </button>
                )}
                {cityTotalPages > 1 && (
                  <div className="flex items-center gap-1 text-[10px] text-[#8f8066]">
                    <button
                      onClick={() => setCityPage((p) => Math.max(0, p - 1))}
                      disabled={citySafePage === 0}
                      aria-label="上一页"
                      className="flex h-5 w-5 items-center justify-center rounded border border-[#d6aa5b]/15 transition hover:border-[#d6aa5b]/40 hover:text-[#f2cf83] disabled:opacity-30 disabled:hover:border-[#d6aa5b]/15"
                    >
                      <ChevronLeft size={13} />
                    </button>
                    <span className="tabular-nums">{citySafePage + 1}/{cityTotalPages}</span>
                    <button
                      onClick={() => setCityPage((p) => Math.min(cityTotalPages - 1, p + 1))}
                      disabled={citySafePage === cityTotalPages - 1}
                      aria-label="下一页"
                      className="flex h-5 w-5 items-center justify-center rounded border border-[#d6aa5b]/15 transition hover:border-[#d6aa5b]/40 hover:text-[#f2cf83] disabled:opacity-30 disabled:hover:border-[#d6aa5b]/15"
                    >
                      <ChevronRight size={13} />
                    </button>
                  </div>
                )}
              </div>
            </div>
            <div className="space-y-1.5">
              {pagedCities.map((item) => (
                <button
                  key={item.city}
                  onClick={() => setFilterCity(filterCity === item.city ? "" : item.city)}
                  className={`flex w-full items-center justify-between rounded-lg border px-3 py-2 text-xs transition ${
                    filterCity === item.city
                      ? "border-[#d6aa5b]/45 bg-[#d6aa5b]/16 text-[#f2cf83]"
                      : "border-transparent bg-white/[0.03] text-[#c7baa0] hover:border-[#d6aa5b]/20 hover:bg-white/[0.06]"
                  }`}
                >
                  <span>{item.city}</span>
                  <span className="text-[#8f8066]">{item.total}</span>
                </button>
              ))}
            </div>
          </section>

          <section>
            <p className="mb-2 text-[11px] font-medium uppercase tracking-[0.24em] text-[#a99566]">
              门类
            </p>
            <div className="grid grid-cols-2 gap-2">
              {Object.entries(CATEGORY_LABELS).map(([value, label]) => (
                <button
                  key={value}
                  onClick={() => setFilterCategory(filterCategory === value ? "" : value)}
                  className={`rounded-lg border px-3 py-2 text-left text-xs transition ${
                    filterCategory === value
                      ? "border-[#d6aa5b]/45 bg-[#d6aa5b]/18 text-[#f2cf83]"
                      : "border-[#d6aa5b]/10 bg-white/[0.03] text-[#b5aa94] hover:border-[#d6aa5b]/24 hover:text-[#f2cf83]"
                  }`}
                >
                  {label}
                </button>
              ))}
            </div>
          </section>

          <section>
            <p className="mb-2 text-[11px] font-medium uppercase tracking-[0.24em] text-[#a99566]">
              年代
            </p>
            <div className="flex flex-wrap gap-2">
              {DYNASTY_OPTIONS.map((dynasty) => (
                <button
                  key={dynasty}
                  onClick={() => setFilterDynasty(filterDynasty === dynasty ? "" : dynasty)}
                  className={`rounded-full border px-3 py-1 text-xs transition ${
                    filterDynasty === dynasty
                      ? "border-[#d6aa5b]/50 bg-[#d6aa5b]/18 text-[#f2cf83]"
                      : "border-[#d6aa5b]/12 bg-white/[0.03] text-[#b5aa94] hover:border-[#d6aa5b]/28 hover:text-[#f2cf83]"
                  }`}
                >
                  {dynasty}
                </button>
              ))}
            </div>
          </section>
        </div>
      </aside>

      <aside className="absolute bottom-11 right-0 top-16 z-40 border-l border-[#d6aa5b]/15 bg-[#080705]/94 shadow-[-18px_0_48px_rgba(0,0,0,0.38)] backdrop-blur-xl transition-transform duration-300" style={{ width: rightWidth, transform: rightCollapsed ? "translateX(100%)" : "none" }}>
        {/* 拖拽手柄：调整右栏宽度 */}
        <div
          onMouseDown={startResize}
          title="拖动调整宽度"
          className="group absolute left-0 top-0 z-50 flex h-full w-1.5 -translate-x-1/2 cursor-col-resize items-center justify-center"
        >
          <span className="h-full w-px bg-transparent transition group-hover:bg-[#d6aa5b]/40" />
          <span className="absolute h-10 w-1 rounded-full bg-[#d6aa5b]/25 transition group-hover:bg-[#d6aa5b]/70" />
        </div>

        {selectedGroup ? (
          <div className="flex h-full flex-col">
            <div className="border-b border-[#d6aa5b]/12 p-5">
              <div className="mb-3 flex items-center justify-between">
                <span className="text-[11px] uppercase tracking-[0.28em] text-[#a99566]">
                  机构藏品
                </span>
                <button
                  onClick={() => setSelectedGroup(null)}
                  className="rounded-full border border-[#d6aa5b]/12 px-2 py-0.5 text-xs text-[#8f8066] hover:border-[#d6aa5b]/35 hover:text-[#f2cf83]"
                >
                  关闭
                </button>
              </div>
              <h2 className="font-serif text-xl font-semibold leading-snug text-[#f6eddc]">
                {selectedGroup.institution_name}
              </h2>
              <p className="mt-1.5 text-xs text-[#9d927c]">
                {selectedGroup.city} · {selectedGroup.points.length} 件藏品
              </p>
            </div>
            <div className="no-scrollbar flex-1 overflow-y-auto p-4">
              {(() => {
                const byInst = new Map<string, MapPoint[]>();
                selectedGroup.points.forEach((p) => {
                  const k = p.institution_name || "未知机构";
                  if (!byInst.has(k)) byInst.set(k, []);
                  byInst.get(k)!.push(p);
                });
                const entries = Array.from(byInst.entries()).sort((a, b) => b[1].length - a[1].length);
                if (entries.length <= 1) {
                  return (
                    <div className="space-y-2">
                      {selectedGroup.points.map((p) => (
                        <button
                          key={p.id}
                          onClick={() => { setSelected(p); setSelectedGroup(null); }}
                          className="w-full rounded-xl border border-white/8 bg-white/[0.03] px-3 py-3 text-left transition hover:border-[#d6aa5b]/28 hover:bg-white/[0.06]"
                        >
                          <div className="flex items-start gap-3">
                            <span className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full border border-[#d6aa5b]/25 bg-[#d6aa5b]/12 text-xs font-bold text-[#f6d48e]">
                              {CATEGORY_MARKS[p.category] ?? "物"}
                            </span>
                            <div className="min-w-0 flex-1">
                              <div className="truncate text-xs font-semibold text-[#f1ead9]">{p.name}</div>
                              <div className="mt-0.5 text-[11px] text-[#8f8066]">
                                {p.dynasty || "年代待考"} · {CATEGORY_LABELS[p.category] ?? p.category}
                              </div>
                            </div>
                            <span className="flex-shrink-0 text-[10px] text-[#a99566]">
                              {p.level === "level_1" ? "一级" : p.level === "level_2" ? "二级" : ""}
                            </span>
                          </div>
                        </button>
                      ))}
                    </div>
                  );
                }
                return (
                  <div className="space-y-4">
                    {entries.map(([inst, pts]) => (
                      <div key={inst}>
                        <div className="mb-2 flex items-center justify-between">
                          <span className="text-xs font-semibold text-[#f2cf83]">{inst}</span>
                          <span className="text-[10px] text-[#8f8066]">{pts.length} 件</span>
                        </div>
                        <div className="space-y-1.5">
                          {pts.map((p) => (
                            <button
                              key={p.id}
                              onClick={() => { setSelected(p); setSelectedGroup(null); }}
                              className="w-full rounded-xl border border-white/8 bg-white/[0.03] px-3 py-2.5 text-left transition hover:border-[#d6aa5b]/28 hover:bg-white/[0.06]"
                            >
                              <div className="flex items-start gap-3">
                                <span className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full border border-[#d6aa5b]/25 bg-[#d6aa5b]/12 text-xs font-bold text-[#f6d48e]">
                                  {CATEGORY_MARKS[p.category] ?? "物"}
                                </span>
                                <div className="min-w-0 flex-1">
                                  <div className="truncate text-xs font-semibold text-[#f1ead9]">{p.name}</div>
                                  <div className="mt-0.5 text-[11px] text-[#8f8066]">
                                    {p.dynasty || "年代待考"} · {CATEGORY_LABELS[p.category] ?? p.category}
                                  </div>
                                </div>
                                <span className="flex-shrink-0 text-[10px] text-[#a99566]">
                                  {p.level === "level_1" ? "一级" : p.level === "level_2" ? "二级" : ""}
                                </span>
                              </div>
                            </button>
                          ))}
                        </div>
                      </div>
                    ))}
                  </div>
                );
              })()}
            </div>
          </div>
        ) : selected ? (
          <div className="flex h-full flex-col">
            <div className="border-b border-[#d6aa5b]/12 p-5">
              <div className="mb-3 flex items-center justify-between">
                <span className="text-[11px] uppercase tracking-[0.28em] text-[#a99566]">
                  文物详情
                </span>
                <button
                  onClick={() => setSelected(null)}
                  className="rounded-full border border-[#d6aa5b]/12 px-2 py-0.5 text-xs text-[#8f8066] hover:border-[#d6aa5b]/35 hover:text-[#f2cf83]"
                >
                  关闭
                </button>
              </div>
              <h2 className="font-serif text-2xl font-semibold leading-snug text-[#f6eddc]">
                {selected.name}
              </h2>
              <p className="mt-2 text-xs text-[#9d927c]">
                {selectedCityText} · {selected.institution_name || "未知机构"}
              </p>
            </div>

            <button
              onClick={() => setInfoCollapsed((v) => !v)}
              className="mx-5 mt-3 flex shrink-0 items-center justify-between rounded-lg bg-white/[0.02] px-3 py-2 text-[11px] uppercase tracking-[0.24em] text-[#a99566] transition hover:bg-white/[0.04] hover:text-[#f2cf83]"
            >
              <span>文物信息</span>
              <span className="tracking-normal">{infoCollapsed ? "展开 ▾" : "收起 ▴"}</span>
            </button>
            {!infoCollapsed && (
              <div className={`no-scrollbar overflow-y-auto px-5 ${chatStarted ? "max-h-[38%] pt-3" : "flex-1 pt-5"}`}>
                <ArtifactImage key={selected.id} point={selected} />
                <div className="mt-4 flex flex-wrap gap-2">
                <span className="rounded-full border border-[#d6aa5b]/40 bg-[#d6aa5b]/18 px-3 py-1 text-xs font-medium text-[#f6dd9f]">
                  {CATEGORY_LABELS[selected.category] ?? selected.category}
                </span>
                <span className="rounded-full border border-[#8faea7]/35 bg-[#8faea7]/13 px-3 py-1 text-xs font-medium text-[#cce7de]">
                  {selected.dynasty || "年代待考"}
                </span>
                <span className="rounded-full border border-white/12 bg-white/[0.06] px-3 py-1 text-xs font-medium text-[#f1ead9]">
                  {LEVEL_LABELS[selected.level] ?? selected.level}
                </span>
                {selected.repatriation_status === "lost_overseas" && (
                  <span className="flex items-center gap-1 rounded-full border border-[#8faea7]/45 bg-[#2f6b60]/25 px-3 py-1 text-xs font-medium text-[#bfe3d6]">
                    <Waves size={12} /> 流失海外
                  </span>
                )}
                {selected.repatriation_status === "repatriated" && (
                  <span className="flex items-center gap-1 rounded-full border border-[#3f9e6a]/55 bg-[#3f9e6a]/22 px-3 py-1 text-xs font-medium text-[#aef0c6]">
                    <Home size={12} /> 已回归
                  </span>
                )}
              </div>

              {selected.story_hook && (
                <div className="mt-5 rounded-2xl border border-[#d6aa5b]/[0.07] bg-[#d6aa5b]/[0.05] p-4">
                  <div className="mb-2 text-[11px] uppercase tracking-[0.24em] text-[#a99566]">
                    故事钩子
                  </div>
                  <p className="font-serif text-base leading-7 text-[#f6eddc]">
                    「{selected.story_hook}」
                  </p>
                </div>
              )}

              <div className="mt-5 space-y-3 rounded-2xl border border-white/[0.04] bg-white/[0.02] p-4 text-xs">
                <div className="flex items-center justify-between gap-4">
                  <span className="text-[#8f8066]">收藏机构</span>
                  <span className="text-right text-[#f1ead9]">{selected.institution_name || "—"}</span>
                </div>
                <div className="flex items-center justify-between gap-4">
                  <span className="text-[#8f8066]">所在城市</span>
                  <span className="text-[#f1ead9]">{selectedCityText}</span>
                </div>
                <div className="flex items-center justify-between gap-4">
                  <span className="text-[#8f8066]">位置类型</span>
                  <span className="text-[#f1ead9]">
                    {selected.location_type === "domestic" ? "国内馆藏" : selected.location_type || "—"}
                  </span>
                </div>
                <div className="flex items-center justify-between gap-4">
                  <span className="text-[#8f8066]">经纬度</span>
                  <span className="font-mono text-[11px] text-[#f1ead9]">
                    {selected.lng.toFixed(4)}, {selected.lat.toFixed(4)}
                  </span>
                </div>
              </div>

              {(() => {
                const sameInst = points.filter(
                  (p) =>
                    p.institution_name &&
                    p.institution_name === selected.institution_name &&
                    p.id !== selected.id
                );
                if (sameInst.length === 0) return null;
                return (
                  <div className="mt-5">
                    <button
                      onClick={() => setSameInstOpen((v) => !v)}
                      className="flex w-full items-center justify-between rounded-xl border border-[#d6aa5b]/[0.08] bg-white/[0.02] px-3 py-3 text-left transition hover:border-[#d6aa5b]/25 hover:bg-[#d6aa5b]/8"
                    >
                      <div className="min-w-0">
                        <div className="text-[11px] uppercase tracking-[0.24em] text-[#a99566]">
                          同一机构
                        </div>
                        <div className="mt-1 truncate text-xs text-[#c7baa0]">
                          {selected.institution_name}
                        </div>
                      </div>
                      <span className="ml-2 flex-shrink-0 text-xs text-[#d6aa5b]">
                        {sameInstOpen ? "收起 ▴" : `还有 ${sameInst.length} 件 ▾`}
                      </span>
                    </button>
                    {sameInstOpen && (
                      <div className="mt-2 space-y-1.5">
                        {sameInst.map((p) => (
                          <button
                            key={p.id}
                            onClick={() => {
                              setSelected(p);
                              setSelectedGroup(null);
                            }}
                            className="flex w-full items-center gap-3 rounded-lg border border-white/[0.05] bg-white/[0.02] px-3 py-2 text-left transition hover:border-[#d6aa5b]/25 hover:bg-[#d6aa5b]/8"
                          >
                            <span className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full border border-[#d6aa5b]/25 bg-[#d6aa5b]/12 text-xs font-bold text-[#f6d48e]">
                              {CATEGORY_MARKS[p.category] ?? "\u7269"}
                            </span>
                            <div className="min-w-0 flex-1">
                              <div className="truncate text-xs font-semibold text-[#f1ead9]">{p.name}</div>
                              <div className="mt-0.5 text-[11px] text-[#8f8066]">
                                {p.dynasty || "年代待考"} · {CATEGORY_LABELS[p.category] ?? p.category}
                              </div>
                            </div>
                            <span className="flex-shrink-0 text-[10px] text-[#a99566]">
                              {p.level === "level_1" ? "一级" : p.level === "level_2" ? "二级" : ""}
                            </span>
                          </button>
                        ))}
                      </div>
                    )}
                  </div>
                );
              })()}
              </div>
            )}

            <div className={chatStarted || infoCollapsed ? "flex min-h-0 flex-1 flex-col px-5 pb-5 pt-3" : "shrink-0 px-5 pb-5"}>
              <ArtifactChat
                fill={chatStarted || infoCollapsed}
                artifactId={selected.id}
                artifactName={selected.name}
                onConversationChange={handleConversationChange}
              />
            </div>
          </div>
        ) : (
          <div className="flex h-full flex-col p-5">
            <div className="text-[11px] uppercase tracking-[0.28em] text-[#a99566]">文物详情</div>
            <h2 className="mt-3 font-serif text-2xl font-semibold text-[#f6eddc]">
              点击地图点位
            </h2>
            <p className="mt-3 text-sm leading-6 text-[#9d927c]">
              选择任意文物后，可查看门类、年代、馆藏机构、城市位置与故事钩子。
            </p>
          </div>
        )}
      </aside>

      <footer className="absolute bottom-0 left-0 right-0 z-50 flex h-11 items-center border-t border-[#d6aa5b]/12 bg-[#080705]/95 px-6 text-xs text-[#8f8066] backdrop-blur-xl">
        <span className="text-[#c8b88e]">显示 {groupedPoints.length} {zoom <= 7 ? "个城市" : "个机构"}（{institutionCount} 家机构 · {visiblePoints.length} 件文物）</span>
        {filterCity && <span className="ml-4">城市：{filterCity}</span>}
        {filterCategory && <span className="ml-4">门类：{CATEGORY_LABELS[filterCategory]}</span>}
        {filterDynasty && <span className="ml-4">年代：{filterDynasty}</span>}
        <div className="flex-1" />
        <span className="mr-4 flex items-center gap-3">
          <span className="flex items-center gap-1.5">
            <span className="inline-block h-2.5 w-2.5 rounded-full bg-[#b9832f]" />国内馆藏
          </span>
          <span className="flex items-center gap-1.5">
            <span className="inline-block h-2.5 w-2.5 rounded-full bg-[#2f6b60]" />流失海外
          </span>
          <span className="flex items-center gap-1.5">
            <span className="inline-block h-2.5 w-2.5 rounded-full bg-[#3f9e6a]" />已回归
          </span>
        </span>
        <span>数据来源：测试机构 seed · 用于 MVP 原型验证</span>
      </footer>

      {showWelfare && (
        <div
          onClick={() => setShowWelfare(false)}
          className="fixed inset-0 z-[300] flex items-center justify-center bg-black/80 backdrop-blur-sm"
        >
          <div
            onClick={(e) => e.stopPropagation()}
            className="w-[min(460px,90vw)] rounded-2xl border border-[#d6aa5b]/25 bg-[#0c0a06] p-6 shadow-[0_20px_60px_rgba(0,0,0,0.6)]"
          >
            <div className="mb-3 flex items-center justify-between">
              <span className="font-serif text-lg font-semibold text-[#f2cf83]">支持文物保护公益</span>
              <button
                onClick={() => setShowWelfare(false)}
                className="flex items-center gap-1 rounded-full border border-[#d6aa5b]/20 px-2.5 py-0.5 text-xs text-[#8f8066] hover:text-[#f2cf83]"
              >
                <X size={13} /> 关闭
              </button>
            </div>
            <p className="text-sm leading-7 text-[#d8cbb0]">
              「留下来的这些瑰宝一定要千方百计呵护好、珍惜好，把我们这个世界上唯一没有中断的文明继续传承下去。」
            </p>
            <p className="mt-4 text-xs leading-6 text-[#9d927c]">
              本平台秉持公益优先：在展览中特别设立公益专场，并从衍生品销售中预留特定比例，用于支持文物修复、文物南迁史料整理与国宝回归等公益文保活动。让更多人参与守护文明火种。
            </p>
            <div className="mt-5 flex gap-2">
              <button
                onClick={() => setShowWelfare(false)}
                className="flex-1 rounded-xl bg-[#d99b3d] px-4 py-2 text-sm font-semibold text-[#1a1005] transition hover:bg-[#e7ad52]"
              >
                我愿了解与参与
              </button>
            </div>
            <p className="mt-3 text-center text-[10px] text-[#6f6656]">MVP 原型 · 公益模块为示意，暂未接入真实捐助</p>
          </div>
        </div>
      )}

      {showRepatriationPicker && (
        <div
          onClick={() => setShowRepatriationPicker(false)}
          className="fixed inset-0 z-[300] flex items-center justify-center bg-black/80 backdrop-blur-sm"
        >
          <div
            onClick={(e) => e.stopPropagation()}
            className="flex max-h-[80vh] w-[min(560px,92vw)] flex-col rounded-2xl border border-[#3f9e6a]/35 bg-[#0c0a06] shadow-[0_20px_60px_rgba(0,0,0,0.6)]"
          >
            <div className="flex items-center justify-between border-b border-[#3f9e6a]/15 px-5 py-3.5">
              <span className="flex items-center gap-2 font-serif text-base font-semibold text-[#aef0c6]">
                <Undo2 size={16} /> 国宝海外回归 · 选择文物
              </span>
              <button
                onClick={() => setShowRepatriationPicker(false)}
                className="flex items-center gap-1 rounded-full border border-[#d6aa5b]/20 px-2.5 py-0.5 text-xs text-[#8f8066] hover:text-[#f2cf83]"
              >
                <X size={13} /> 关闭
              </button>
            </div>

            {/* 门类筛选 */}
            <div className="flex flex-wrap gap-1.5 border-b border-[#3f9e6a]/10 px-5 py-3">
              <button
                onClick={() => setRepatPickerCat("")}
                className={`rounded-full border px-2.5 py-1 text-[11px] transition ${
                  repatPickerCat === ""
                    ? "border-[#3f9e6a]/55 bg-[#3f9e6a]/18 text-[#aef0c6]"
                    : "border-[#d6aa5b]/12 bg-white/[0.03] text-[#b5aa94] hover:text-[#f2cf83]"
                }`}
              >
                全部
              </button>
              {Array.from(
                new Set(
                  routes
                    .filter((r) => r.type === "repatriation" && r.artifact_category)
                    .map((r) => r.artifact_category as string)
                )
              ).map((cat) => (
                <button
                  key={cat}
                  onClick={() => setRepatPickerCat(cat)}
                  className={`rounded-full border px-2.5 py-1 text-[11px] transition ${
                    repatPickerCat === cat
                      ? "border-[#3f9e6a]/55 bg-[#3f9e6a]/18 text-[#aef0c6]"
                      : "border-[#d6aa5b]/12 bg-white/[0.03] text-[#b5aa94] hover:text-[#f2cf83]"
                  }`}
                >
                  {CATEGORY_LABELS[cat] ?? cat}
                </button>
              ))}
            </div>

            {/* 文物列表 */}
            <div className="no-scrollbar flex-1 space-y-2 overflow-y-auto p-4">
              {routes
                .filter(
                  (r) =>
                    r.type === "repatriation" &&
                    (repatPickerCat === "" || r.artifact_category === repatPickerCat)
                )
                .map((r) => (
                  <button
                    key={r.code}
                    onClick={() => selectRepatriation(r)}
                    className="flex w-full items-start gap-3 rounded-xl border border-white/[0.05] bg-white/[0.03] px-4 py-3 text-left transition hover:border-[#3f9e6a]/35 hover:bg-[#3f9e6a]/8"
                  >
                    <span className="mt-0.5 flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full border border-[#3f9e6a]/30 bg-[#3f9e6a]/12 font-serif text-base font-bold text-[#aef0c6]">
                      {CATEGORY_MARKS[r.artifact_category ?? "other"] ?? "物"}
                    </span>
                    <div className="min-w-0 flex-1">
                      <div className="truncate text-sm font-semibold text-[#f1ead9]">
                        {r.artifact_name ?? r.title}
                      </div>
                      <div className="mt-0.5 text-[11px] text-[#8f8066]">
                        {r.artifact_dynasty || "年代待考"} · {CATEGORY_LABELS[r.artifact_category ?? "other"] ?? ""}
                        {r.institution_name ? ` · 现藏 ${r.institution_name}` : ""}
                      </div>
                      {r.summary && (
                        <div className="mt-1 line-clamp-2 text-[11px] leading-4 text-[#9d927c]">{r.summary}</div>
                      )}
                    </div>
                    <ChevronRight size={16} className="mt-2 flex-shrink-0 text-[#3f9e6a]" />
                  </button>
                ))}
            </div>
            <div className="border-t border-[#3f9e6a]/10 px-5 py-2 text-center text-[10px] text-[#6f6656]">
              选定后将展示该文物的回归之路，并在右侧定位到它
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
