/* ==========================================================================
   AI 导航 · 前台样式
   依赖 assets/tokens.css
   目录：公告条 / 顶栏 / Hero / 分类面板 / 榜单 / 广告位 / 工具网格 / 详情页
        / 表单页 / 页脚 / 悬浮客服 / 响应式
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. 公告条
   -------------------------------------------------------------------------- */

.notice-bar {
  background: var(--bg-sunken);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-table);
  color: var(--text-2);
}
.notice-inner {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 34px;
  flex-wrap: wrap;
}
.notice-inner b { color: var(--text-1); font-weight: 600; }
.notice-inner .notice-link { color: var(--brand-text); }
.notice-inner .notice-link:hover { text-decoration: underline; }
.notice-close {
  margin-left: auto;
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  border-radius: 4px;
  color: var(--text-3);
}
.notice-close:hover { background: var(--border); color: var(--text-1); }

/* --------------------------------------------------------------------------
   2. 顶栏（吸顶）
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.header-inner {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  height: 60px;
}

.brand { display: flex; align-items: center; gap: 10px; flex: none; }
.brand-mark {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  border-radius: var(--r-card);
  background: var(--brand);
  color: #fff;
  font-size: var(--fs-card);
  font-weight: 600;
  line-height: 1;
}
.brand-text { display: grid; gap: 1px; }
.brand-name { font-size: var(--fs-card); font-weight: 600; line-height: 1.2; }
.brand-sub { font-size: 12px; color: var(--text-3); line-height: 1.2; font-family: var(--font-mono); }

.main-nav { display: flex; align-items: center; gap: 4px; }
.main-nav a {
  display: inline-flex;
  align-items: center;
  height: 34px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-control);
  font-size: var(--fs-body);
  color: var(--text-1);
  transition: background-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.main-nav a:hover { background: var(--bg-content); color: var(--brand-text); }
.main-nav a.is-active { background: var(--brand-soft); color: var(--brand-text); font-weight: 600; }

.header-actions { display: flex; align-items: center; gap: var(--sp-1); margin-left: auto; }

/* 顶栏内嵌搜索（点击图标展开） */
.header-search { position: relative; display: flex; align-items: center; }
.header-search .search-toggle {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  border-radius: var(--r-control);
  color: var(--text-2);
  transition: background-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.header-search .search-toggle:hover { background: var(--bg-content); color: var(--brand-text); }
.header-search .search-field {
  width: 0;
  opacity: 0;
  overflow: hidden;
  transition: width var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
}
.header-search.is-open .search-field { width: 220px; opacity: 1; }
.header-search .input { height: 34px; min-height: 34px; }

/* --------------------------------------------------------------------------
   3. Hero
   -------------------------------------------------------------------------- */

.hero {
  background: var(--bg-content);
  border-bottom: 1px solid var(--border);
  padding: var(--sp-6) 0;
}
.hero-inner {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  text-align: center;
}
.hero-title {
  font-size: var(--fs-hero);
  font-weight: 600;
  line-height: var(--lh-hero);
  letter-spacing: -.5px;
}
.hero-title em { font-style: normal; color: var(--brand-text); }
.hero-sub { font-size: var(--fs-card); color: var(--text-2); }

.hero-search {
  display: flex;
  align-items: center;
  gap: 6px;
  width: min(720px, 100%);
  margin-top: var(--sp-1);
  padding: 6px 6px 6px var(--sp-3);
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: var(--r-pill);
  transition: border-color var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro) var(--ease-out);
}
.hero-search:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--brand-soft);
}
.hero-search .search-ico { flex: none; color: var(--text-3); }
.hero-search:focus-within .search-ico { color: var(--brand); }
.hero-search input {
  flex: 1 1 0;
  min-width: 0;
  height: 34px;
  border: 0;
  background: none;
  font-size: var(--fs-card);
  outline: none;
  /* 提示语是「搜索 AI 工具，如 DeepSeek、AI 绘画、视频生成」，手机宽度下装不下。
     不写这条时浏览器是在字腰上硬切一刀，末尾停在「…如 DeepSeek、 A」——
     看着像渲染坏了而不是「还有下文」。加省略号，切成「…如 DeepSeek…」。
     Chrome 认 input 上的 text-overflow，Firefox 认 ::placeholder 上的，
     所以两处都写。 */
  text-overflow: ellipsis;
}
.hero-search input::placeholder { color: var(--text-3); text-overflow: ellipsis; }
.hero-search .btn { flex: none; height: 36px; border-radius: var(--r-pill); padding: 0 var(--sp-4); }

.hot-words { display: flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: 6px; }
.hot-words .hot-label { font-size: var(--fs-table); color: var(--text-2); margin-right: 2px; }
.hot-chip {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: var(--fs-table);
  color: var(--text-2);
  transition: border-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.hot-chip:hover { border-color: var(--brand); color: var(--brand-text); }

/* --------------------------------------------------------------------------
   4. 通用区块间距
   -------------------------------------------------------------------------- */

.section { padding: var(--sp-5) 0 0; }
.section:last-of-type { padding-bottom: var(--sp-6); }
.section-alt {
  margin-top: var(--sp-5);
  padding: var(--sp-5) 0;
  background: var(--bg-content);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

/* --------------------------------------------------------------------------
   5. 分类导航区（左侧两层面板 + 右侧今日推荐）
   -------------------------------------------------------------------------- */

.cat-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--sp-3);
  align-items: start;
}

.cat-panel {
  display: grid;
  grid-template-columns: 208px minmax(0, 1fr);
  /* 与 .cat-tree 的 max-height 保持同一个数：面板里就这两列，
     树的 max-height 才是实际决定高度的那一个（右侧详情只用到 280px 左右）。
     两边写成一样的值，改的时候记得一起改。 */
  min-height: 380px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  overflow: hidden;
}

/* 分类树的可视高度必须是**行高的整数倍**。
   .cat-tree-item 是 min-height:38px，11 个大类就是 418px。原先写 396px ——
   396 ÷ 38 = 10.42，正好把第 11 行（AI资讯与社区）从字腰上切开，下半截看不见，
   看着像渲染坏了。380 = 38 × 10 落在行边界上，行数再多也只是干净地滚动。
   （行高改动时这个数要跟着重算。） */
.cat-tree {
  border-right: 1px solid var(--border);
  background: var(--bg-content);
  max-height: 380px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.cat-tree-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 38px;
  padding: 0 var(--sp-2) 0 var(--sp-3);
  border-left: 3px solid transparent;
  font-size: var(--fs-table);
  color: var(--text-1);
  text-align: left;
  transition: background-color var(--dur-micro) var(--ease-out),
              color var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.cat-tree-item .cat-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.cat-tree-item .cat-count {
  flex: none;
  font-size: 12px;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.cat-tree-item:hover { background: var(--surface); color: var(--brand-text); }
.cat-tree-item.is-active {
  background: var(--surface);
  color: var(--brand-text);
  border-left-color: var(--brand);
  font-weight: 600;
}
.cat-tree-item.is-active .cat-count { color: var(--brand-text); }

.cat-detail { padding: var(--sp-4); }
.cat-detail-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
}
.cat-detail-title { font-size: var(--fs-h2); font-weight: 600; }
.cat-detail-meta { font-size: var(--fs-meta); color: var(--text-2); }
.cat-sub-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2) var(--sp-3);
  padding-top: var(--sp-3);
}
.cat-sub {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 10px;
  border-radius: var(--r-control);
  font-size: var(--fs-table);
  color: var(--text-1);
  background: var(--bg-content);
  transition: background-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.cat-sub:hover { background: var(--brand-soft); color: var(--brand-text); }
.cat-sub .cat-sub-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.cat-sub .cat-sub-count { flex: none; font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums; }

/* 今日推荐 */
.pick-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: var(--sp-3);
}
.pick-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-h2);
  font-weight: 600;
}
.pick-head .ico { color: var(--brand); }
.pick-list { display: grid; gap: 2px; padding-top: var(--sp-2); }
.pick-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: var(--r-control);
  transition: background-color var(--dur-micro) var(--ease-out);
}
.pick-item:hover { background: var(--bg-content); }
.pick-item .tool-logo { width: 34px; height: 34px; font-size: var(--fs-body); }
.pick-body { display: grid; gap: 2px; min-width: 0; flex: 1 1 auto; }
.pick-name { font-size: var(--fs-table); font-weight: 600; line-height: 1.35; }
.pick-desc { font-size: var(--fs-meta); color: var(--text-2); line-height: 1.4; }

/* --------------------------------------------------------------------------
   6. 榜单区
   -------------------------------------------------------------------------- */

.rank2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }

.rank-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: var(--sp-3);
}
.rank-box-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
}
.rank-box-head .ico { color: var(--brand); }
.rank-box-head .rank-box-title { font-size: var(--fs-h2); font-weight: 600; flex: 1 1 auto; }
.rank-box-head .rank-box-sub { font-size: var(--fs-meta); color: var(--text-2); }

.rank-list { display: grid; }
.rank-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 6px;
  border-bottom: 1px dashed var(--border);
  transition: background-color var(--dur-micro) var(--ease-out);
}
.rank-row:last-child { border-bottom: 0; }
.rank-row:hover { background: var(--bg-content); }
.rank-no {
  flex: none;
  width: 20px;
  text-align: center;
  font-size: var(--fs-card);
  font-weight: 600;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.rank-row:nth-child(1) .rank-no { color: #DC2626; }
.rank-row:nth-child(2) .rank-no { color: #EA580C; }
.rank-row:nth-child(3) .rank-no { color: #CA8A04; }
.rank-row .tool-logo { width: 30px; height: 30px; font-size: var(--fs-table); }
.rank-body { display: grid; gap: 1px; flex: 1 1 auto; min-width: 0; }
.rank-name { font-size: var(--fs-table); font-weight: 600; line-height: 1.35; }
.rank-desc { font-size: var(--fs-meta); color: var(--text-2); line-height: 1.4; }
.rank-value {
  flex: none;
  font-size: var(--fs-table);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.rank-value.is-new { color: var(--success); }

/* --------------------------------------------------------------------------
   7. 广告位
   -------------------------------------------------------------------------- */

.ad-slot {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: 84px;
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-content);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-card);
}
.ad-badge {
  flex: none;
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 6px;
  border-radius: 4px;
  background: var(--bg-sunken);
  color: var(--text-3);
  font-size: 12px;
  line-height: 1;
}
.ad-body { display: grid; gap: 2px; min-width: 0; flex: 1 1 auto; }
.ad-title { font-size: var(--fs-card); font-weight: 600; }
.ad-desc { font-size: var(--fs-table); color: var(--text-2); }
.ad-cta { flex: none; }

/* 窄栏里的广告位（首页右栏）改成竖排。
   320px 宽横排时，徽标 + 说明 + 「广告合作」按钮挤在一行，
   说明文字会被压成一条缝，两边各自换行后又对不齐。 */
.ad-slot.is-stack {
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-2);
  min-height: 0;
  padding: var(--sp-3);
}
.ad-slot.is-stack .ad-cta { width: 100%; }

/* 投放中：创意换掉占位文案之后的形态。
   虚线框改成实线 —— 虚线是「空位」的视觉语言，广告真投出来了还挂着虚框，
   看着像这块位子仍然没人买。角标留在原地（合规要求，见 ad_slot.html），
   所以排版改成竖排：角标独占一行、创意占满整宽，省得两样东西在横排里抢位置。 */
.ad-slot.is-filled {
  display: block;
  border-style: solid;
}
.ad-slot.is-filled .ad-badge { margin-bottom: var(--sp-2); }

/* 首页右栏：广告位 + 今日推荐 + 广告位 竖着摞。
   多包这一层是因为 .cat-layout 是两列栅格，直接往里加第三个孩子会被
   挤到第二行去；广告位要留在右栏内，就得先在栏里摞起来。 */
.side-col { display: grid; gap: var(--sp-3); align-content: start; }

/* 顶部弹窗广告位：外壳是共用的 .modal-scrim/.modal，这里只补它没有的两件事
   —— 居中而不是顶上对齐、让图片广告贴满圆角（裁掉溢出）。 */
.ad-popup { align-items: center; padding: var(--sp-4); }
.ad-popup .modal { overflow: hidden; }
.ad-popup-head { padding: var(--sp-2) var(--sp-3); }

/* 文字类广告要内边距，否则首行顶在圆角上；图片类反过来要贴满，
   由 JS 给 body 加 is-image —— 两种形态共用一支模板，只能这样分。 */
.ad-popup-body { display: grid; padding: var(--sp-4); }
.ad-popup-body.is-image { padding: 0; }
.ad-popup-body > a { display: block; }
.ad-popup-body img { display: block; width: 100%; height: auto; }

/* --------------------------------------------------------------------------
   8. 工具网格
   -------------------------------------------------------------------------- */

.tool-grid {
  display: grid;
  /* 桌面 4 列（原为 5 列）。5 列时卡片只有 237px、文字区 161px，
     13px 的简介一行只排得下 12 个汉字，每张卡都被截成半句话。
     收到 4 列后文字区约 223px，配合字号上调，一行能到 16 个汉字。
     另外设计稿（v1.0.1-模板原型-参考与提示词.md）写的就是「桌面 4 列卡片」，
     5 列是当初实现时多出来的那一列。 */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-2);
}

.tool-card {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  color: var(--text-1);
  text-decoration: none;
  transition: transform var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out),
              box-shadow var(--dur-micro) var(--ease-out);
}
.tool-card:hover {
  transform: translateY(-2px);
  border-color: var(--brand);
  box-shadow: var(--sh-lift);
}
.tool-card:hover .tool-name { color: var(--brand-text); }
.tool-card .tool-body { display: grid; gap: 3px; min-width: 0; flex: 1 1 auto; }
.tool-name {
  font-size: var(--fs-card);
  font-weight: 600;
  line-height: 1.35;
  transition: color var(--dur-micro) var(--ease-out);
}
.tool-desc {
  font-size: var(--fs-table);
  color: var(--text-2);
  line-height: 1.45;
}
.tool-foot { display: flex; align-items: center; gap: 6px; margin-top: 2px; }

/* 网格的骨架加载态 */
.tool-card.is-skeleton { pointer-events: none; }
.tool-card.is-skeleton .tool-logo { background: var(--bg-sunken); color: transparent; }
.tool-card.is-skeleton .skeleton-line:first-of-type { width: 62%; }

/* --------------------------------------------------------------------------
   9. 详情页
   -------------------------------------------------------------------------- */

.detail-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--sp-3);
  align-items: start;
}

.detail-hero {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
}
.detail-hero .tool-logo { width: 64px; height: 64px; border-radius: var(--r-card); font-size: 26px; }
.detail-head { display: grid; gap: 6px; flex: 1 1 auto; min-width: 0; }
.detail-name { font-size: 24px; font-weight: 600; line-height: 1.3; }
.detail-desc { font-size: var(--fs-body); color: var(--text-2); }
.detail-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-1); margin-top: 2px; }

.detail-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.stat-cell {
  display: grid;
  gap: 2px;
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
}
.stat-num {
  font-size: var(--fs-h1);
  font-weight: 600;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
}
.stat-label { font-size: var(--fs-meta); color: var(--text-2); }

.prose-block { padding: var(--sp-4); }
.prose-block + .prose-block { border-top: 1px solid var(--border); }
.prose-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-h2);
  font-weight: 600;
}
.prose-title .ico { color: var(--brand); }
.prose-block p { font-size: var(--fs-body); color: var(--text-1); line-height: 1.75; }
.prose-block p + p { margin-top: var(--sp-2); }
.prose-list { display: grid; gap: 8px; }
.prose-list li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: var(--fs-body);
  color: var(--text-1);
  line-height: 1.65;
}
.prose-list .ico { flex: none; margin-top: 4px; color: var(--brand); }

.preview-frame {
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  overflow: hidden;
  background: var(--surface);
}
.preview-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px var(--sp-2);
  background: var(--bg-sunken);
  border-bottom: 1px solid var(--border);
}
.preview-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border-strong); }
.preview-url {
  flex: 1 1 0;
  min-width: 0;
  height: 22px;
  display: flex;
  align-items: center;
  padding: 0 8px;
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-2);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.preview-canvas {
  display: grid;
  gap: 8px;
  padding: var(--sp-3);
  background: var(--bg-content);
}

.side-card { padding: var(--sp-3); }
.side-card + .side-card { margin-top: var(--sp-3); }
.side-title {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-h2);
  font-weight: 600;
}
.side-title .ico { color: var(--brand); }
.side-list { display: grid; gap: 2px; padding-top: var(--sp-2); }

/* --------------------------------------------------------------------------
   10. 列表页 / 筛选
   -------------------------------------------------------------------------- */

.list-layout {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: start;
}

.filter-panel { padding: var(--sp-3); position: sticky; top: 76px; }
.filter-group + .filter-group { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--border); }
.filter-label {
  display: block;
  margin-bottom: 8px;
  font-size: var(--fs-table);
  color: var(--text-2);
}
.filter-opts { display: flex; flex-wrap: wrap; gap: 6px; }
.filter-opt {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 10px;
  border-radius: var(--r-control);
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: var(--fs-table);
  color: var(--text-2);
  transition: border-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out), background-color var(--dur-micro) var(--ease-out);
}
.filter-opt:hover { border-color: var(--brand); color: var(--brand-text); }
.filter-opt.is-on { background: var(--brand-soft); border-color: var(--brand-line); color: var(--brand-text); font-weight: 600; }

.list-toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  margin-bottom: var(--sp-2);
}
.result-count { font-size: var(--fs-table); color: var(--text-2); }
.result-count b { color: var(--text-1); font-weight: 600; font-variant-numeric: tabular-nums; }
.sort-tabs { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.sort-tab {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 10px;
  border-radius: var(--r-control);
  font-size: var(--fs-table);
  color: var(--text-2);
  transition: background-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.sort-tab:hover { background: var(--bg-content); color: var(--brand-text); }
.sort-tab.is-on { background: var(--brand-soft); color: var(--brand-text); font-weight: 600; }

/* --------------------------------------------------------------------------
   11. 排行榜页
   -------------------------------------------------------------------------- */

/* 显式写 grid-template-columns: minmax(0, 1fr) 不能省：
   不写的话隐式列的 auto 最小值会取行内容的 min-content，而行内
   .rank-page-body 虽然设了 min-width:0，行本身仍会被撑到内容宽度，
   在 390px 手机上把整个页面顶出横向滚动条（实测 /list、/rankings、
   /category/* 三处，行宽 470 > 容器 358）。minmax(0,1fr) 允许轨道收缩。 */
.rank-page-list { display: grid; grid-template-columns: minmax(0, 1fr); }
.rank-page-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-bottom: 1px solid var(--border);
  transition: background-color var(--dur-micro) var(--ease-out);
}
.rank-page-row:last-child { border-bottom: 0; }
.rank-page-row:hover { background: var(--bg-content); }
.rank-page-no {
  flex: none;
  width: 34px;
  font-size: var(--fs-h1);
  font-weight: 600;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.rank-page-row.is-top1 .rank-page-no { color: #DC2626; }
.rank-page-row.is-top2 .rank-page-no { color: #EA580C; }
.rank-page-row.is-top3 .rank-page-no { color: #CA8A04; }
.rank-page-body { display: grid; gap: 2px; flex: 1 1 auto; min-width: 0; }
.rank-page-name { font-size: var(--fs-card); font-weight: 600; line-height: 1.35; }
.rank-page-desc { font-size: var(--fs-table); color: var(--text-2); line-height: 1.45; }
.rank-page-metrics { display: flex; align-items: center; gap: var(--sp-4); flex: none; }
.metric { display: grid; gap: 1px; justify-items: end; }
.metric-num { font-size: var(--fs-card); font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.3; }
.metric-label { font-size: var(--fs-meta); color: var(--text-2); }
.rank-trend { display: inline-flex; align-items: center; gap: 2px; font-size: var(--fs-meta); }
.rank-trend.is-up { color: var(--danger); }
.rank-trend.is-down { color: var(--success); }
.rank-trend.is-flat { color: var(--text-3); }

/* --------------------------------------------------------------------------
   12. 表单页（提交工具）
   -------------------------------------------------------------------------- */

.form-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: var(--sp-5);
  max-width: 720px;
}
.form-grid { display: grid; gap: var(--sp-3); }
.form-row { display: flex; align-items: flex-start; gap: var(--sp-2); }
.form-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
.char-count { font-size: var(--fs-meta); color: var(--text-3); text-align: right; font-variant-numeric: tabular-nums; }

.success-panel {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
  padding: var(--sp-6) var(--sp-4);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  max-width: 720px;
}
.success-art {
  display: flex; align-items: center; justify-content: center;
  width: 72px; height: 72px;
  border-radius: 50%;
  background: var(--success-soft);
  color: var(--success);
}

/* --------------------------------------------------------------------------
   13. 页脚
   -------------------------------------------------------------------------- */

.site-footer {
  margin-top: var(--sp-6);
  background: var(--bg-content);
  border-top: 1px solid var(--border);
  padding: var(--sp-5) 0 var(--sp-4);
  font-size: var(--fs-table);
}
.footer-cols {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: var(--sp-4);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--border);
}
.footer-col-title {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-1);
}
.footer-about p { color: var(--text-2); line-height: 1.75; }
.footer-about .brand { margin-bottom: var(--sp-2); }
.footer-links { display: grid; gap: 8px; }
.footer-links a { color: var(--text-2); transition: color var(--dur-micro) var(--ease-out); }
.footer-links a:hover { color: var(--brand-text); }
.footer-contact { display: grid; gap: 8px; color: var(--text-2); }
.footer-contact .row { display: flex; align-items: center; gap: 6px; }
.footer-contact .ico { color: var(--text-3); }

.footer-bottom {
  display: grid;
  gap: 6px;
  padding-top: var(--sp-3);
  text-align: center;
  color: var(--text-2);
  font-size: var(--fs-meta);
}
.footer-bottom .icp { color: var(--text-2); }
.footer-bottom .icp a { color: var(--text-2); }
.footer-bottom .icp a:hover { color: var(--brand-text); }

/* --------------------------------------------------------------------------
   14. 右下角悬浮客服
   -------------------------------------------------------------------------- */

.float-dock {
  position: fixed;
  right: var(--sp-4);
  bottom: var(--sp-4);
  z-index: var(--z-drawer);
  display: grid;
  gap: 6px;
  justify-items: end;
}
.dock-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  border-radius: var(--r-card);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--sh-lift);
  color: var(--text-2);
  transition: background-color var(--dur-micro) var(--ease-out),
              color var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.dock-btn:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
.dock-btn .dock-label {
  position: absolute;
  right: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%);
  padding: 4px 8px;
  border-radius: var(--r-control);
  background: var(--text-1);
  color: #fff;
  font-size: var(--fs-meta);
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-micro) var(--ease-out), visibility var(--dur-micro);
}
.dock-btn:hover .dock-label { opacity: 1; visibility: visible; }
/* 「回到顶部」在页面顶部时是隐藏的（不该露着一个当下按不动的按钮），但它用的是
   opacity/visibility —— 元素仍然留在 grid 流里，占着 44px 高 + 6px 间距。
   于是这个悬浮组看着只有两个按钮，实际却按三个按钮的高度（144px）压着页面右下角，
   多出来的那 50px 挡住的地方底下什么都没有 —— 纯浪费。
   用绝对定位把它摘出文档流：不显示时不占位；显示时浮在另两个按钮正上方，
   依旧是淡入淡出，位置也接得上（6px 间距靠 calc 对齐）。 */
.dock-btn.is-top { position: absolute; bottom: calc(100% + 6px); right: 0;
                   opacity: 0; visibility: hidden; }
.dock-btn.is-top.is-on { opacity: 1; visibility: visible; }

.dock-pop {
  position: absolute;
  right: calc(100% + 10px);
  bottom: 0;
  width: 188px;
  padding: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: var(--sh-pop);
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transform: translateX(6px);
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              visibility var(--dur-base);
}
.dock-pop.is-open { opacity: 1; visibility: visible; transform: none; }
.dock-pop-title { font-size: var(--fs-table); font-weight: 600; }
.dock-pop-desc { margin-top: 2px; font-size: var(--fs-meta); color: var(--text-2); }
.qr-box {
  display: grid;
  place-items: center;
  width: 132px; height: 132px;
  margin: var(--sp-2) auto 6px;
  padding: 8px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-control);
}
.dock-caption { font-size: 12px; color: var(--text-3); }

/* --------------------------------------------------------------------------
   15. 响应式
   -------------------------------------------------------------------------- */

@media (max-width: 1200px) {
  .tool-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cat-sub-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cat-layout { grid-template-columns: minmax(0, 1fr) 280px; }
  .detail-layout { grid-template-columns: minmax(0, 1fr) 260px; }
}

@media (max-width: 1024px) {
  .wrap { padding: 0 var(--sp-3); }
  /* .tool-grid 不再在这里重复声明 —— 上面 1200px 那条已经把它压到 3 列，
     这里再写一遍是同一个值，只会让人以为 1024 又是一道断点。 */
  .cat-layout { grid-template-columns: minmax(0, 1fr); }
  .cat-panel { grid-template-columns: 180px minmax(0, 1fr); }
  .rank2 { grid-template-columns: minmax(0, 1fr); }
  .detail-layout { grid-template-columns: minmax(0, 1fr); }
  .list-layout { grid-template-columns: minmax(0, 1fr); }
  .filter-panel { position: static; }
  .footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .main-nav a { padding: 0 8px; }
}

@media (max-width: 768px) {
  .hero { padding: var(--sp-5) 0; }
  .hero-title { font-size: 26px; }
  .hero-sub { font-size: var(--fs-body); }
  .hero-search { padding-left: var(--sp-2); }
  .hero-search .btn { padding: 0 var(--sp-3); }
  .tool-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
  .detail-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .detail-hero { flex-wrap: wrap; }
  .detail-hero .tool-logo { width: 52px; height: 52px; font-size: 22px; }
  .detail-name { font-size: var(--fs-h1); }
  .cat-panel { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .cat-tree { max-height: 232px; border-right: 0; border-bottom: 1px solid var(--border); }
  .cat-sub-list { grid-template-columns: minmax(0, 1fr); }
  .form-card { padding: var(--sp-3); }
  .rank-page-metrics { gap: var(--sp-3); }
  .footer-cols { grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); }
  .float-dock { right: var(--sp-3); bottom: var(--sp-3); }

  /* 悬浮组是 fixed 的：页面滚到底时它正好压在页脚最后那行版权文字上。
     那已经是页面末尾、滚无可滚 —— 等于被压住的那截字永远看不见
     （390px 下实测遮掉 10%，600px 遮 7%，834px 以上为 0）。
     给页脚补出悬浮组实际占掉的高度，让它落到内容下方去：
     两个常驻按钮 44×2 + 间距 6 = 94px，加上「回到顶部」出现时浮在最上层的
     那 44+6 = 50px，共 144px，再加悬浮组自身离底边的距离。
     .dock-btn 的尺寸/数量或上面那条 bottom 改动时，这个数要跟着重算。 */
  .site-footer { padding-bottom: calc(144px + var(--sp-3)); }
  .rank-page-row { padding: var(--sp-2); gap: var(--sp-2); }
  .rank-page-no { width: 26px; font-size: var(--fs-h2); }
}

@media (max-width: 560px) {
  .main-nav { display: none; }
  .header-search.is-open .search-field { width: 150px; }
  .notice-close { display: none; }
  .hero-title { font-size: 22px; }
  .brand-sub { display: none; }
  .tool-grid { gap: 10px; }
  .tool-card { padding: 10px; gap: 8px; }
  .tool-card .tool-logo { width: 34px; height: 34px; font-size: var(--fs-body); }
  .tool-name { font-size: var(--fs-body); }
  .tool-desc { font-size: var(--fs-meta); }
  .detail-layout, .list-layout { gap: var(--sp-2); }
}

/* ==========================================================================
   站点补充 · 分类面板（v1.0.1）
   原型的面板按「大类 7 个子类」的假数据设计，真实数据里子类数量 2–13 不等，
   子类少的大类右侧会空出一块。这里补上「本类热门」与「全部」入口把面板填满。
   ========================================================================== */

.cat-detail-all {
  margin-left: auto;
  font-size: var(--fs-meta);
  color: var(--brand-text);
  white-space: nowrap;
}
.cat-detail-all:hover { text-decoration: underline; }

.cat-hot { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--border); }
.cat-hot-title { display: block; margin-bottom: var(--sp-2); font-size: var(--fs-meta); color: var(--text-2); }
.cat-hot-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px var(--sp-2); }

.cat-hot-item {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 4px 6px;
  border-radius: var(--r-control);
  transition: background-color var(--dur-micro) var(--ease-out);
}
.cat-hot-item:hover { background: var(--bg-content); }
.cat-hot-item:hover .cat-hot-name { color: var(--brand-text); }
.cat-hot-item .tool-logo { width: 24px; height: 24px; font-size: var(--fs-meta); border-radius: 6px; }
.cat-hot-name { font-size: var(--fs-table); color: var(--text-1); }

@media (max-width: 900px) {
  .cat-hot-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .cat-hot-list { grid-template-columns: minmax(0, 1fr); }
}

/* 广告位：图片型广告填进 .ad-body 后的样式 */
.ad-slot .ad-image { display: block; width: 100%; height: auto; border-radius: var(--r-card); }

/* 站点 Logo（后台「系统设置」里配了 logo 时用它替换品牌字块）。
   必须限死尺寸：logo.png 常见为 512×512，不限的话会按原图铺满整屏。 */
.brand-logo {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: var(--r-card);
  object-fit: contain;
}
.footer-about .brand-logo { width: 34px; height: 34px; }

/* ==========================================================================
   列表 / 榜单 / 详情 / 关于 / 提交 页局部样式
   （来自原型各页 <style> 块，原样并入，token 变量沿用 tokens.css）
   ========================================================================== */

/* ---- 侧栏信息表（详情页 / 关于页共用）---- */
.info-list { display: grid; }
.info-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-2); padding: 8px 0; font-size: var(--fs-table);
}
.info-row + .info-row { border-top: 1px solid var(--border); }
.info-row dt { color: var(--text-2); flex: none; }
.info-row dd { color: var(--text-1); text-align: right; min-width: 0; }

/* ---- 关于页正文有序列表 ---- */
.prose-block ol { display: grid; gap: 8px; padding-left: 20px; list-style: decimal; }
.prose-block ol li { font-size: var(--fs-body); color: var(--text-1); line-height: 1.75; }

/* ---- 提交页步骤条 ---- */
.step-list { display: grid; gap: 10px; counter-reset: step; }
.step-item { display: flex; align-items: flex-start; gap: 10px; font-size: var(--fs-table); color: var(--text-2); line-height: 1.65; }
.step-no {
  flex: none; width: 20px; height: 20px; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--brand-soft); color: var(--brand-text);
  font-size: var(--fs-meta); font-weight: 600;
}

/* ---- 详情页 / 列表页 / 关于页 / 提交页 响应式 ---- */
@media (max-width: 1024px) {
  .detail-layout { grid-template-columns: minmax(0, 1fr); }
  .detail-stats  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .detail-hero { flex-direction: column; }
}

/* ---- 详情页：真实截图放入原型的浏览器框内 ---- */
.preview-shot { display: block; width: 100%; height: auto; border-radius: 0 0 var(--r-card) var(--r-card); }
.preview-empty {
  display: flex; align-items: center; justify-content: center;
  min-height: 180px; padding: 24px;
  font-size: var(--fs-body); color: var(--text-3);
  background: var(--bg-sunken);
  border-radius: 0 0 var(--r-card) var(--r-card);
}

/* ---- 列表页：筛选分组之间的分隔 ---- */
.filter-group + .filter-group { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border); }
.filter-opts { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---- 详情页侧栏与列表页「相似工具」小卡描述截断 ---- */
.side-list .pick-desc { display: block; max-width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

/* ---- 详情页数据条：只展示有数据的格子（当前 3 格）---- */
.detail-stats.is-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 640px) {
  .detail-stats.is-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- 列表/榜单行整体是链接：给键盘用户一个可见的焦点态 ---- */
a.rank-page-row:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* ---- 错误页（404 / 500）：独立成页，不套 layout，故这里自带居中布局 ---- */
.error-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: 40px 20px;
  background: var(--bg-page);
}
.error-page .empty {
  width: 100%;
  max-width: 460px;
  margin: 0;
  padding: 40px 32px;
}
.error-page .empty-title { text-align: center; }
.error-page .empty-desc { text-align: center; }
.error-page .od-row { justify-content: center; flex-wrap: wrap; }
