<script setup>
import { ref, computed, onMounted, nextTick } from "vue";
import { getSpots } from "@/api/spot";
import { getTags } from "@/api/tag";
import { getPromotions, recordClick } from "@/api/promotion";
import { get, extractList } from "@/utils/request";
import { resolveImageUrl } from "@/utils/image";
import { formatSpotPrice } from "@/utils/spot";
import CityPicker from "@/components/city-picker/city-picker.vue";
import TagBar from "@/components/tag-bar/tag-bar.vue";

const banners = ref([]);

const sysInfo = uni.getSystemInfoSync();
const winH = sysInfo.windowHeight;
const PEEK_H = Math.round(winH * 0.25);
const EXPAND_H = Math.round(winH * 0.82);

const panelTopH = ref(100);
const measurePanelTop = () => {
  nextTick(() => {
    uni.createSelectorQuery().select(".panel-top").boundingClientRect((rect) => {
      if (rect && rect.height > 0) panelTopH.value = rect.height;
    }).exec();
  });
};
const scrollH = computed(() => Math.max(0, panelH.value - panelTopH.value));

const city = ref("全部城市");
const spots = ref([]);
const page = ref(1);
const pageSize = 10;
const hasMore = ref(true);
const loading = ref(false);
const showCityPicker = ref(false);
const tags = ref([]);
const activeTagId = ref(null);

const sortOptions = [
  { label: "最新", value: "created_at" },
  { label: "评分", value: "avg_rating" },
  { label: "热门", value: "favorite_count" },
];
const sortBy = ref("created_at");
const showSortPicker = ref(false);

const selectedSpot = ref(null);
const locating = ref(false);
const mapCtx = ref(null);
const userLocation = ref(null);

const truncate = (str, len = 8) =>
  str && str.length > len ? str.slice(0, len) + "…" : str;

const markers = computed(() =>
  spots.value
    .filter((s) => s.latitude != null && s.longitude != null)
    .map((s) => ({
      id: s.id,
      latitude: s.latitude,
      longitude: s.longitude,
      title: s.title,
      iconPath: "/static/marker.svg",
      width: 28,
      height: 36,
      callout: {
        content: truncate(s.title),
        display: "ALWAYS",
        fontSize: 12,
        borderRadius: 6,
        padding: 6,
        bgColor: "#6366f1",
        color: "#ffffff",
        textAlign: "center",
        anchorY: 0,
      },
    }))
);

const leftCol = computed(() => spots.value.filter((_, i) => i % 2 === 0));
const rightCol = computed(() => spots.value.filter((_, i) => i % 2 === 1));

const mapCenter = ref({ latitude: 39.9, longitude: 116.4 });
const mapId = "spot-map";

const panelH = ref(PEEK_H);
const panelExpanded = ref(false);
const dragging = ref(false);
let touchStartY = 0;
let startH = 0;

const scrollTop = ref(0);

const getY = (e) => {
  if (e.touches && e.touches.length > 0) return e.touches[0].clientY;
  if (e.changedTouches && e.changedTouches.length > 0) return e.changedTouches[0].clientY;
  if (typeof e.clientY === "number") return e.clientY;
  return 0;
};

const onDragStart = (e) => {
  touchStartY = getY(e);
  startH = panelH.value;
  dragging.value = true;
};

const onDragMove = (e) => {
  if (!dragging.value) return;
  const y = getY(e);
  const dy = touchStartY - y;
  let h = startH + dy;
  if (h < PEEK_H) h = PEEK_H;
  if (h > EXPAND_H) h = EXPAND_H;
  panelH.value = h;
};

const onDragEnd = () => {
  if (!dragging.value) return;
  dragging.value = false;
  if (panelH.value > startH) {
    panelH.value = EXPAND_H;
    panelExpanded.value = true;
  } else if (panelH.value < startH) {
    panelH.value = PEEK_H;
    panelExpanded.value = false;
  }
  measurePanelTop();
};

let contentTouchY = 0;
let contentDragging = false;

const onContentTouchStart = (e) => {
  contentTouchY = getY(e);
  contentDragging = false;
};

const onContentTouchMove = (e) => {
  if (contentDragging) return;
  const y = getY(e);
  const dy = contentTouchY - y;
  if (Math.abs(dy) < 10) return;
  if (!panelExpanded.value && dy > 0) {
    contentDragging = true;
    panelH.value = EXPAND_H;
    panelExpanded.value = true;
    measurePanelTop();
  } else if (panelExpanded.value && dy < 0 && scrollTop.value <= 0) {
    contentDragging = true;
    panelH.value = PEEK_H;
    panelExpanded.value = false;
    measurePanelTop();
  }
};

const onScrollUpdate = (e) => {
  scrollTop.value = e.detail.scrollTop || 0;
};

const panelStyle = computed(() => ({
  height: `${panelH.value}px`,
  transition: dragging.value
    ? "none"
    : "height 0.3s cubic-bezier(0.25,0.8,0.25,1)",
}));

const selectSpot = (spot) => {
  selectedSpot.value = spot;
  panelH.value = PEEK_H;
  panelExpanded.value = false;
  if (spot.latitude != null && spot.longitude != null) {
    mapCenter.value = { latitude: spot.latitude, longitude: spot.longitude };
    mapCtx.value?.moveToLocation({
      latitude: spot.latitude,
      longitude: spot.longitude,
    });
  }
};

const closePopup = () => {
  selectedSpot.value = null;
};

const goDetail = (id) => {
  uni.navigateTo({ url: `/pages/spot/detail?id=${id}` });
};

const onMarkerTap = (e) => {
  const markerId = e.detail?.markerId || e.markerId;
  if (markerId) {
    const spot = spots.value.find((s) => s.id === markerId);
    if (spot) {
      selectSpot(spot);
    } else {
      goDetail(markerId);
    }
  }
};

const onMapTap = () => {
  if (selectedSpot.value) closePopup();
};

const fetchSpots = async (reset = false) => {
  if (loading.value) return;
  if (!reset && !hasMore.value) return;

  loading.value = true;
  if (reset) {
    page.value = 1;
    hasMore.value = true;
  }

  try {
    const params = { page: page.value, page_size: pageSize, sort_by: sortBy.value };
    if (city.value !== "全部城市") params.city = city.value;
    if (activeTagId.value !== null) params.tag_id = activeTagId.value;
    const res = await getSpots(params);
    const list = extractList(res);

    if (reset) {
      spots.value = list;
    } else {
      spots.value.push(...list);
    }

    if (list.length < pageSize) {
      hasMore.value = false;
    } else {
      page.value++;
    }

    if (reset && list.length > 0) {
      const first = list.find(
        (s) => s.latitude != null && s.longitude != null
      );
      if (first) {
        mapCenter.value = {
          latitude: first.latitude,
          longitude: first.longitude,
        };
      }
    }
  } catch (e) {
    console.error(e);
  } finally {
    loading.value = false;
  }
};

const handleCitySelect = (selected) => {
  city.value = selected;
  showCityPicker.value = false;
  fetchSpots(true);
};

const handleTagSelect = (tagId) => {
  activeTagId.value = tagId;
  fetchSpots(true);
};

const goSearch = () => {
  uni.navigateTo({ url: "/pages/search/index" });
};

const fetchTags = async () => {
  try {
    const res = await getTags();
    tags.value = extractList(res);
    measurePanelTop();
  } catch (e) {
    console.error(e);
  }
};

const loadMore = () => {
  if (!loading.value && hasMore.value) fetchSpots();
};

const refreshSpots = () => {
  if (loading.value) return;
  fetchSpots(true);
};

const reverseGeocode = async (lat, lng) => {
  const data = await get("/map/geocoder/reverse", {
    location: `${lat},${lng}`,
  });
  return data?.status === 0 ? data.result : null;
};

const extractCity = (result) => {
  return (
    result?.address_component?.city ||
    result?.ad_info?.city ||
    result?.address_reference?.town?.title ||
    ""
  );
};

const locateToUser = async ({ syncCity = true } = {}) => {
  if (locating.value) return;
  locating.value = true;

  try {
    const location = await new Promise((resolve, reject) => {
      uni.getLocation({
        type: "gcj02",
        success: resolve,
        fail: reject,
      });
    });

    userLocation.value = {
      latitude: location.latitude,
      longitude: location.longitude,
    };
    mapCenter.value = userLocation.value;
    selectedSpot.value = null;

    mapCtx.value?.moveToLocation({
      latitude: location.latitude,
      longitude: location.longitude,
    });

    if (syncCity) {
      const result = await reverseGeocode(location.latitude, location.longitude);
      const currentCity = extractCity(result);
      if (currentCity) {
        city.value = currentCity;
      }
    }

    await fetchSpots(true);
  } catch (e) {
    console.error(e);
    if (!spots.value.length) {
      await fetchSpots(true);
    }
  } finally {
    locating.value = false;
  }
};

const initPage = async () => {
  fetchTags();
  await locateToUser({ syncCity: true });
};

async function loadBanners() {
  try {
    const res = await getPromotions("home_banner");
    banners.value = Array.isArray(res) ? res : [];
  } catch (e) { /* ignore */ }
}

function onBannerClick(item) {
  recordClick(item.id).catch(() => {});
  if (item.link_type === "spot" && item.link_id) {
    uni.navigateTo({ url: `/pages/spot/detail?id=${item.link_id}` });
  } else if (item.link_type === "event" && item.link_id) {
    uni.navigateTo({ url: `/pages/event/detail?id=${item.link_id}` });
  } else if (item.link_type === "shooting" && item.link_id) {
    uni.navigateTo({ url: `/pages/shooting/detail?id=${item.link_id}` });
  } else if (item.link_type === "url" && item.link_url) {
    // #ifdef H5
    window.open(item.link_url, "_blank");
    // #endif
    // #ifndef H5
    uni.navigateTo({ url: `/pages/webview/index?url=${encodeURIComponent(item.link_url)}` });
    // #endif
  }
}

onMounted(() => {
  mapCtx.value = uni.createMapContext(mapId);
  measurePanelTop();
  initPage();
  loadBanners();
});
</script>

<template>
  <page-meta :page-style="'overflow:hidden;height:100%;'" />
  <view class="discover-page">
    <map
      :id="mapId"
      class="full-map"
      :latitude="mapCenter.latitude"
      :longitude="mapCenter.longitude"
      :markers="markers"
      :scale="12"
      :show-location="true"
      :enable-zoom="true"
      :enable-scroll="true"
      @markertap="onMarkerTap"
      @tap="onMapTap"
    />

    <!-- 搜索栏 -->
    <view class="top-overlay">
      <view class="top-bar">
        <view class="search-entry" @tap="goSearch">
          <uni-icons type="search" size="16" color="#94a3b8" />
          <text class="search-entry-text">搜索取景地</text>
        </view>
        <view class="action-btn refresh-btn" @tap="refreshSpots">
          <uni-icons type="refresh" size="16" color="#6366f1" />
        </view>
        <view class="city-selector" @tap="showCityPicker = true">
          <uni-icons type="location" size="16" color="#6366f1" />
          <text class="city-name">{{ city }}</text>
          <uni-icons type="arrowdown" size="10" color="#94a3b8" />
        </view>
      </view>
    </view>

    <view class="locate-btn" @tap="locateToUser({ syncCity: true })">
      <uni-icons
        type="location-filled"
        size="18"
        color="#6366f1"
      />
      <text class="locate-btn-text">{{ locating ? "定位中" : "定位" }}</text>
    </view>

    <!-- 地图上的详情浮窗 -->
    <view v-if="selectedSpot" class="map-popup" @tap.stop>
      <view class="popup-close" @tap="closePopup">
        <uni-icons type="closeempty" size="18" color="#94a3b8" />
      </view>
      <view class="popup-body" @tap="goDetail(selectedSpot.id)">
        <image
          v-if="selectedSpot.cover_image_url"
          class="popup-cover"
          :src="resolveImageUrl(selectedSpot.cover_image_url)"
          mode="aspectFill"
        />
        <view v-else class="popup-cover popup-cover-empty">
          <uni-icons type="camera" size="28" color="#94a3b8" />
        </view>
        <view class="popup-info">
          <text class="popup-title">{{ selectedSpot.title }}</text>
          <text class="popup-city">{{ selectedSpot.city }}</text>
          <text
            class="popup-price"
            :class="{ free: formatSpotPrice(selectedSpot).isFree, paid: !formatSpotPrice(selectedSpot).isFree }"
          >
            {{ formatSpotPrice(selectedSpot).label }}
          </text>
          <view v-if="selectedSpot.avg_rating" class="popup-rating">
            <uni-icons type="star-filled" size="14" color="#f59e0b" />
            <text class="popup-rating-num">{{
              selectedSpot.avg_rating.toFixed(1)
            }}</text>
          </view>
        </view>
        <view class="popup-arrow">
          <uni-icons type="right" size="18" color="#6366f1" />
        </view>
      </view>
    </view>

    <!-- 底部面板 -->
    <view class="bottom-panel" :style="panelStyle">
      <view class="panel-top">
        <view
          class="panel-drag-zone"
          @touchstart.stop="onDragStart"
          @touchmove.stop.prevent="onDragMove"
          @touchend.stop="onDragEnd"
          @mousedown.stop="onDragStart"
          @mousemove.stop.prevent="onDragMove"
          @mouseup.stop="onDragEnd"
          @mouseleave="onDragEnd"
        >
          <view class="panel-handle" />
        </view>

        <TagBar
          :tags="tags"
          :active-id="activeTagId"
          @select="handleTagSelect"
        />

        <swiper
          v-if="banners.length > 0"
          class="banner-swiper"
          :indicator-dots="banners.length > 1"
          :autoplay="true"
          :interval="4000"
          :circular="true"
        >
          <swiper-item v-for="b in banners" :key="b.id" @tap="onBannerClick(b)">
            <image class="banner-img" :src="resolveImageUrl(b.image_url)" mode="aspectFill" />
            <view class="banner-title-bar">
              <text class="banner-title">{{ b.title }}</text>
            </view>
          </swiper-item>
        </swiper>

        <view class="panel-header">
          <text class="panel-title">推荐取景地</text>
          <view class="panel-header-right">
            <view class="sort-trigger" @tap="showSortPicker = !showSortPicker">
              <uni-icons type="bars" size="14" color="#6366f1" />
              <text class="sort-label">{{ sortOptions.find(o => o.value === sortBy)?.label }}</text>
            </view>
            <text class="panel-count" v-if="spots.length">{{ spots.length }}个</text>
          </view>
        </view>
        <view v-if="showSortPicker" class="sort-dropdown">
          <view
            v-for="opt in sortOptions"
            :key="opt.value"
            class="sort-option"
            :class="{ active: sortBy === opt.value }"
            @tap="sortBy = opt.value; showSortPicker = false; fetchSpots(true)"
          >
            <text class="sort-option-text">{{ opt.label }}</text>
            <uni-icons v-if="sortBy === opt.value" type="checkmarkempty" size="14" color="#6366f1" />
          </view>
        </view>
      </view>

      <scroll-view
        scroll-y
        :show-scrollbar="false"
        class="panel-scroll"
        :style="{ height: scrollH + 'px' }"
        @scrolltolower="loadMore"
        @scroll="onScrollUpdate"
        @touchstart="onContentTouchStart"
        @touchmove="onContentTouchMove"
      >
        <view class="waterfall">
          <view class="waterfall-col">
            <view
              v-for="item in leftCol"
              :key="item.id"
              class="wf-card"
              @tap="selectSpot(item)"
            >
              <image
                v-if="item.cover_image_url"
                class="wf-cover"
                :src="resolveImageUrl(item.cover_image_url)"
                mode="widthFix"
              />
              <view v-else class="wf-cover wf-cover-empty">
                <uni-icons type="camera" size="32" color="#94a3b8" />
              </view>
              <view class="wf-info">
                <text class="wf-title">{{ item.title }}</text>
                <view class="wf-meta">
                  <text class="wf-city">{{ item.city }}</text>
                  <view v-if="item.avg_rating" class="wf-rating">
                    <uni-icons type="star-filled" size="12" color="#f59e0b" />
                    <text class="wf-rating-num">{{ item.avg_rating.toFixed(1) }}</text>
                  </view>
                </view>
                <view class="wf-bottom">
                  <text
                    class="wf-price"
                    :class="{ free: formatSpotPrice(item).isFree, paid: !formatSpotPrice(item).isFree }"
                  >{{ formatSpotPrice(item).label }}</text>
                  <view v-if="item.favorite_count" class="wf-fav">
                    <uni-icons type="heart-filled" size="12" color="#94a3b8" />
                    <text class="wf-fav-num">{{ item.favorite_count }}</text>
                  </view>
                </view>
              </view>
            </view>
          </view>
          <view class="waterfall-col">
            <view
              v-for="item in rightCol"
              :key="item.id"
              class="wf-card"
              @tap="selectSpot(item)"
            >
              <image
                v-if="item.cover_image_url"
                class="wf-cover"
                :src="resolveImageUrl(item.cover_image_url)"
                mode="widthFix"
              />
              <view v-else class="wf-cover wf-cover-empty">
                <uni-icons type="camera" size="32" color="#94a3b8" />
              </view>
              <view class="wf-info">
                <text class="wf-title">{{ item.title }}</text>
                <view class="wf-meta">
                  <text class="wf-city">{{ item.city }}</text>
                  <view v-if="item.avg_rating" class="wf-rating">
                    <uni-icons type="star-filled" size="12" color="#f59e0b" />
                    <text class="wf-rating-num">{{ item.avg_rating.toFixed(1) }}</text>
                  </view>
                </view>
                <view class="wf-bottom">
                  <text
                    class="wf-price"
                    :class="{ free: formatSpotPrice(item).isFree, paid: !formatSpotPrice(item).isFree }"
                  >{{ formatSpotPrice(item).label }}</text>
                  <view v-if="item.favorite_count" class="wf-fav">
                    <uni-icons type="heart-filled" size="12" color="#94a3b8" />
                    <text class="wf-fav-num">{{ item.favorite_count }}</text>
                  </view>
                </view>
              </view>
            </view>
          </view>
        </view>

        <view v-if="loading" class="status-tip">
          <text>加载中...</text>
        </view>
        <view v-else-if="!hasMore && spots.length > 0" class="status-tip">
          <text>没有更多了</text>
        </view>
        <view
          v-else-if="!loading && spots.length === 0"
          class="status-tip empty"
        >
          <uni-icons type="search" size="40" color="#94a3b8" />
          <text class="empty-text">暂无取景地数据</text>
        </view>
      </scroll-view>
    </view><!-- bottom-panel -->

    <CityPicker
      :visible="showCityPicker"
      :current-city="city"
      @close="showCityPicker = false"
      @select="handleCitySelect"
    />
  </view>
</template>

<style scoped>
.discover-page {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #f5f6fa;
}

.full-map {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.top-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  padding-top: var(--status-bar-height, 0px);
}

.top-bar {
  display: flex;
  align-items: center;
  padding: 16rpx 24rpx;
  gap: 16rpx;
}

.locate-btn {
  position: absolute;
  right: 24rpx;
  top: calc(112rpx + env(safe-area-inset-top));
  z-index: 11;
  height: 72rpx;
  padding: 0 22rpx;
  border-radius: 36rpx;
  background: rgba(255, 255, 255, 0.96);
  box-shadow: 0 6rpx 18rpx rgba(15, 23, 42, 0.1);
  display: flex;
  align-items: center;
  gap: 8rpx;
}

.locate-btn-text {
  font-size: 24rpx;
  color: #475569;
  font-weight: 600;
}

.search-entry {
  flex: 1;
  display: flex;
  align-items: center;
  height: 72rpx;
  background: rgba(255, 255, 255, 0.95);
  border-radius: 36rpx;
  padding: 0 24rpx;
  gap: 10rpx;
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
}

.search-entry-text {
  font-size: 28rpx;
  color: #94a3b8;
}

.city-selector {
  display: flex;
  align-items: center;
  height: 72rpx;
  padding: 0 20rpx;
  background: rgba(255, 255, 255, 0.95);
  border-radius: 36rpx;
  gap: 6rpx;
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
}

.action-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72rpx;
  height: 72rpx;
  border-radius: 36rpx;
  background: rgba(255, 255, 255, 0.95);
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
  flex-shrink: 0;
}

.city-name {
  font-size: 26rpx;
  color: #1e293b;
  font-weight: 500;
}

/* 地图浮窗 */
.map-popup {
  position: absolute;
  left: 24rpx;
  right: 24rpx;
  bottom: 30%;
  z-index: 25;
  background: #ffffff;
  border-radius: 20rpx;
  box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
  overflow: hidden;
}

.popup-close {
  position: absolute;
  top: 12rpx;
  right: 12rpx;
  width: 48rpx;
  height: 48rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  background: rgba(255, 255, 255, 0.8);
  border-radius: 24rpx;
}

.popup-body {
  display: flex;
  align-items: center;
  padding: 20rpx;
  gap: 20rpx;
}

.popup-cover {
  width: 140rpx;
  height: 140rpx;
  border-radius: 12rpx;
  flex-shrink: 0;
}

.popup-cover-empty {
  background: #e2e8f0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.popup-info {
  flex: 1;
  min-width: 0;
}

.popup-title {
  font-size: 30rpx;
  font-weight: 600;
  color: #1e293b;
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-bottom: 8rpx;
}

.popup-city {
  font-size: 24rpx;
  color: #64748b;
  display: block;
  margin-bottom: 8rpx;
}

.popup-price {
  font-size: 24rpx;
  display: block;
  margin-bottom: 8rpx;
  font-weight: 600;
}

.popup-price.free {
  color: #16a34a;
}

.popup-price.paid {
  color: #d97706;
}

.popup-rating {
  display: flex;
  align-items: center;
  gap: 4rpx;
}

.popup-rating-num {
  font-size: 24rpx;
  color: #f59e0b;
  font-weight: 600;
}

.popup-arrow {
  flex-shrink: 0;
  width: 48rpx;
  height: 48rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(99, 102, 241, 0.1);
  border-radius: 24rpx;
}

/* 底部面板 */
.bottom-panel {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: #f5f6fa;
  border-radius: 32rpx 32rpx 0 0;
  z-index: 20;
  box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.panel-top {
  flex-shrink: 0;
}

.panel-drag-zone {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16rpx 0 8rpx;
  flex-shrink: 0;
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.panel-handle {
  width: 80rpx;
  height: 10rpx;
  background: #cbd5e1;
  border-radius: 5rpx;
  pointer-events: none;
}

.banner-swiper {
  width: calc(100% - 40rpx);
  margin: 12rpx 20rpx 0;
  height: 240rpx;
  border-radius: 16rpx;
  overflow: hidden;
  flex-shrink: 0;
}
.banner-img {
  width: 100%;
  height: 240rpx;
  border-radius: 16rpx;
}
.banner-title-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.5));
  padding: 16rpx 20rpx 12rpx;
  border-radius: 0 0 16rpx 16rpx;
}
.banner-title {
  font-size: 26rpx;
  color: #fff;
  font-weight: 600;
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10rpx 32rpx 8rpx;
  flex-shrink: 0;
}

.panel-title {
  font-size: 30rpx;
  font-weight: 700;
  color: #1e293b;
}

.panel-header-right {
  display: flex;
  align-items: center;
  gap: 16rpx;
}
.sort-trigger {
  display: flex;
  align-items: center;
  gap: 6rpx;
  padding: 6rpx 16rpx;
  background: rgba(99, 102, 241, 0.08);
  border-radius: 20rpx;
}
.sort-label {
  font-size: 22rpx;
  color: #6366f1;
  font-weight: 500;
}
.sort-dropdown {
  background: #fff;
  border-radius: 12rpx;
  margin: 0 32rpx 8rpx;
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
  overflow: hidden;
}
.sort-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20rpx 28rpx;
  border-bottom: 1rpx solid #f1f5f9;
}
.sort-option:last-child { border-bottom: none; }
.sort-option.active { background: rgba(99, 102, 241, 0.04); }
.sort-option-text {
  font-size: 26rpx;
  color: #334155;
}
.sort-option.active .sort-option-text {
  color: #6366f1;
  font-weight: 600;
}
.panel-count {
  font-size: 22rpx;
  color: #94a3b8;
}

.panel-scroll {
  flex: 1;
  min-height: 0;
}
.panel-scroll ::-webkit-scrollbar {
  display: none;
}

/* 瀑布流 */
.waterfall {
  display: flex;
  gap: 16rpx;
  padding: 8rpx 24rpx 120rpx;
}

.waterfall-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 16rpx;
}

.wf-card {
  background: #ffffff;
  border-radius: 16rpx;
  overflow: hidden;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}

.wf-cover {
  width: 100%;
}

.wf-cover-empty {
  height: 200rpx;
  background: #e2e8f0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wf-info {
  padding: 16rpx 18rpx 18rpx;
}

.wf-title {
  font-size: 26rpx;
  font-weight: 600;
  color: #1e293b;
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-bottom: 6rpx;
}

.wf-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4rpx;
}
.wf-city {
  font-size: 22rpx;
  color: #6366f1;
}
.wf-rating {
  display: flex;
  align-items: center;
  gap: 2rpx;
}
.wf-rating-num {
  font-size: 20rpx;
  color: #f59e0b;
  font-weight: 600;
}
.wf-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 4rpx;
}
.wf-price {
  font-size: 22rpx;
  font-weight: 600;
}
.wf-price.free { color: #16a34a; }
.wf-price.paid { color: #d97706; }
.wf-fav {
  display: flex;
  align-items: center;
  gap: 2rpx;
}
.wf-fav-num {
  font-size: 20rpx;
  color: #94a3b8;
}

.status-tip {
  text-align: center;
  padding: 40rpx 0;
  color: #94a3b8;
  font-size: 26rpx;
}

.status-tip.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 40rpx 0;
}

.empty-text {
  margin-top: 12rpx;
  font-size: 26rpx;
  color: #94a3b8;
}
</style>
