<template>
  <view class="hot-category-block">
    <!-- Tab 栏 -->
    <view class="ranking-tabs">
      <view
        v-for="tab in tabs"
        :key="tab.key"
        class="ranking-tab-item"
        :class="{ active: activeTabKey === tab.key }"
        :data-key="tab.key"
        @click="handleTabClick"
      >
        <image
          v-if="tab.icon"
          class="ranking-tab-icon"
          :src="tab.icon"
          mode="aspectFit"
          :style="{
            width: (tab.iconWidth || 32) + 'rpx',
            height: (tab.iconHeight || 40) + 'rpx',
          }"
        />
        <!-- <text class="ranking-tab-label">{{ tab.label }}</text> -->
      </view>
    </view>

    <!-- 内容区域: 内部 scroll -->
    <scroll-view
      class="content-scroll"
      scroll-y
      :show-scrollbar="false"
      :bounce="false"
      :lower-threshold="80"
      :scroll-into-view="scrollIntoView"
      :scroll-with-animation="false"
      @scrolltolower="handleScrollToLower"
    >
      <!-- 顶部哨兵：切换 tab 时通过 scroll-into-view 把列表强制滚回顶部 -->
      <view id="grid-top" class="scroll-anchor"></view>

      <!-- 骨架屏 -->
      <view v-if="loading" class="grid-skeleton">
        <view v-for="i in 11" :key="i" class="skeleton-card">
          <view class="skeleton-image"></view>
          <view class="skeleton-info">
            <view class="skeleton-avatar"></view>
            <view class="skeleton-name"></view>
          </view>
        </view>
      </view>

      <!-- 内容网格 -->
      <view v-else class="items-grid">
        <view
          v-for="(item, index) in items"
          :key="item.id || index"
          class="grid-card"
          :class="{
            [`grid-card-top-${index + 1}`]: index < 5,
            'grid-card-top-other': index >= 5,
          }"
          @click="handleCardClick(item)"
        >
          <!-- 点赞动效波纹 -->
          <view
            class="wf-like-wave wf-like-wave-outer"
            :class="{ 'wf-like-wave-active': likingMap[item.id] }"
          />
          <view
            class="wf-like-wave wf-like-wave-inner"
            :class="{ 'wf-like-wave-active': likingMap[item.id] }"
          />

          <!-- 单行布局：藏品图片 + 头像 + 点赞数 + TOP 标签 -->
          <view class="card-row">
            <view class="card-image-wrap">
              <image
                class="card-image"
                :src="item.cover_url || item.cover_image || ''"
                mode="aspectFill"
                lazy-load
              />
              <!-- 前 3 名专属：包裹整个卡片的边框图 -->
              <image
                v-if="index < 3"
                class="frame-image"
                :src="TOP_FRAME_MAP[index]"
                mode="scaleToFill"
                lazy-load
              />
              <!-- 前 3 名专属：左上角奖牌装饰 -->
              <!-- <image
                v-if="index < 3"
                class="card-medal"
                :src="MEDAL_MAP[index]"
                mode="aspectFit"
                lazy-load
              /> -->
            </view>
            <view
              class="like-info"
              :class="{ [`like-info-top-${index + 1}`]: index < 3 }"
            >
              <view class="like-row">
                <image
                  class="like-icon"
                  :src="
                    item.is_liked
                      ? '/static/icon/heart-icon.png'
                      : '/static/icon/heart-icon-false.png'
                  "
                  mode="aspectFit"
                />
                <text class="like-count">{{
                  formatCount(item.like_count)
                }}</text>
              </view>
              <text class="user-name">{{
                item.owner_nickname || item.creator_name || item.name || ""
              }}</text>
              <text class="user-number">No.{{ item.owner_uid || "" }}</text>
            </view>
            <view v-if="index >= 3" class="top-badge">
              <view class="badge-rank">
                <image
                  class="badge-rank-icon"
                  src="/static/square/top/top.png"
                  mode="aspectFit"
                />
                <text class="badge-rank-number">{{ index + 1 }}</text>
              </view>
            </view>
          </view>
        </view>

        <!-- 分页底部状态：加载中 / 没有更多了 / 暂无数据 -->
        <view v-if="loadingMore" class="load-more-tip load-more-tip-loading">
          <text class="load-more-text">加载中...</text>
        </view>
        <view v-else-if="!hasMore && items.length > 0" class="load-more-tip">
          <text class="load-more-text">— 没有更多了 —</text>
        </view>
        <view v-else-if="!loading && items.length === 0" class="load-more-tip">
          <text class="load-more-text">暂无数据</text>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

<script setup>
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
import { onShow } from "@dcloudio/uni-app";
import { getHotRankingApi } from "@/utils/api.js";
import {
  getAssetCoverRealUrl,
  getInstantAssetCoverUrl,
} from "@/utils/assetImageHelper.js";

// 把后端返回的 cover_url / cover_image 转成真实可访问的 URL
// 处理 3 种形态：/static/... (本地)、相对路径 (需 presign)、完整 URL (可能过期)
async function resolveItemUrls(item) {
  if (!item) return item;
  const cover = item.cover_url || item.cover_image || "";
  if (cover) {
    item.cover_url = await getAssetCoverRealUrl(cover);
  }
  return item;
}

const emit = defineEmits(["cardClick"]);

const items = ref([]);
const loading = ref(false);
const likingMap = ref({});
const activeTabKey = ref("");

// ===== 分页状态 =====
// currentPage   : 已加载到的页码（从 1 开始）
// hasMore       : 是否还有下一页（接口返回数量 < PAGE_SIZE 或累计已 >= total 时置 false）
// loadingMore   : 是否正在加载下一页（防止 scrolltolower 重复触发）
const currentPage = ref(1);
const hasMore = ref(true);
const loadingMore = ref(false);

// scroll-into-view 目标元素 id —— 切换 tab 时设为 'grid-top' 把 scroll-view 滚回顶部
// 通过先置空 → nextTick 设回目标 id 来强制触发（即使上一次已经是同一个 id 也能生效）
const scrollIntoView = ref("");

// 每页数量
const PAGE_SIZE = 10;

// Tab 配置（直接写死在组件内）
// 新增 tab 在这里 push 一项即可：{ key, label, icon, iconWidth, iconHeight, fetch }
// fetch 接受 page 参数，由组件内部分页逻辑统一传入。
const tabs = [
  {
    key: "hot",
    label: "点赞榜",
    icon: "/static/square/galaxy/dianzanbang.png",
    iconWidth: 96,
    iconHeight: 88,
    fetch: (page) => getHotRankingApi("displaying", null, page, PAGE_SIZE),
  },
  {
    key: "new",
    label: "活跃榜",
    icon: "/static/square/galaxy/huoyuebang.png",
    iconWidth: 96,
    iconHeight: 88,
    fetch: (page) => getHotRankingApi("displaying", null, page, PAGE_SIZE),
  },
  {
    key: "trending",
    label: "曝光榜",
    icon: "/static/square/galaxy/baoguangbang.png",
    iconWidth: 96,
    iconHeight: 88,
    fetch: (page) => getHotRankingApi("displaying", null, page, PAGE_SIZE),
  },
  {
    key: "tongcheng",
    label: "同城榜",
    icon: "/static/square/galaxy/tongchengbang.png",
    iconWidth: 96,
    iconHeight: 88,
    fetch: (page) => getHotRankingApi("displaying", null, page, PAGE_SIZE),
  },
];

// 前 3 名奖牌图标
const MEDAL_MAP = {
  0: "/static/square/top/TOP1icon.png",
  1: "/static/square/top/TOP2icon.png",
  2: "/static/square/top/TOP3icon.png",
};

// 前 3 名对应的边框图
const TOP_FRAME_MAP = {
  0: "/static/square/top/TOP1biankuang1.png",
  1: "/static/square/top/TOP2biankuang2.png",
  2: "/static/square/top/TOP3biankuangpng3.png",
};

const activeTab = computed(
  () => tabs.find((t) => t.key === activeTabKey.value) || tabs[0],
);

// 初始化 activeTabKey：默认选第一个 tab
watch(
  () => tabs,
  (newTabs) => {
    if (
      newTabs.length > 0 &&
      !newTabs.some((t) => t.key === activeTabKey.value)
    ) {
      activeTabKey.value = newTabs[0].key;
    }
  },
  { immediate: true },
);

// 切换 tab
const handleTabClick = (e) => {
  const key = e.currentTarget.dataset.key;
  if (key && key !== activeTabKey.value) {
    activeTabKey.value = key;
  }
};

// activeTab 变化时重新加载数据（重置分页）
watch(activeTab, () => {
  resetAndLoad();
});

// 格式化数量
const formatCount = (count) => {
  if (!count) return "0";
  if (count >= 10000) return (count / 10000).toFixed(1) + "w";
  if (count >= 1000) return (count / 1000).toFixed(1) + "k";
  return count.toString();
};

const handleCardClick = (item) => {
  emit("cardClick", item);
};

// 监听全局点赞事件，更新状态
const onAssetLiked = ({ asset_id, data }) => {
  const index = items.value.findIndex(
    (item) => (item.asset_id || item.id) === asset_id,
  );
  if (index !== -1) {
    const updatedItems = [...items.value];
    updatedItems[index] = {
      ...updatedItems[index],
      is_liked: data?.is_liked ?? true,
      like_count:
        data?.new_like_count ?? (updatedItems[index].like_count || 0) + 1,
    };
    items.value = updatedItems;
    // 触发动画
    likingMap.value = { ...likingMap.value, [asset_id]: true };
    setTimeout(() => {
      likingMap.value = { ...likingMap.value, [asset_id]: false };
    }, 600);
  }
};

// 重置分页并重新加载第 1 页
// 切换 tab 时调用：清空 items、把 currentPage 拉回 1、hasMore 设回 true、滚动到顶。
const resetAndLoad = () => {
  currentPage.value = 1;
  hasMore.value = true;
  loadingMore.value = false;
  items.value = [];
  // 强制滚回顶部：先清空再设回 id，避免上一次值就是 'grid-top' 时不触发
  scrollIntoView.value = "";
  nextTick(() => {
    scrollIntoView.value = "grid-top";
  });
  loadData({ append: false });
};

// 加载数据
// append=false：首页加载，覆盖 items；append=true：分页追加，拼接到 items 末尾。
//
// 性能关键：不再 await 预签名！流程：
//   1) 接口返回后立即用 getInstantAssetCoverUrl 同步拿到「能马上渲染的 URL」（命中缓存/未过期URL/占位图）
//   2) 立刻 set items.value → 列表瞬时出现，骨架屏立即下线
//   3) 后台并行跑 getAssetCoverRealUrl 拿到精确的预签名 URL，逐个 patch 回 items.value[i].cover_url
//      （Vue 3 ref 数组里的对象是 reactive 代理，单个属性变更会触发该卡片重渲染）
const loadData = async ({ append = false } = {}) => {
  const tab = activeTab.value;
  if (!tab || typeof tab.fetch !== "function") {
    console.warn("[HotCategoryBlock] 当前 tab 未配置 fetch:", tab);
    if (!append) items.value = [];
    return;
  }
  // 首页用 loading 整屏骨架；分页用 loadingMore 底部小指示器
  if (append) {
    loadingMore.value = true;
  } else {
    loading.value = true;
  }
  try {
    const res = await tab.fetch(currentPage.value);
    if (res && res.code === 0 && res.data?.items) {
      // ① 立刻准备好「能马上渲染的」items —— 不等任何网络
      const rawItems = res.data.items.map((it) => {
        const id = it.id || it.asset_id;
        const rawCover = it.cover_url || it.cover_image || "";
        return {
          ...it,
          id,
          _rawCover: rawCover, // 留一份原始 URL，后台异步任务会用它去预签名
          cover_url: getInstantAssetCoverUrl(rawCover),
        };
      });

      // ② 立即上屏
      const baseOffset = append ? items.value.length : 0;
      if (append) {
        items.value = [...items.value, ...rawItems];
      } else {
        items.value = rawItems;
      }

      // ③ 后台并行解析预签名 URL，单张图回来就 patch 一张
      //    不 await —— 不阻塞任何渲染
      rawItems.forEach((it, idx) => {
        const raw = it._rawCover;
        if (!raw) return;
        // 同步路径下已经有精确 URL（命中缓存 / 完整未过期）则无需再请求
        const instant = it.cover_url;
        if (
          instant &&
          instant !== PLACEHOLDER_IMAGE &&
          instant === getInstantAssetCoverUrl(raw)
        ) {
          // 已是精确 URL；仍然异步校准一次以处理过期（getAssetCoverRealUrl 内部命中缓存就是同步快速返回）
        }
        getAssetCoverRealUrl(raw)
          .then((realUrl) => {
            const targetIdx = baseOffset + idx;
            const target = items.value[targetIdx];
            // 防御：用户已切 tab / 列表被清空时，跳过 patch
            if (target && target.id === it.id && realUrl) {
              target.cover_url = realUrl;
            }
          })
          .catch(() => {
            /* 单张失败不影响其它图 */
          });
      });

      // 判定是否还有下一页
      const total = Number(res.data.total ?? 0);
      if (total > 0) {
        hasMore.value = items.value.length < total;
      } else {
        hasMore.value = rawItems.length >= PAGE_SIZE;
      }
    } else if (!append) {
      items.value = [];
      hasMore.value = false;
    }
  } catch (e) {
    console.error("[HotCategoryBlock] 加载数据失败", e?.message ?? e);
    if (!append) items.value = [];
  } finally {
    loading.value = false;
    loadingMore.value = false;
  }
};

// 占位图常量（与 helper 内部 DEFAULT_IMAGE 一致）
const PLACEHOLDER_IMAGE = "/static/nft/collection.png";

// 滚动到底部触发加载下一页
const handleScrollToLower = () => {
  if (loading.value || loadingMore.value || !hasMore.value) return;
  currentPage.value += 1;
  loadData({ append: true });
};

onMounted(() => {
  uni.$on("assetLiked", onAssetLiked);
  loadData();
});

onShow(() => {
  // loadData();
});

onUnmounted(() => {
  uni.$off("assetLiked", onAssetLiked);
});
</script>

<style scoped lang="scss">
.hot-category-block {
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  //  padding: 0 9.5rpx;
  border-radius: 24rpx;
  position: relative;
  background: url("/static/square/galaxy/xbbj.png") center no-repeat;
  background-size: 100% 100%;
}

.content-scroll {
  flex: 1;
  min-height: 0;
  border-radius: 12px;
  overflow: hidden;
  position: relative;
  z-index: 2;
}

.ranking-tabs {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 64rpx;
  position: relative;
  top: 16rpx;
  margin: 0 24rpx;
  // width: 480rpx;
  height: 96rpx;
  // clip-path: inset(-200rpx 0 16rpx 0);
  &::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
      184deg,
      rgba(255, 90, 93, 0.47) -36.55%,
      rgba(194, 235, 255, 0.47) 121.2%
    );
    filter: blur(2.5px);
    // opacity: 0.8;
    // Figma 用的就是 filter: blur(图形模糊),不是 backdrop-filter(背景模糊)
    // filter: blur(3.7px);
    -webkit-filter: blur(2.5px);
    border-top-left-radius: 14px;
    border-top-right-radius: 13px;
    border-bottom-right-radius: 8px;
    border-bottom-left-radius: 7px;
  }
}

.ranking-tab-item {
  height: 80rpx;
  width: 99.2rpx;
  display: flex;
  align-items: center;
  flex-direction: column;
  transition: all 0.25s ease;
  position: relative;
}

.ranking-tab-item.active {
  width: 99.2rpx;
  height: 160rpx;
  top: 40rpx;
  z-index: 1;
  &::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
      190.37deg,
      rgba(255, 90, 140, 0.98) 5.5378%,
      rgba(194, 235, 255, 0.98) 184.09%
    );
    opacity: 0.5;
    // backdrop-filter: blur(0.9px);
    border-radius: 20rpx;
    filter: blur(1.3px);
  }
}

.ranking-tab-item.active .ranking-tab-icon {
  top: -8rpx;
  height: 96rpx !important;
}

.ranking-tab-label {
  font-size: 22rpx;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.4);
  line-height: 1;
  white-space: nowrap;
}

.ranking-tab-icon {
  display: block;
  position: absolute;
}

.ranking-tab-item.active .ranking-tab-label {
  color: #ffffff;
}

/* 骨架屏 */
.grid-skeleton {
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 2;
  border-radius: 12px;

  background: linear-gradient(
    161.28deg,
    rgba(255, 90, 93, 0.2) 16.63%,
    rgba(76, 237, 255, 0.2) 48.19%,
    rgba(255, 122, 124, 0.2) 83.71%
  );
  // backdrop-filter: blur(9.300000190734863px);
  overflow: hidden;
}

.skeleton-card {
  width: 100%;
  height: 120rpx; /* 与新卡片 row 高度一致 */
  background: rgba(255, 255, 255, 0.1);
  border-radius: 16rpx;
  display: flex;
  align-items: center;
  padding: 12rpx 16rpx;
  box-sizing: border-box;
}

.skeleton-avatar {
  width: 56rpx;
  height: 56rpx;
  border-radius: 50%;
  background: #3a3a4a;
  flex-shrink: 0;
}

.skeleton-name {
  flex: 1;
  height: 28rpx;
  background: #3a3a4a;
  border-radius: 8rpx;
  margin-left: 16rpx;
}

.skeleton-image,
.skeleton-info {
  display: none; /* 新布局下不再使用 */
}

@keyframes shimmer {
  0% {
    background-position: 200% 0;
  }

  100% {
    background-position: -200% 0;
  }
}

/* 内容网格 */
.items-grid {
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 2;
  // background: linear-gradient(
  //   145.83deg,
  //   rgba(255, 90, 93, 0.2) 16.63%,
  //   rgba(76, 237, 255, 0.2) 48.19%,
  //   rgba(255, 122, 124, 0.2) 83.71%
  // );

  // pointer-events: none;
  // backdrop-filter: blur(4.65px);
  border-top-left-radius: 13px;
  border-top-right-radius: 12px;
  border-bottom-right-radius: 12px;
  border-bottom-left-radius: 12px;
  // opacity: 0.8;
  padding: 40rpx 20rpx 32rpx;
  overflow: hidden;
}

.grid-card {
  width: 100%;
  border-radius: 16rpx;
  position: relative;
  /* background: rgba(255, 255, 255, 0.15); */
  /* box-shadow: 2px 2px 4.5px 0px #f04b4b40; */
  box-shadow: 2px 4px 4px 0px #c92f2f5c;
  margin-bottom: 36.8rpx;
  z-index: 3;
}

/* 单行布局：藏品图片 + 头像 + 点赞信息 + TOP 标签 */
.card-row {
  display: flex;
  align-items: center;
  padding: 0 16rpx;
  width: 100%;
  height: 104rpx;
  box-sizing: border-box;
  position: relative;
}

.card-image-wrap {
  position: relative;
  width: 90rpx;
  height: 120rpx;
}
.card-image {
  width: 100%;
  height: 100%;
  border-radius: 12rpx;
  display: block;
  position: absolute;
  top: -16rpx;
  left: 32rpx;
}

/* 前 3 名专属：左上角奖牌装饰 */
.card-medal {
  position: absolute;
  top: -32rpx;
  left: 96rpx;
  width: 48rpx;
  height: 48rpx;
  z-index: 5;
  pointer-events: none;
  transform: rotate(45deg);
}

.grid-card-top-1 {
  background: url("/static/square/galaxy/TOP.png") no-repeat center;
  background-size: 100% 100%;
}

.grid-card-top-2 {
  background: url("/static/square/galaxy/TOP2.png") no-repeat center;
  background-size: 100% 100%;
}

.grid-card-top-3 {
  background: url("/static/square/galaxy/TOP3.png") no-repeat center;
  background-size: 100% 100%;
}
.grid-card-top-4 {
  position: relative;
  // overflow: hidden;
  // [方案3] 伪元素承载 bj.png，对图片单独设 opacity
  &::before {
    content: "";
    position: absolute;
    inset: 0;
    background: url("/static/square/galaxy/TOP4.png") center no-repeat;
    background-size: 100% 100%;
    opacity: 0.8; // ⬅ 调这个数控制图片透明度（0=完全透明，1=完全不透明）
    pointer-events: none;
    z-index: 0;
  }
}
.grid-card-top-5 {
  position: relative;
  // overflow: hidden;
  &::before {
    content: "";
    position: absolute;
    inset: 0;
    background: url("/static/square/galaxy/TOP4.png") center no-repeat;
    background-size: 100% 100%;
    opacity: 0.8;
    pointer-events: none;
    z-index: 0;
  }
}

.grid-card-top-other {
  position: relative;
  // overflow: hidden;

  &::before {
    content: "";
    position: absolute;
    inset: 0;
    background: url("/static/square/galaxy/TOP6.png") center no-repeat;
    background-size: 100% 100%;
    opacity: 0.8;
  }
}

/* 前 3 名专属：包裹藏品图的边框图（叠加在 card-image 之上） */
.frame-image {
  position: absolute;
  top: -16rpx;
  left: 32rpx;
  width: 100%;
  height: 100%;
  z-index: 4;
  pointer-events: none;
  display: block;
}

.like-info {
  display: flex;
  flex-direction: column; /* 从上往下：用户名 → 编号 → 点赞 */
  justify-content: center;
  margin-left: 64rpx; /* 距头像 10rpx */
  flex: 1;
  min-width: 0;
}

.user-name {
  font-size: 20rpx;
  font-weight: 800;
  color: #fffabd;
  text-shadow: -1px 1px 4px #ce0909d6;
  white-space: nowrap;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.user-number {
  font-size: 20rpx;
  font-weight: 800;
  line-height: 1.3;
  color: #fffabd;
  text-shadow: -1px 1px 4px #ce0909d6;
  white-space: nowrap;
}

.like-row {
  display: flex;
  align-items: center;
  margin-top: 6rpx;
}

.like-info .like-icon {
  width: 24rpx;
  height: 24rpx;
  margin-right: 4rpx;
}

.like-info .like-count {
  font-size: 28rpx;
  font-weight: 400;
  color: #fffabd;
  text-shadow: -1px 1px 4px #ce0909d6;
  white-space: nowrap;
}

/* Top 排名标签（顶到右边） */
.top-badge {
  margin-left: auto; /* 推到右侧 */
  margin-right: 16rpx;
  min-width: 100rpx;
  height: 36rpx;
  border-radius: 18rpx;
  padding: 0 16rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.badge-rank {
  display: flex;
  align-items: baseline;
  justify-content: center;
}

.badge-rank-icon {
  width: 90.24rpx;
  height: 40rpx;
  margin-right: 5px;
}

.badge-rank-number {
  font-family: "Abyssinica SIL";
  font-size: 84rpx;
  font-weight: 600;
  /* 渐变填充到文字 */
  background: linear-gradient(
    181.98deg,
    #fcfcf8 23.78%,
    #ffb3eb 66.88%,
    #ffeded 94.93%
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  white-space: nowrap;
}

/* 点赞动效波纹 */
.wf-like-wave {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  z-index: 1;
}

.wf-like-wave-outer {
  background: radial-gradient(
    circle,
    rgba(255, 107, 107, 0.8) 0%,
    transparent 70%
  );
}

.wf-like-wave-inner {
  background: radial-gradient(
    circle,
    rgba(255, 184, 0, 0.6) 0%,
    transparent 70%
  );
}

.wf-like-wave-active {
  animation: likeWave 0.6s ease-out forwards;
}

@keyframes likeWave {
  0% {
    opacity: 0.9;
    transform: scale(0.8);
  }

  100% {
    opacity: 0;
    transform: scale(1.5);
  }
}

/* 分页底部加载提示 */
.load-more-tip {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 16rpx 0 8rpx;
  position: relative;
  z-index: 3;
}

/* 滚动哨兵：仅作为 scroll-into-view 的锚点，不占据视觉空间 */
.scroll-anchor {
  width: 100%;
  height: 0;
  overflow: hidden;
}

.load-more-text {
  font-size: 22rpx;
  color: #fffabd;
  text-shadow: -1px 1px 4px #ce0909d6;
  opacity: 0.85;
  letter-spacing: 1rpx;
}

.load-more-tip-loading .load-more-text {
  animation: loadMorePulse 1s ease-in-out infinite;
}

@keyframes loadMorePulse {
  0%,
  100% {
    opacity: 0.5;
  }

  50% {
    opacity: 1;
  }
}
</style>
