<template>
  <div class="home-page">
    <!-- 第一层：单行紧凑布局，tabs + 搜索/筛选图标 -->
    <div v-if="categoryLayers.length > 0" class="home-category-layer1-wrap">
      <div class="home-category-layer1-row">
        <v-tabs
          :model-value="layerActiveValues[0]"
          class="home-tab-bar home-tab-bar--inline"
          @update:model-value="onCategorySelect(0, $event)"
        >
          <v-tab v-for="item in categoryLayers[0]" :key="item.id" :value="item.id">
            {{ item.label }}
          </v-tab>
        </v-tabs>
        <div class="home-category-layer1-actions">
          <v-btn
            icon
            variant="text"
            size="small"
            class="home-category-action-btn"
            :aria-label="t('common.search')"
            @click="expandSearch"
          >
            <v-icon size="20">mdi-magnify</v-icon>
          </v-btn>
          <v-btn
            icon
            variant="text"
            size="small"
            class="home-category-action-btn"
            :aria-label="t('common.filter')"
          >
            <v-icon size="20">mdi-filter-outline</v-icon>
          </v-btn>
        </div>
      </div>
      <!-- 搜索展开时：浮层输入框 + 历史记录 -->
      <transition name="home-search-overlay">
        <div v-if="searchExpanded" class="home-search-overlay">
          <div class="home-search-overlay-inner">
            <v-text-field
              ref="searchInputRef"
              v-model="searchKeyword"
              density="compact"
              hide-details
              :placeholder="t('home.searchPlaceholder')"
              prepend-inner-icon="mdi-magnify"
              variant="outlined"
              class="home-search-overlay-field"
              @blur="onSearchBlur"
              @keydown.enter="onSearchSubmit"
            />
            <v-btn
              icon
              variant="text"
              size="small"
              class="home-search-close-btn"
              :aria-label="t('common.collapse')"
              @click="collapseSearch"
            >
              <v-icon size="18">mdi-close</v-icon>
            </v-btn>
          </div>
          <div v-if="searchHistoryList.length > 0" class="home-search-history">
            <div class="home-search-history-header">
              <span class="home-search-history-title">{{ t('home.searchHistory') }}</span>
              <v-btn
                variant="text"
                size="x-small"
                color="primary"
                class="home-search-history-clear"
                @click="searchHistory.clearAll"
              >
                {{ t('common.clear') }}
              </v-btn>
            </div>
            <ul class="home-search-history-list">
              <li
                v-for="(item, idx) in searchHistoryList"
                :key="`${item}-${idx}`"
                class="home-search-history-item"
              >
                <button
                  type="button"
                  class="home-search-history-text"
                  @click="selectHistoryItem(item)"
                >
                  {{ item }}
                </button>
                <v-btn
                  icon
                  variant="text"
                  size="x-small"
                  class="home-search-history-delete"
                  :aria-label="t('common.delete')"
                  @click.stop="searchHistory.remove(idx)"
                >
                  <v-icon size="16">mdi-close</v-icon>
                </v-btn>
              </li>
            </ul>
          </div>
        </div>
      </transition>
    </div>
    <v-container fluid class="home-container">
      <!-- 第二三层：随内容滚动，回顶部时与列表第一行一起出现 -->
      <div v-if="categoryLayers.length >= 2" class="home-category-layers-23-scroll">
        <div class="home-category-layer home-category-layer--icon">
          <div class="home-category-icon-row">
            <button
              v-for="item in categoryLayers[1]"
              :key="item.id"
              type="button"
              class="home-category-icon-item"
              :class="{ 'home-category-icon-item--active': layerActiveValues[1] === item.id }"
              @click="onCategorySelect(1, item.id)"
            >
              <v-icon
                size="24"
                class="home-category-icon"
                :color="resolveCategoryIconColor(item) ?? 'grey'"
              >
                {{ item.icon || DEFAULT_CATEGORY_ICON }}
              </v-icon>
              <span class="home-category-icon-label">{{ item.label }}</span>
            </button>
          </div>
        </div>
        <div
          v-if="categoryLayers.length >= 3"
          class="home-category-layer home-category-layer--third"
        >
          <v-tabs
            :model-value="layerActiveValues[2]"
            class="home-tab-bar home-tab-bar--compact"
            @update:model-value="onCategorySelect(2, $event)"
          >
            <v-tab v-for="item in categoryLayers[2]" :key="item.id" :value="item.id">
              {{ item.label }}
            </v-tab>
          </v-tabs>
        </div>
      </div>
      <!-- 可滚动容器作为 v-pull-to-refresh 的父元素，组件据此判断 scrollTop 仅在顶部时才响应下拉 -->
      <div ref="scrollRef" class="home-list-scroll">
        <v-pull-to-refresh class="pull-to-refresh" @load="onRefresh">
          <div class="pull-to-refresh-inner">
            <div ref="listRef" class="home-list" :style="gridListStyle">
              <MarketCard
                v-for="card in eventList"
                :key="card.id"
                :id="card.id"
                :slug="card.slug"
                :market-title="card.marketTitle"
                :chance-value="card.chanceValue"
                :market-info="card.marketInfo"
                :image-url="card.imageUrl"
                :category="card.category"
                :expires-at="card.expiresAt"
                :display-type="card.displayType"
                :outcomes="card.outcomes"
                :yes-label="card.yesLabel"
                :no-label="card.noLabel"
                :is-new="card.isNew"
                :market-id="card.marketId"
                :clob-token-ids="card.clobTokenIds"
                @open-trade="onCardOpenTrade"
              />
              <div v-if="eventList.length === 0 && !loadingMore" class="home-list-empty">
                暂无数据
              </div>
            </div>
            <div v-if="eventList.length > 0" class="load-more-footer">
              <div ref="sentinelRef" class="load-more-sentinel" aria-hidden="true" />
              <div v-if="loadingMore" class="load-more-indicator">
                <v-progress-circular indeterminate size="24" width="2" />
                <span>{{ t('common.loading') }}</span>
              </div>
              <div v-else-if="noMoreEvents" class="no-more-tip">没有更多了</div>
              <v-btn
                v-else
                class="load-more-btn"
                variant="outlined"
                color="primary"
                :disabled="loadingMore"
                @click="loadMore"
              >
                {{ t('home.loadMore') }}
              </v-btn>
            </div>
          </div>
        </v-pull-to-refresh>
      </div>

      <!-- PC：对话框；手机：底部 sheet，直接显示交易表单 -->
      <v-dialog
        v-if="!isMobile"
        v-model="tradeDialogOpen"
        max-width="420"
        scrollable
        content-class="trade-dialog trade-dialog--bare"
        transition="dialog-transition"
        @click:outside="tradeDialogOpen = false"
      >
        <TradeComponent
          :key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`"
          :market="homeTradeMarketPayload"
          :initial-option="tradeDialogSide"
          @order-success="onOrderSuccess"
        />
      </v-dialog>
      <v-bottom-sheet v-else v-model="tradeDialogOpen" content-class="trade-bottom-sheet">
        <TradeComponent
          :key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`"
          :market="homeTradeMarketPayload"
          :initial-option="tradeDialogSide"
          embedded-in-sheet
          @order-success="onOrderSuccess"
        />
      </v-bottom-sheet>
    </v-container>

    <footer class="home-footer">
      <div class="footer-inner">
        <div class="footer-top">
          <div class="footer-brand">
            <div class="footer-logo">
              <span class="logo-mark">M</span>
              <span class="logo-text">Polymarket</span>
            </div>
            <p class="footer-slogan">The World's Largest Prediction Market™</p>
          </div>
          <div class="footer-links-row">
            <div class="footer-col">
              <h4 class="footer-col-title">Support & Social</h4>
              <ul class="footer-link-list">
                <li><a href="#">Contact us</a></li>
                <li><a href="#">Learn</a></li>
                <li><a href="#">X (Twitter)</a></li>
                <li><a href="#">Instagram</a></li>
                <li><a href="#">Discord</a></li>
                <li><a href="#">TikTok</a></li>
                <li><a href="#">News</a></li>
              </ul>
            </div>
            <div class="footer-col">
              <h4 class="footer-col-title">Polymarket</h4>
              <ul class="footer-link-list">
                <li><a href="#">Accuracy</a></li>
                <li><a href="#">Activity</a></li>
                <li><a href="#">Leaderboard</a></li>
                <li><a href="#">Rewards</a></li>
                <li><a href="#">Press</a></li>
                <li><a href="#">Careers</a></li>
                <li><a href="#">APIs</a></li>
              </ul>
            </div>
          </div>
        </div>
        <div class="footer-bottom">
          <div class="footer-legal-links">
            <a href="#">Adventure One QSS Inc. © 2026</a>
            <span class="sep">/</span>
            <a href="#">Privacy</a>
            <span class="sep">/</span>
            <a href="#">Terms of Use</a>
            <span class="sep">/</span>
            <a href="#">Help Center</a>
            <span class="sep">/</span>
            <a href="#">Docs</a>
          </div>
          <div class="footer-lang-social">
            <v-select
              v-model="footerLang"
              :items="['English']"
              density="compact"
              hide-details
              variant="outlined"
              class="footer-lang-select"
            />
            <div class="footer-social-icons">
              <v-icon size="20">mdi-email-outline</v-icon>
              <v-icon size="20">mdi-twitter</v-icon>
              <v-icon size="20">mdi-instagram</v-icon>
              <v-icon size="20">mdi-discord</v-icon>
              <v-icon size="20">mdi-music</v-icon>
            </div>
          </div>
        </div>
        <p class="footer-disclaimer">
          Polymarket operates globally through separate legal entities. Polymarket US is operated by
          QCX LLC d/b/a Polymarket US, a CFTC-regulated Designated Contract Market. This
          international platform is not regulated by the CFTC and operates independently. Trading
          involves substantial risk of loss. See our
          <a href="#">Terms of Service &amp; Privacy Policy</a>.
        </p>
      </div>
    </footer>
  </div>
</template>

<script setup lang="ts">
defineOptions({ name: 'Home' })
import { ref, onMounted, onUnmounted, onActivated, onDeactivated, nextTick, computed } from 'vue'
import { useDisplay } from 'vuetify'
import MarketCard from '../components/MarketCard.vue'
import TradeComponent from '../components/TradeComponent.vue'
import {
  getPmEventPublic,
  mapEventItemToCard,
  getEventListCache,
  setEventListCache,
  clearEventListCache,
  type EventCardItem,
} from '../api/event'
import {
  DEFAULT_CATEGORY_ICON,
  enrichWithIcons,
  getPmTagMain,
  MOCK_CATEGORY_TREE,
  resolveCategoryIconColor,
  type CategoryTreeNode,
} from '../api/category'
import { USE_MOCK_CATEGORY } from '../config/mock'
import { useI18n } from 'vue-i18n'
import { useSearchHistory } from '../composables/useSearchHistory'
import { useToastStore } from '../stores/toast'

const { mobile } = useDisplay()
const { t } = useI18n()
const searchHistory = useSearchHistory()
const searchHistoryList = computed(() => searchHistory.list.value)
const isMobile = computed(() => mobile.value)

/** 分类树（顶层） */
const categoryTree = ref<CategoryTreeNode[]>([])
/** 每层选中的 id，[layer0, layer1?, layer2?] */
const layerActiveValues = ref<string[]>([])
/** 第三层搜索框是否展开 */
const searchExpanded = ref(false)
/** 搜索关键词 */
const searchKeyword = ref('')
const searchInputRef = ref<{ focus: () => void } | null>(null)

function expandSearch() {
  searchExpanded.value = true
  nextTick(() => {
    const el = searchInputRef.value as { focus?: () => void } | null
    el?.focus?.()
  })
}

function collapseSearch() {
  searchExpanded.value = false
  searchKeyword.value = ''
  if (activeSearchKeyword.value) {
    activeSearchKeyword.value = ''
    clearEventListCache()
    eventPage.value = 1
    loadEvents(1, false, '')
  }
}

function onSearchBlur() {
  setTimeout(() => {
    if (!searchKeyword.value.trim()) searchExpanded.value = false
  }, 100)
}

function onSearchSubmit() {
  const k = searchKeyword.value.trim()
  if (k) {
    searchHistory.add(k)
    doSearch(k)
  }
}

function selectHistoryItem(item: string) {
  searchKeyword.value = item
  searchHistory.add(item)
  doSearch(item)
}

function doSearch(keyword: string) {
  activeSearchKeyword.value = keyword
  clearEventListCache()
  eventPage.value = 1
  loadEvents(1, false, keyword)
}

/** 过滤：仅当 forceShow 明确为 false 时不显示，其他（null/undefined/true 等）均显示；排除 forceHide 的节点 */
function filterVisible(nodes: CategoryTreeNode[] | undefined): CategoryTreeNode[] {
  if (!nodes?.length) return []
  return nodes.filter((n) => n.forceShow !== false && !n.forceHide)
}

/** 第三层区块标题：取当前选中的第一层节点的 sectionTitle 或 label */
const selectedLayer0SectionTitle = computed(() => {
  const root = filterVisible(categoryTree.value)
  const id = layerActiveValues.value[0]
  const node = id ? root.find((n) => n.id === id) : root[0]
  return node?.sectionTitle ?? node?.label ?? ''
})

/** 从树中递归查找节点 */
function findNodeById(nodes: CategoryTreeNode[], id: string): CategoryTreeNode | undefined {
  for (const n of nodes) {
    if (n.id === id) return n
    const found = n.children ? findNodeById(n.children, id) : undefined
    if (found) return found
  }
  return undefined
}

/** 当前选中分类的 tag 筛选（取最后选中的层级，用于 API tagId/tagSlug） */
const activeTagFilter = computed(() => {
  const ids = layerActiveValues.value
  if (ids.length === 0) return { tagId: undefined as number | undefined, tagSlug: undefined as string | undefined }
  const lastId = ids[ids.length - 1]
  const root = filterVisible(categoryTree.value)
  const node = lastId ? findNodeById(root, lastId) : undefined
  if (!node) return { tagId: undefined, tagSlug: undefined }
  const tagId = /^\d+$/.test(node.id) ? parseInt(node.id, 10) : undefined
  return { tagId, tagSlug: node.slug || undefined }
})

/** 当前展示的层级数据：[[layer0], [layer1]?, [layer2]?] */
const categoryLayers = computed(() => {
  const root = filterVisible(categoryTree.value)
  if (root.length === 0) return []

  const layers: CategoryTreeNode[][] = [root]
  const active = layerActiveValues.value

  let currentNodes = root
  for (let i = 0; i < 2; i++) {
    const selectedId = active[i]
    const node = selectedId ? currentNodes.find((n) => n.id === selectedId) : currentNodes[0]
    const children = filterVisible(node?.children)
    if (children.length === 0) break
    layers.push(children)
    currentNodes = children
  }
  return layers
})

/** 分类选中时：若有 children 则展开下一层并默认选中第一个，并重新加载列表 */
function onCategorySelect(layerIndex: number, selectedId: string) {
  const nextValues = [...layerActiveValues.value]
  nextValues[layerIndex] = selectedId
  nextValues.length = layerIndex + 1

  const layers = categoryLayers.value
  const layer = layers[layerIndex]
  const node = layer?.find((n) => n.id === selectedId)
  const children = filterVisible(node?.children)

  const firstChild = children[0]
  if (firstChild && layerIndex < 2) {
    nextValues.push(firstChild.id)
  }
  layerActiveValues.value = nextValues

  clearEventListCache()
  eventPage.value = 1
  loadEvents(1, false)
}

/** 初始化分类选中：默认选中第一个，若有 children 则递归展开 */
function initCategorySelection() {
  const root = filterVisible(categoryTree.value)
  if (root.length === 0) return

  const values: string[] = []
  let current = root
  for (let i = 0; i < 3; i++) {
    const first = current[0]
    if (!first) break
    values.push(first.id)
    const children = filterVisible(first.children)
    if (children.length === 0) break
    current = children
  }
  layerActiveValues.value = values
}

const PAGE_SIZE = 10

/** 接口返回的列表（已映射为卡片所需结构） */
const eventList = ref<EventCardItem[]>([])
/** 当前页码（从 1 开始） */
const eventPage = ref(1)
/** 接口返回的 total */
const eventTotal = ref(0)
const eventPageSize = ref(PAGE_SIZE)
const loadingMore = ref(false)

const noMoreEvents = computed(() => {
  if (eventList.value.length === 0) return false
  return (
    eventList.value.length >= eventTotal.value ||
    eventPage.value * eventPageSize.value >= eventTotal.value
  )
})

const footerLang = ref('English')
const tradeDialogOpen = ref(false)
const tradeDialogSide = ref<'yes' | 'no'>('yes')
const tradeDialogMarket = ref<{
  id: string
  title: string
  marketId?: string
  clobTokenIds?: string[]
  yesLabel?: string
  noLabel?: string
} | null>(null)
const scrollRef = ref<HTMLElement | null>(null)

function onCardOpenTrade(
  side: 'yes' | 'no',
  market?: { id: string; title: string; marketId?: string; yesLabel?: string; noLabel?: string },
) {
  tradeDialogSide.value = side
  tradeDialogMarket.value = market ?? null
  tradeDialogOpen.value = true
}

const toastStore = useToastStore()
function onOrderSuccess() {
  tradeDialogOpen.value = false
  toastStore.show(t('toast.orderSuccess'))
}

/** 传给 TradeComponent 的 market（Home 弹窗/底部栏），供 Split、下单等使用 */
const homeTradeMarketPayload = computed(() => {
  const m = tradeDialogMarket.value
  if (!m) return undefined
  const marketId = m.marketId ?? m.id
  const chance = 50
  const yesPrice = Math.min(1, Math.max(0, chance / 100))
  const noPrice = 1 - yesPrice
  const outcomes =
    m.yesLabel != null || m.noLabel != null
      ? [m.yesLabel ?? 'Yes', m.noLabel ?? 'No']
      : undefined
  return { marketId, yesPrice, noPrice, title: m.title, clobTokenIds: m.clobTokenIds, outcomes }
})

const sentinelRef = ref<HTMLElement | null>(null)
let observer: IntersectionObserver | null = null
let resizeObserver: ResizeObserver | null = null

const SCROLL_LOAD_THRESHOLD = 280

/** 卡片最小宽度（与 MarketCard 一致），用于动态计算列数 */
const CARD_MIN_WIDTH = 310
const GRID_GAP = 20

const listRef = ref<HTMLElement | null>(null)
const gridColumns = ref(1)

const gridListStyle = computed(() => ({
  gridTemplateColumns: `repeat(${gridColumns.value}, 1fr)`,
}))

function updateGridColumns() {
  const el = listRef.value
  if (!el) return
  const width = el.getBoundingClientRect().width
  const n = Math.floor((width + GRID_GAP) / (CARD_MIN_WIDTH + GRID_GAP))
  gridColumns.value = Math.max(1, n)
}

/** 当前生效的搜索关键词（用于分页加载） */
const activeSearchKeyword = ref('')

/** 请求事件列表并追加或覆盖到 eventList（公开接口，无需鉴权）；成功后会更新内存缓存 */
async function loadEvents(page: number, append: boolean, keyword?: string) {
  const kw = keyword !== undefined ? keyword : activeSearchKeyword.value
  const { tagId, tagSlug } = activeTagFilter.value
  try {
    const res = await getPmEventPublic({
      page,
      pageSize: PAGE_SIZE,
      keyword: kw || undefined,
      tagId,
      tagSlug,
    })
    if (res.code !== 0 && res.code !== 200) {
      throw new Error(res.msg || '请求失败')
    }
    const data = res.data
    if (!data?.list || !Array.isArray(data.list)) {
      if (!append) eventList.value = []
      return
    }
    const mapped = data.list.map((item) => mapEventItemToCard(item))
    eventTotal.value = data.total ?? 0
    eventPageSize.value = data.pageSize && data.pageSize > 0 ? data.pageSize : PAGE_SIZE
    eventPage.value = data.page ?? page
    if (append) {
      eventList.value = [...eventList.value, ...mapped]
    } else {
      eventList.value = mapped
    }
    setEventListCache({
      list: eventList.value,
      page: eventPage.value,
      total: eventTotal.value,
      pageSize: eventPageSize.value,
    })
  } catch (e) {
    if (!append) eventList.value = []
    console.warn('getPmEventList failed', e)
  }
}

function onRefresh({ done }: { done: () => void }) {
  clearEventListCache()
  eventPage.value = 1
  loadEvents(1, false, activeSearchKeyword.value).finally(() => done())
}

function loadMore() {
  if (loadingMore.value || noMoreEvents.value || eventList.value.length === 0) return
  loadingMore.value = true
  const nextPage = eventPage.value + 1
  loadEvents(nextPage, true).finally(() => {
    loadingMore.value = false
  })
}

function checkScrollLoad() {
  if (loadingMore.value || eventList.value.length === 0 || noMoreEvents.value) return
  const { scrollY, innerHeight } = window
  const scrollHeight = document.documentElement.scrollHeight
  if (scrollHeight - scrollY - innerHeight < SCROLL_LOAD_THRESHOLD) loadMore()
}

onMounted(() => {
  /** 分类树就绪后加载列表（确保 activeTagFilter 已计算，与下拉刷新参数一致） */
  function loadEventListAfterCategoryReady() {
    const cached = getEventListCache()
    if (cached && cached.list.length > 0) {
      eventList.value = cached.list
      eventPage.value = cached.page
      eventTotal.value = cached.total
      eventPageSize.value = cached.pageSize
    } else {
      loadEvents(1, false)
    }
  }

  if (USE_MOCK_CATEGORY) {
    categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
    initCategorySelection()
    loadEventListAfterCategoryReady()
  } else {
    getPmTagMain()
      .then((res) => {
        if (res.code === 0 || res.code === 200) {
          const data = res.data
          categoryTree.value = Array.isArray(data) && data.length > 0 ? data : enrichWithIcons(MOCK_CATEGORY_TREE)
        } else {
          categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
        }
        initCategorySelection()
        loadEventListAfterCategoryReady()
      })
      .catch(() => {
        categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
        initCategorySelection()
        loadEventListAfterCategoryReady()
      })
  }
  nextTick(() => {
    const sentinel = sentinelRef.value
    if (sentinel) {
      observer = new IntersectionObserver(
        (entries) => {
          if (!entries[0]?.isIntersecting) return
          loadMore()
        },
        { root: null, rootMargin: `${SCROLL_LOAD_THRESHOLD}px`, threshold: 0 },
      )
      observer.observe(sentinel)
    }
    window.addEventListener('scroll', checkScrollLoad, { passive: true })
    const listEl = listRef.value
    if (listEl) {
      updateGridColumns()
      resizeObserver = new ResizeObserver(updateGridColumns)
      resizeObserver.observe(listEl)
    }
  })
})

function removeScrollListeners() {
  const sentinel = sentinelRef.value
  if (observer && sentinel) observer.unobserve(sentinel)
  observer = null
  if (resizeObserver) {
    resizeObserver.disconnect()
    resizeObserver = null
  }
  window.removeEventListener('scroll', checkScrollLoad)
}

// keep-alive 时离开页面不会触发 onUnmounted，需在 onDeactivated 移除监听，否则详情页滚动到底会误触发 loadMore
onDeactivated(removeScrollListeners)
onUnmounted(removeScrollListeners)

// 从详情页返回时重新注册监听（仅当 observer 已被 removeScrollListeners 清空时）
onActivated(() => {
  nextTick(() => {
    const sentinel = sentinelRef.value
    if (sentinel && !observer) {
      observer = new IntersectionObserver(
        (entries) => {
          if (!entries[0]?.isIntersecting) return
          loadMore()
        },
        { root: null, rootMargin: `${SCROLL_LOAD_THRESHOLD}px`, threshold: 0 },
      )
      observer.observe(sentinel)
      window.addEventListener('scroll', checkScrollLoad, { passive: true })
    }
    const listEl = listRef.value
    if (listEl && !resizeObserver) {
      updateGridColumns()
      resizeObserver = new ResizeObserver(updateGridColumns)
      resizeObserver.observe(listEl)
    }
  })
})
</script>

<style scoped>
/* fluid 后无断点 max-width，用自定义 max-width 让列表在 2137px 等宽屏下能算到 6 列 */
.home-container {
  min-height: 100vh;
  max-width: 2560px;
  margin-left: auto;
  margin-right: auto;
}

.home-header {
  margin-bottom: 20px;
}

.home-title {
  font-size: 3rem;
  font-weight: bold;
  color: #0066cc;
  margin: 0;
}

.pull-to-refresh {
  width: 100%;
  margin-top: 8px;
  min-height: 100%;
}

.pull-to-refresh-inner {
  min-height: 100%;
}

/* 不设固定高度与 overflow，列表随页面（窗口）滚动，便于 Vue Router scrollBehavior 自动恢复位置 */
.home-list-scroll {
  width: 100%;
  overflow-x: hidden;
}

/* 列数由 JS 根据容器宽度与 CARD_MIN_WIDTH 连续计算，避免断点导致 6→4 跳变 */
.home-list {
  width: 100%;
  display: grid;
  gap: 20px;
}

.home-list-empty {
  grid-column: 1 / -1;
  text-align: center;
  color: #6b7280;
  font-size: 14px;
  padding: 48px 16px;
}

.home-list > * {
  min-width: 0;
  width: 100%;
}

.load-more-footer {
  min-height: 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
}

.load-more-sentinel {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 8px;
  pointer-events: none;
  opacity: 0;
}

.load-more-indicator,
.no-more-tip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px;
  color: #666;
  font-size: 14px;
}

.load-more-btn {
  text-transform: none;
  margin-top: 4px;
}

.trade-dialog--bare :deep(.v-overlay__content) {
  padding: 0;
  overflow: visible;
}
.trade-dialog--bare :deep(.v-card) {
  box-shadow: none;
}

.trade-bottom-sheet :deep(.v-overlay__content) {
  padding: 0;
}

.home-subtitle {
  margin-bottom: 40px;
}

.home-subtitle p {
  font-size: 1.2rem;
  color: #666666;
  margin: 0;
}

.home-tabs {
  margin-top: 40px;
}

/* 第一层：置顶、全宽 */
.home-category-layer1-wrap {
  width: 100vw;
  max-width: 100vw;
  margin-left: calc(50% - 50vw);
  position: sticky;
  top: 64px;
  z-index: 10;
  background-color: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.home-category-layer1-row {
  display: flex;
  align-items: center;
  min-height: 48px;
}

.home-tab-bar--inline {
  flex: 1;
  min-width: 0;
}

.home-tab-bar--inline :deep(.v-tabs) {
  min-height: 48px;
}

.home-tab-bar--inline :deep(.v-tab) {
  min-height: 48px;
}

.home-category-layer1-actions {
  display: flex;
  flex-shrink: 0;
  gap: 0;
}

/* 搜索浮层：绝对定位，遮挡第二三层，不顶开布局 */
.home-search-overlay {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 11;
  padding: 8px 12px 12px;
  background-color: white;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  border-top: 1px solid rgba(0, 0, 0, 0.06);
}

.home-search-overlay-inner {
  display: flex;
  align-items: center;
  gap: 8px;
}

.home-search-overlay-field {
  flex: 1;
  min-width: 0;
  font-size: 14px;
}

.home-search-close-btn {
  flex-shrink: 0;
}

.home-search-overlay-field :deep(.v-field) {
  background-color: #f8fafc;
}

.home-search-overlay-enter-active,
.home-search-overlay-leave-active {
  transition:
    opacity 0.15s ease,
    transform 0.15s ease;
}

.home-search-overlay-enter-from,
.home-search-overlay-leave-to {
  opacity: 0;
}

.home-search-history {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid rgba(0, 0, 0, 0.06);
}

.home-search-history-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.home-search-history-title {
  font-size: 12px;
  color: #64748b;
}

.home-search-history-clear {
  min-width: auto;
  padding: 0 4px;
  font-size: 12px;
}

.home-search-history-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 160px;
  overflow-y: auto;
}

.home-search-history-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.04);
}

.home-search-history-item:last-child {
  border-bottom: none;
}

.home-search-history-text {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: none;
  background: none;
  font-size: 14px;
  color: #1e293b;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.home-search-history-text:hover {
  color: rgb(var(--v-theme-primary));
}

.home-search-history-delete {
  flex-shrink: 0;
  min-width: 28px;
  color: #94a3b8;
}

.home-search-history-delete:hover {
  color: #ef4444;
}

/* 第二三层：随内容滚动，全宽 */
.home-category-layers-23-scroll {
  width: 100vw;
  max-width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-bottom: 0;
  background-color: white;
}

.home-category-layer {
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.home-category-layer:last-child {
  border-bottom: none;
}

.home-category-layer--text :deep(.v-tabs) {
  min-height: 48px;
}

.home-category-layer--icon {
  padding: 12px 16px;
}

.home-category-icon-row {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 4px;
  scrollbar-width: none;
}

.home-category-icon-row::-webkit-scrollbar {
  display: none;
}

.home-category-icon-item {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 56px;
  padding: 8px 12px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: #64748b;
  font-size: 12px;
  cursor: pointer;
  transition:
    background-color 0.2s,
    color 0.2s;
}

.home-category-icon-item:hover {
  background-color: rgba(0, 0, 0, 0.04);
  color: #334155;
}

.home-category-icon-item--active {
  background-color: rgb(var(--v-theme-primary));
  color: rgb(var(--v-theme-on-primary));
}

.home-category-icon {
  flex-shrink: 0;
}

.home-category-icon-placeholder {
  display: block;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
}

.home-category-icon-label {
  white-space: nowrap;
}

.home-category-layer--third {
  padding: 12px 16px 0;
}

.home-category-action-btn {
  min-width: 36px;
}

.home-tab-bar {
  position: relative;
  top: auto;
  left: auto;
  transform: none;
  width: 100%;
  background-color: transparent;
  margin-bottom: 0;
  box-shadow: none;
}

.home-tab-bar--compact :deep(.v-tab) {
  min-height: 40px;
  font-size: 14px;
}

.home-card {
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  padding: 24px;
  text-align: center;
}

.home-card-text {
  margin-bottom: 24px;
}

.home-btn {
  margin-top: 16px;
}

.market-card-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 20px;
  padding: 20px 0;
  width: 100%;
  max-width: 100%;
}

/* Mobile view */
@media (max-width: 600px) {
  .market-card-container {
    grid-template-columns: 1fr;
  }
}

/* Footer */
.home-page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.home-footer {
  width: 100%;
  background-color: #374151;
  color: rgba(255, 255, 255, 0.85);
  margin-top: auto;
  padding: 48px 24px 32px;
}

.footer-inner {
  max-width: 1200px;
  margin: 0 auto;
}

.footer-top {
  display: flex;
  flex-wrap: wrap;
  gap: 48px;
  padding-bottom: 32px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.footer-brand {
  flex-shrink: 0;
}

.footer-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}

.logo-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-weight: 700;
  font-size: 18px;
  border-radius: 6px;
}

.logo-text {
  font-size: 1.25rem;
  font-weight: 600;
  color: #fff;
}

.footer-slogan {
  font-size: 0.875rem;
  color: rgba(255, 255, 255, 0.7);
  margin: 0;
}

.footer-links-row {
  display: flex;
  gap: 64px;
  flex-wrap: wrap;
}

.footer-col-title {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: rgba(255, 255, 255, 0.9);
  margin: 0 0 12px 0;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}

.footer-link-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer-link-list li {
  margin-bottom: 6px;
}

.footer-link-list a {
  color: rgba(255, 255, 255, 0.75);
  text-decoration: none;
  font-size: 0.875rem;
}

.footer-link-list a:hover {
  color: #fff;
}

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 24px;
  padding-bottom: 24px;
}

.footer-legal-links {
  font-size: 0.8125rem;
}

.footer-legal-links a {
  color: rgba(255, 255, 255, 0.75);
  text-decoration: none;
}

.footer-legal-links a:hover {
  color: #fff;
}

.footer-legal-links .sep {
  margin: 0 8px;
  color: rgba(255, 255, 255, 0.4);
}

.footer-lang-social {
  display: flex;
  align-items: center;
  gap: 16px;
}

.footer-lang-select {
  max-width: 120px;
}

.footer-lang-select :deep(.v-field) {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  font-size: 0.875rem;
}

.footer-social-icons {
  display: flex;
  gap: 12px;
  color: rgba(255, 255, 255, 0.8);
}

.footer-social-icons .v-icon {
  cursor: pointer;
}

.footer-social-icons .v-icon:hover {
  color: #fff;
}

.footer-disclaimer {
  font-size: 0.75rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.6);
  margin: 0;
  max-width: 720px;
}

.footer-disclaimer a {
  color: rgba(255, 255, 255, 0.85);
  text-decoration: underline;
}

.footer-disclaimer a:hover {
  color: #fff;
}

@media (max-width: 600px) {
  .home-footer {
    padding: 32px 16px 24px;
  }
  .footer-top {
    flex-direction: column;
    gap: 32px;
    padding-bottom: 24px;
  }
  .footer-links-row {
    gap: 32px;
  }
  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
