/* ==========================================================================
   idcub 主题 · 商城组件 (Shop)
   分类胶囊 / 商品网格 / 商品卡 / 详情页 / 登录页
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. 分类胶囊 · Category Pills
   横向滚动，选中态填品牌渐变
   -------------------------------------------------------------------------- */
.nv-cats {
  position: relative;
  margin-bottom: var(--nv-space-6);
}

.nv-cats::-webkit-scrollbar { display: none; }

.nv-cats {
  display: flex;
  gap: var(--nv-space-2);
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  padding-block: var(--nv-space-1);
}

.nv-cat {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-space-2);
  flex-shrink: 0;
  height: 38px;
  padding-inline: var(--nv-space-4);
  border-radius: var(--nv-radius-pill);
  border: 1px solid var(--nv-border);
  background-color: var(--nv-elevated);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-text-secondary);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition:
    background-color var(--nv-dur-fast) var(--nv-ease-out),
    border-color var(--nv-dur-fast) var(--nv-ease-out),
    color var(--nv-dur-fast) var(--nv-ease-out),
    box-shadow var(--nv-dur-normal) var(--nv-ease-out);
}

/* ★ 选中态必须同时认【两个类名】—— 这不是冗余，背后是两套互不知道对方的维护者：
     · 核心 index.js 的 _SwitchCategory() 加 .is-primary
       （第 83 行在页面加载时就调用一次，所以初始态实际由核心保证）；
     · 本主题的 initCategoryBar() 加 .is-active。
     只认一个就会出现「点了分类但胶囊条不高亮」，或搜索后高亮残留在旧分类上。
     （2026-10-16 实测：Index.html 原先连 data-nv-cats 都没有，
       initCategoryBar() 第 160 行直接 return —— 移动端胶囊条从未有过高亮态） */
.nv-cat:hover:not(.is-active):not(.is-primary):not(.is-disabled) {
  border-color: var(--nv-border-brand);
  background-color: var(--nv-brand-softer);
  color: var(--nv-brand-ink);
}

/* 选中态：品牌渐变填充 */
.nv-cat.is-active,
.nv-cat.is-primary {
  background-image: var(--nv-gradient);
  border-color: transparent;
  color: #fff;
  box-shadow: var(--nv-shadow-brand);
}

/* 商品数为 0 的分类：弱化但仍可点（用户能理解"暂无商品"而非"坏了"） */
.nv-cat.is-disabled {
  opacity: .45;
  cursor: default;
}

.nv-cat__icon {
  width: 20px; height: 20px;
  border-radius: var(--nv-radius-sm);
  object-fit: cover;
  flex-shrink: 0;
}

/* 二级分类缩进条 */
.nv-cat__sub {
  width: 3px;
  height: 12px;
  border-radius: var(--nv-radius-pill);
  background-color: var(--nv-border-strong);
  flex-shrink: 0;
}
.nv-cat.is-active .nv-cat__sub,
.nv-cat.is-primary .nv-cat__sub { background-color: rgba(255, 255, 255, .8); }

/* --------------------------------------------------------------------------
   2. 页头 · Page Head
   -------------------------------------------------------------------------- */
.nv-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--nv-space-4);
  margin-bottom: var(--nv-space-5);
}

.nv-head__title {
  font-size: var(--nv-fs-title);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-ls-title);
}

.nv-head__count {
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}

/* --------------------------------------------------------------------------
   3. 商品网格 · Grid
   移动 2 列 / 平板 3 列 / 桌面 4 列
   -------------------------------------------------------------------------- */
.nv-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--nv-space-4);
}

@media (min-width: 640px) {
  .nv-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--nv-space-5); }
}
@media (min-width: 1024px) {
  .nv-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 1440px) {
  .nv-grid { gap: var(--nv-space-6); }
}

/* 扁平卡列表容器
   ------------------------------------------------------------------------
   ★★ 踩坑记录：这里【不能】写 .item-list.nv-grid。
      核心 index.js 渲染列表时会【把 nv-grid 类移除】，
      最终 DOM 只剩 class="nv-list item-list"。
      用 .nv-grid 做限定条件的规则永远匹配不上，
      表现为卡片撑满整宽（本该是单列扁卡）。

      正确做法：只依赖核心自己保留的 .item-list 类。
      用双类 .item-list.nv-list 保证权重高于上面的 .nv-grid。
   ------------------------------------------------------------------------ */
.item-list.nv-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* 列表模式 */
.nv-grid--list { grid-template-columns: 1fr; }
@media (min-width: 768px) {
  .nv-grid--list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --------------------------------------------------------------------------
   4. 商品卡 · Product Card
   结构：顶部渐变细条（签名）→ 浅品牌色图区 → 白底价格行
   -------------------------------------------------------------------------- */

/* hover：上浮 + 阴影加深 */
@media (hover: hover) {
}

/* 触摸设备没有 hover，用 active 给即时反馈 */
@media (hover: hover) {
}

/* 主图：白底毛玻璃方块 */
@media (hover: hover) {
}

/* 图片加载失败：显示首字 */

.nv-pcard.is-soldout { opacity: .85; }
.nv-pcard.is-soldout .nv-pcard__thumb { filter: grayscale(.25); }

/* 限时秒杀角标 */

@media (min-width: 1024px) {
}

/* --------------------------------------------------------------------------
   6. 商品详情页 · Product Detail
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   面包屑 · .nv-breadcrumb
   --------------------------------------------------------------------------
   ★ 2026-10-15 重做。原实现的问题：
     1. 分隔符（--nv-text-faint #86868b）与文字（--nv-text-muted #6e6e73）
        只差一档，视觉上是一条平铺的字符串，看不出层级
     2. 末项（当前页）继承父级灰色，与可点击项长得一样 ——
        用户扫一眼不知道"我在哪"，而这是面包屑最核心的作用
     3. 无 hover 反馈，只能靠鼠标指针判断是否可点
     4. 末项长标题会换行撑开，破坏下方内容的起始位置

   ★ 改法（全部基于既有令牌，零新增颜色）：
     · 可点项加 padding + 圆角，hover 用 --nv-brand-soft 浅底
     · 末项换 --nv-text-primary + 500 字重，双重强调建立"当前页"权重
     · 分隔符降到 --nv-breadcrumb-sep（比 faint 更淡），退到背景层
     · 末项加 max-width + 省略号，防长标题破坏布局
   ══════════════════════════════════════════════════════════════════════════ */
.nv-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px;
  margin-bottom: var(--nv-space-4);
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}

/* 可点项：chip 形态，hover 有明确反馈 */
.nv-breadcrumb a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border-radius: var(--nv-radius-chip);
  color: var(--nv-text-muted);
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out),
              color var(--nv-dur-fast) var(--nv-ease-out);
}
.nv-breadcrumb a:hover {
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
}

/* 分隔符：用 chevron 图标替代斜杠，气质更轻。
   颜色比 faint 再淡一档，明确退到背景层。 */
.nv-breadcrumb__sep {
  display: inline-flex;
  align-items: center;
  color: var(--nv-breadcrumb-sep);
  font-size: 0;            /* 隐藏旧的 "/" 文本，只留图标 */
  flex: 0 0 auto;
}
.nv-breadcrumb__sep::before {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 1.5px solid currentColor;
  border-top: 1.5px solid currentColor;
  transform: rotate(45deg);
  border-radius: 1px;
}

/* 首页图标：Material Icons 默认 24px，在 13px 的字号里会撑破行高 */
.nv-breadcrumb .material-icons-outlined { font-size: 14px; }

/* 分类段：一级、二级都【仅文字、不可点】
   ★ 详情页没有商品列表可筛选，分类链过去是空跳转，所以两级都不给可点反馈。
     视觉上比可点项弱一档：与普通文字同色（--nv-text-muted），
     不加 chip padding 与 hover 底色 —— 靠"没有 hover 反馈"本身
     传达"这不是链接"。（对照：首页图标是 <a>，走上面的 chip 样式） */
.nv-breadcrumb__cat [data-nv-crumb-parent],
.nv-breadcrumb__cat [data-nv-crumb-self] {
  padding: 4px 9px;
  color: var(--nv-text-muted);
  white-space: nowrap;
}

/* ★ hidden 兜底：本文件给 .nv-breadcrumb__sep 写了 display:inline-flex，
   它会盖掉浏览器默认的 [hidden]{display:none} —— 于是"隐藏分隔符"
   反而更显眼（比可见时还多一个箭头）。这三条把 hidden 拉回最高优先。
   注意父级 .nv-breadcrumb__cat 在 shop.css 里没有 display 声明，
   不会被覆盖，但一起写进来以免将来有人给它加 flex 时又踩同一个坑。 */
.nv-breadcrumb__cat[hidden],
.nv-breadcrumb__sep[hidden] {
  display: none !important;
}

/* 末项 = 当前位置：加重 + 截断保护 */
.nv-breadcrumb > :last-child {
  padding: 4px 9px;
  color: var(--nv-text-primary);
  font-weight: var(--nv-weight-medium);
  max-width: 42ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 窄屏：末项截得更短，避免把行撑爆 */
@media (max-width: 640px) {
  .nv-breadcrumb > :last-child { max-width: 20ch; }
  .nv-breadcrumb a { padding: 4px 6px; }
}

@media (min-width: 900px) {
  /* ★ 左栏从 480px 加到 520px：给主图更多展示面积，
     原比例下主图偏小、右侧拥挤。 */
}

/* ══════════════════════════════════════════════════════════════════════
   6.0 详情区两栏：左分类侧栏 + 右宝贝详情
   ----------------------------------------------------------------------
   ★★ 2026-10-14 位置修正（用户明确要求）：
     侧栏最初放在产品区最左（与商品图同排），但用户要的是
     「分类出现在【宝贝详情】的左侧」——
     所以现在它与 .nv-detail__desc 组成独立的两栏区块 .nv-below，
     产品区（图 + 信息）保持原来的两栏不动。

   ★ 三段式降级：
       ≥1200px  侧栏 + 详情（两栏）
       <1200px  侧栏隐藏，详情占满整宽
     详情文本是长内容，窄屏硬塞侧栏会让正文只有 300px 宽，非常难读。
   ══════════════════════════════════════════════════════════════════════ */
.nv-below {
  display: grid;
  grid-template-columns: 1fr;      /* 默认单栏：侧栏隐藏 */
  gap: var(--nv-space-6);
  align-items: start;
  margin-top: var(--nv-space-8);
}

/* 窄屏/中屏隐藏侧栏 */

@media (min-width: 1200px) {
  .nv-below {
    /* 侧栏 220px：足够放下分类名，又不挤压正文 */
    grid-template-columns: 220px minmax(0, 1fr);
    gap: var(--nv-space-6);
    align-items: start;
  }

  /* 侧栏跟随滚动（与首页分类树同款处理） */
}

/* ★ 2026-10-14：左侧分类栏已删除，详情卡恢复为独立单栏，
   所以它需要自己带一个顶部外边距（原先是外层 .nv-below 提供的）。
   ★ 特异度说明：原规则是 `.nv-below .nv-detail__desc`（0,2,0），
     这里用 `.nv-detail__desc.nv-card`（0,2,0）同分，靠【后定义】胜出，
     所以必须写在原规则之后。

   ⚠ 保留上面那条 .nv-below 规则：万一以后恢复侧栏，
     它能把 margin 归零，避免叠加成双倍间距。 */
.nv-detail__desc.nv-card { margin-top: var(--nv-space-8); }

/* 侧栏骨架占位：避免 JS 渲染分类前高度从 0 跳到满，造成页面抖动 */
@keyframes nv-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
/* 尊重用户的"减少动效"偏好 */
@media (prefers-reduced-motion: reduce) {
}

/* 侧栏里的分类项（与首页共用 .side-link 外观，这里只补侧栏特有的收窄） */
.nv-detail-side .side-card { padding: var(--nv-space-3); }
.nv-detail-side .side-link { min-height: 46px; }
.nv-detail-side .side-desc { display: none; }   /* 侧栏窄，去掉副标题省空间 */

/* 6.1 图集
   ★ 两栏等高后，图集本身只需要"内容高度 + 吸顶"，
     ★★ 2026-10-14 第四轮（用户："删掉缩略图条 + 左侧图片高度与右侧一致"）

     前三轮一直在用 aspect-ratio 4:3 给主图一个"固定比例画框"，
     结果主图高度（约 345px）永远比右侧信息区（约 500-990px）矮，
     页面看着左右不齐。

     → 本轮换成【跟随右栏等高】：
       · .nv-gallery 去掉 align-self:start，让它参与栅格的 stretch（默认行为）
       · .nv-gallery__main 去掉 aspect-ratio，改用 flex:1 吃满剩余高度
       · 图片用 object-fit:cover 铺满容器（会裁切，但左右终于齐平了）

     ★ 为什么这次接受裁切：用户明确要"高度一致"，
       而"不裁切"和"等高"在固定宽度下是矛盾的 ——
       一张 16:9 的图放进竖长容器，要么裁、要么留白。
       既然缩略图条也删了（那块本来就是多余的），
       主图改成 cover 铺满反而是最干净的结果。
     -------------------------------------------------------------------- */
/* ══ 6.1 主图集（2026-10-15 重做：并入「主图卡」）════════════════════
   ★★ 旧实现的两个问题，这里一并解决：
     1. `position: sticky` —— 它会【打断 flex 高度链】，导致图无法被
        卡片高度约束，图会溢出到下方承诺条之上（实测图链 628px、
        卡片只有 704px 且承诺条被压在图上）。
        现在图是卡片的一段，不需要 sticky，已移除。
     2. `border` + `border-radius` + `box-shadow` —— 旧版自己是一张独立卡片。
        现在外层 .item-card 已经承担卡片外观，内层再带边框会叠出双层框，
        且内圆角会压在紧邻的承诺条上。全部去掉，交外层统一裁切。
   ★ 图与承诺条同属一张卡（.item-card--media），
     所以这里只负责"吃满剩余空间"。
   -------------------------------------------------------------------- */
.nv-gallery {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* ★ min-height:0 是整条 flex 链的关键：默认 auto 会让内容（大图）
     顶大卡片，收缩失效。逐层写 0 才能真正被卡片高度约束。 */
}

/* ── 主图容器 ─────────────────────────────────────────────
   ★ flex:1 吃满 .nv-gallery 的剩余高度；
   ★ min-height 兜底：右栏内容少时不至于被压成一条扁片。
   -------------------------------------------------------------------- */
.nv-gallery__main {
  position: relative;
  flex: 1 1 auto;
  min-height: 300px;
  display: flex;
  align-items: stretch;
  justify-content: center;
  padding: 0;
  background-color: var(--nv-sunken);
  overflow: hidden;
  /* 卡片外观由外层 .item-card 负责，这里不再自带边框/圆角/阴影 */
}
[data-theme="dark"] .nv-gallery__main { background-color: var(--nv-sunken); }

.nv-gallery__main img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  border-radius: 0;
  /* ★ 2026-10-16 去掉 transition: transform ——
     它此前只服务于「主图 hover 放大」，那条规则已按用户要求删除
     （见下方 §「主图 hover 缩放」处新增的说明）。
     没有属性再变化，留着就是一行永不触发的死代码。 */
}

/* ══ 6.1b 图集缩略图条（当前【未启用】）════════════════════════════
   ★★ 2026-10-14 用户要求删除缩略图条：
     本主题的图集数据源只有 $item.cover 一张图，
     缩略图条里永远只有一个按钮 —— 纯冗余，已从模板中移除。

   ★ 这里【保留】这套样式，理由：
     1. 死代码不占运行成本（无元素命中，浏览器不做匹配）
     2. 将来若支持多图（$item.images 之类），模板加回
        .nv-gallery__thumbs 即可直接生效，不用重写样式
   -------------------------------------------------------------------- */
.nv-gallery__thumbs {
  display: flex;
  gap: var(--nv-space-2);
  margin-top: var(--nv-space-3);
  overflow-x: auto;
  scrollbar-width: none;
}
.nv-gallery__thumbs::-webkit-scrollbar { display: none; }

.nv-gallery__thumb {
  flex: 0 0 64px;
  width: 64px; height: 64px;
  padding: var(--nv-space-1);
  background-color: var(--nv-elevated);
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  cursor: pointer;
  overflow: hidden;
  transition: border-color var(--nv-dur-fast) var(--nv-ease-out), box-shadow var(--nv-dur-fast) var(--nv-ease-out);
}
.nv-gallery__thumb img { width: 100%; height: 100%; object-fit: contain; }
.nv-gallery__thumb.is-active,
.nv-gallery__thumb:hover {
  border-color: var(--nv-brand);
  box-shadow: var(--nv-ring);
}

/* 6.2 信息区 */

/* --------------------------------------------------------------------------
   商品详情 · 「推荐」金徽章
   --------------------------------------------------------------------------
   ★ 与【商品列表卡片】上的推荐徽章同源（同一套 --nv-gold* 令牌 + 同一个 ♛ 字形），
     保证"列表里认过的标记，进详情页能立刻对上"。

   ★ 为什么这里写独立类名而不是复用 bridge.css 的
     .badge-soft-primary[data-nv-rec]：
       · 那条规则带 .acg-card 前缀，只作用于核心列表卡片，够不到详情页；
       · 详情页的徽章是本主题自己的 .nv-badge 体系，本来就该有自己的样式。
   -------------------------------------------------------------------------- */
.nv-rec-badge {
  background-color: var(--nv-gold-soft);
  color: var(--nv-gold-ink);
  border: 1px solid var(--nv-gold-border);
  font-weight: var(--nv-weight-bold);
  /* ★ 加 1px 边框会比邻座徽章胖 2px、高 2px，padding 各减 1 拉平视觉高度。
     （.nv-badge 基础 padding 是 2px var(--nv-space-2)） */
  padding: 1px calc(var(--nv-space-2) - 1px);
}
/* 皇冠比文字再饱和一点，金得更有"金属感" */
.nv-rec-badge .nv-crown {
  font-size: 1.05em;
  line-height: 1;
  color: var(--nv-gold);
}

/* 价格面板 */

@media (min-width: 768px) {
}

/* 规格选择 */
.nv-races {
  display: flex;
  flex-wrap: wrap;
  gap: var(--nv-space-2);
  margin-bottom: var(--nv-space-4);
}

.nv-race {
  /* ★★ 2026-10-14 修：文案没垂直居中。
     原来是 <a>（inline）+ min-height:38px —— line-height 默认继承 body 的 1.5，
     文字只占一行高度、贴着容器顶部，下方空出一截（截图可见）。
     → 改成 inline-flex + 居中对齐，文字永远在正中。
       min-height 是"最小"高度，实际高度由 flex 内容 + padding 决定，
       所以这两条必须一起加，缺一个都会歪。 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 38px;
  padding: var(--nv-space-1) var(--nv-space-4);
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-elevated);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  line-height: 1.2;                 /* 收紧行高，避免撑高后文字仍偏上 */
  color: var(--nv-text-secondary);
  cursor: pointer;
  transition: all var(--nv-dur-fast) var(--nv-ease-out);
}
.nv-race:hover { border-color: var(--nv-border-brand); background-color: var(--nv-brand-softer); color: var(--nv-brand-ink); }
.nv-race.is-active {
  border-color: var(--nv-brand);
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
  box-shadow: var(--nv-ring);
}

/* 自定义下单控件 */
.nv-widget { margin-bottom: var(--nv-space-4); }
.nv-widget .form-label { display: block; margin-bottom: var(--nv-space-2); font-size: var(--nv-fs-caption); font-weight: var(--nv-weight-medium); color: var(--nv-text-secondary); }

/* 购买栏 */

/* 数量步进器 */
.nv-qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  overflow: hidden;
  background-color: var(--nv-elevated);
}

.nv-qty__btn {
  width: 36px; height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.125rem;
  color: var(--nv-text-secondary);
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.nv-qty__btn:hover:not(:disabled) { background-color: var(--nv-soft); color: var(--nv-text-primary); }
.nv-qty__btn:disabled { opacity: .35; cursor: not-allowed; }

.nv-qty__input {
  width: 52px; height: 36px;
  border: 0;
  border-inline: 1px solid var(--nv-border);
  background-color: transparent;
  text-align: center;
  font-size: var(--nv-fs-body);
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  color: var(--nv-text-primary);
  -moz-appearance: textfield;
}
.nv-qty__input::-webkit-outer-spin-button,
.nv-qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.nv-qty__input:focus { outline: none; background-color: var(--nv-brand-softer); }

/* 6.3 图文详情（宝贝详情）
   --------------------------------------------------------------------------
   ★★ 2026-10-14 改为卡片容器（用户："详情页需要容器效果"）。
   原来只有一条上边框，正文直接贴页面背景，和上面「右栏信息卡」的
   视觉语言完全不一致，像是两块不相干的内容。

   现在：卡片外壳（同 .nv-card 的圆角+描边+阴影）→ 带图标的标题条 → 内边距正文。
   与商品区、查询结果卡使用同一套 .nv-card 语言。
   -------------------------------------------------------------------------- */
.nv-detail__desc-head .material-icons-outlined {
  font-size: var(--nv-icon-md);
  color: var(--nv-brand);
}

/* --------------------------------------------------------------------------
   7. 登录 / 注册 · Auth
   Apple 风：卡片居中
   --------------------------------------------------------------------------
   ★★ 留白修正（2026-10-14 v3，终于定位到两个真凶）

   真凶 A：`.nv-auth` 有 `min-height: calc(100vh - 60px)`。
            卡片只有 ~616px，容器却被撑到视口高 —— 差值全部变成卡片【下方】空白。
            实测：视口 700px→40px 空白，900px→116px，1080px→206px，1400px→366px。
            也就是说【屏幕越大，下面越空】。

   真凶 B：认证页也渲染了 220px 高的常规页脚。
            页脚本来是"内容结束"的标志，但认证页只有一张卡，
            页脚挂在下面显得下半页被塞了一块无关内容。
            → 认证页不需要页脚（用户此刻的意图就是登录，不是逛站）。
              用 CSS 隐藏，比在模板里去掉 include 更安全
              （其他页面共用 Footer.html，模板级改动风险更大）。

   最终策略：
     · 去掉 min-height 对"填满视口"的依赖；
     · 用 flex 的 `margin-block:auto` 让剩余空间【上下平分】，
       卡片自然居中 —— 视口高时居中，视口矮时贴顶并允许滚动；
     · 隐藏页脚，把纵向空间全部留给卡片。
   -------------------------------------------------------------------------- */
.nv-auth {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  /* 上下给一档稳定的呼吸空间；卡片之外不再有额外撑高 */
  padding: var(--nv-space-8) var(--nv-space-4) var(--nv-space-10);
}

/* 视口够高时：让 .nv-auth 填满"除页头与页脚之外"的剩余空间，卡片在其中居中。
   ★★ 关键 1：`margin-block: auto` 只在【main 轴是纵向】时才能吸收纵向空间。
     原来 flex-direction 默认是 row（横向），`margin-block:auto` 在交叉轴上
     会被 `align-items` 接管 —— 而 align-items:flex-start 会把它压到顶部，
     于是所有空白都留在下面（实测就是这个问题）。
     解法：显式声明 flex-direction: column，让 margin-block:auto 真正生效。

   ★★ 关键 2：min-height 必须【同时减掉页头 60px 和页脚高度】。
     页脚恢复显示后，如果只减页头，卡片会被页脚顶出视口、产生滚动条
     —— 又回到"下半页空 + 还得滚"的老问题。
     页脚高度随内容变化（约 220px），用 JS 量不划算，这里取一个稳妥常量
     并留 fallback：--nv-footer-h 若未定义则退回 220px。 */
@media (min-height: 560px) {
  .nv-auth {
    flex-direction: column;
    min-height: calc(100vh - var(--nv-header-h) - var(--nv-footer-h, 220px));
    padding-top: var(--nv-space-6);
    padding-bottom: var(--nv-space-6);
  }
  .nv-auth__card {
    margin-block: auto;
    /* column 下 align-items 管横向，要让它水平居中 */
    margin-inline: auto;
  }
}

/* ★ 认证页的页脚：保留。
   ----------------------------------------------------------------------
   上一轮为治"下半页留白过多"，我一度用 `body:has(.nv-auth) .nv-footer{display:none}`
   把这个页脚隐藏了。但隐藏是"治标" —— 真正的问题是 min-height 撑高 + 卡片贴顶。
   现在卡片已经上下均衡居中（见上），页脚不再显得是"多出来的空白"，
   反而给了页面一个正常的收尾。所以恢复显示。
   （用户 2026-10-14 反馈："登录注册页面为什么 footer 没有了"）
   ---------------------------------------------------------------------- */
/* 认证页卡片与页脚之间留一档间距，避免贴太近 */
body:has(.nv-auth) .nv-footer { margin-top: var(--nv-space-6); }

.nv-auth__card {
  width: 100%;
  max-width: 420px;
  padding: var(--nv-space-8);
  background-color: var(--nv-elevated);
  border: 1px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-card);
  box-shadow: var(--nv-shadow-2);
}

@media (min-width: 480px) {
  .nv-auth__card { padding: var(--nv-space-10); }
}

/* 卡片头部：logo + 标题 + 副标题。
   ★ 上下留白同步收紧（原来 margin-bottom 24px，现在 20px），
     并把 logo 与其下方文字的间距也收一档，避免头部占掉半张卡。 */
.nv-auth__head {
  text-align: center;
  margin-bottom: var(--nv-space-5);
}

/* ── 认证卡片的 logo ──────────────────────────────────────
   ★ 与页头 .nv-brand__mark 同构：优先显示真实 favicon（<img>），
     加载失败时由 .is-fallback 回落到品牌渐变方块。
     ★ 用 <span> 而非 <div> 是为了能放进 <a>/行内上下文，
       所以这里必须显式 display:flex（span 默认 inline，
       宽高与居中都会失效）。 */
.nv-auth__mark {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px; height: 52px;
  margin: 0 auto var(--nv-space-3);
  border-radius: var(--nv-radius-btn);
  background-image: var(--nv-gradient);
  box-shadow: var(--nv-shadow-brand);
  overflow: hidden;
}

.nv-auth__mark > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: inherit;
}

/* 有图时不要再透出渐变底（透明 PNG 也能干净显示） */
.nv-auth__mark:has(> img) {
  background-image: none;
  box-shadow: none;
}

.nv-auth__title {
  font-size: var(--nv-fs-title);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-ls-title);
  margin-bottom: var(--nv-space-1);
}

.nv-auth__sub { font-size: var(--nv-fs-caption); color: var(--nv-text-muted); }

/* ── 插件登录入口的「或」分隔线 ─────────────────────────────
   ★ 只在确实有插件订阅 USER_VIEW_AUTH_LOGIN_BUTTON 时才渲染
     （模板里用 getHookNum() 判断），否则会出现一条光秃秃的「或」。
   ★ 用两条 flex:1 的细线夹住文字，比单独写个「或」更能表达
     「以下属于另一类登录方式」，也更符合 Apple HIG 的分组分隔手法。 */
.nv-auth__divider {
  display: flex;
  align-items: center;
  gap: var(--nv-space-3);
  margin-top: var(--nv-space-5);
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-faint);
}
.nv-auth__divider::before,
.nv-auth__divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background-color: var(--nv-border);
}

.nv-auth__foot {
  margin-top: var(--nv-space-5);
  padding-top: var(--nv-space-4);
  border-top: 1px solid var(--nv-border-soft);
  text-align: center;
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}

/* ── 验证码行：输入框 + 图形/发送按钮 ─────────────────────
   ★ 布局用 flex，让输入框吃掉剩余宽度、右侧控件保持固定宽。
     图形验证码图与「发送验证码」按钮共用这一行，故两者都约束高度。 */
.nv-captcha {
  display: flex;
  align-items: stretch;
  gap: var(--nv-space-2);
}
.nv-captcha > .nv-input {
  flex: 1;
  min-width: 0;
}
.nv-captcha > .nv-btn {
  flex: 0 0 auto;
  /* 与输入框等高：base.css 的 .nv-btn--sm 是 34px，input 是 --nv-tap-min，
     这里用 align-self:stretch 让按钮跟随输入框高度，视觉上齐平。 */
  align-self: stretch;
  min-height: auto;
}

/* ── 密码框 · 显示/隐藏按钮（2026-10-14 新增） ──────────────
   结构：
     <div class="nv-pwd">
       <input class="nv-input" type="password">
       <button class="nv-pwd__btn" data-nv-pwd-toggle>👁</button>
     </div>

   ★ 输入框右侧要给按钮留出空间，否则长密码会跑到按钮底下。
     用 padding-right 预留（44px = 按钮 36px + 右侧 8px 间距）。
   ★ 按钮绝对定位在容器内右侧，垂直居中 —— 不参与 flex 布局，
     这样输入框可以占满整宽，视觉上更整洁。
   ★ 按钮只用图标，所以不需要额外的"文字避让"逻辑。
   ---------------------------------------------------------- */
.nv-pwd { position: relative; display: block; }

.nv-pwd > .nv-input {
  /* 给右侧眼睛按钮留位 */
  padding-right: 44px;
}

.nv-pwd__btn {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--nv-radius-chip);
  background: transparent;
  color: var(--nv-text-faint);
  cursor: pointer;
  transition:
    color var(--nv-dur-fast) var(--nv-ease-out),
    background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.nv-pwd__btn .material-icons-outlined { font-size: var(--nv-icon-md); }

.nv-pwd__btn:hover {
  color: var(--nv-text-secondary);
  background-color: var(--nv-soft);
}
.nv-pwd__btn:focus-visible {
  outline: 2px solid var(--nv-brand);
  outline-offset: 1px;
}
/* 已显示明文时按钮用品牌色，状态一眼可辨 */
.nv-pwd__btn[aria-pressed="true"] { color: var(--nv-brand-ink); }

/* 图形验证码图片 */
.nv-captcha > .image-code,
.image-code {
  flex: 0 0 auto;
  height: var(--nv-tap-min);
  width: auto;
  min-width: 96px;
  border-radius: var(--nv-radius-btn);
  border: 1px solid var(--nv-border);
  background-color: var(--nv-soft);
  object-fit: cover;
  cursor: pointer;
  transition: border-color var(--nv-dur-fast) var(--nv-ease-out);
}
.image-code:hover { border-color: var(--nv-border-brand); }

/* 人机验证弹窗里的验证码图（核心 message.prompt 注入，选择器固定） */
.prompt-image-code {
  display: block;
  height: 44px;
  margin: 0 auto var(--nv-space-3);
  border-radius: var(--nv-radius-btn);
  border: 1px solid var(--nv-border);
  cursor: pointer;
}

.nv-page-head__title {
  font-size: var(--nv-fs-title);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-ls-title);
  line-height: 1.2;
  color: var(--nv-text-primary);
}

.nv-page-head__sub {
  margin-top: var(--nv-space-1);
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}

.nv-section-title {
  font-size: var(--nv-fs-heading);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-ls-heading);
  color: var(--nv-text-primary);
}

/* ── 通用面板（会员页/其他页面也在用，保留） ───────────── */

/* ── 查询卡片 ───────────────────────────────────────────── */
/* ★★ 宽度对齐（2026-10-14 v2）：
     上一版把查询卡限制在 780px，而页面容器 .nv-container 是 1400px ——
     结果查询卡在宽屏上显得又窄又空，和页面标题、结果区不在同一条视觉基线上
     （用户截图反馈"太窄，建议与页面宽度一致"）。

     现在改为【跟随页面容器宽度】：查询卡、结果区、空态全部占满容器，
     与「订单查询」标题左右对齐，视线有明确的左右边界。
     --q-max 保留为可调旋钮：想收窄就改这一个值。
   -------------------------------------------------------------------------- */
#nv-main {
  --q-max: 100%;
}
.q-card__ico .fa-duotone,
.q-card__ico .material-icons-outlined { font-size: 16px; }

.q-search__ico .material-icons-outlined { font-size: 20px; }

/* 输入框：清掉自身边框与背景，完全融入外层容器 */
.q-search__input,
.q-search__input.nv-input {
  flex: 1;
  min-width: 0;
  height: 100%;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  font-size: var(--nv-fs-body);
  color: var(--nv-text-primary);
}
.q-search__input:focus,
.q-search__input.nv-input:focus {
  outline: none;
  border: 0;
  background: transparent;
  box-shadow: none;
}

.q-search__btn .fa-duotone,
.q-search__btn .material-icons-outlined { font-size: 14px; }


/* 窄屏：卡片与搜索条适配 */
@media (max-width: 640px) {   /* 只留放大镜图标，避免文字挤 */
}

/* --------------------------------------------------------------------------
   8.0 搜索区 · 大号搜索胶囊（2026-10-16，方案 A 落地）
   --------------------------------------------------------------------------
   ★★ 与旧版的区别：旧版是「页面标题 + 一张 q-card 查询卡片」，
      新版把【搜索框本身】做成页面主角 —— 这是方案 A 的性格所在。

   为什么搜索框值得当主角：这一页用户只有一个任务（查订单），
   把它做成 60px 的圆角胶囊、聚焦时整条亮起，比塞进一张卡片里更聚焦。

   ★ 保留 .order-query-form / .btn-search-query 契约不变。
   ★ 快捷入口的 inputmode 由 theme.js 按 data-nv-qmode 切换
     （手机→tel 键盘、邮箱→email 键盘），与商品页联系方式的做法同源。
   -------------------------------------------------------------------------- */

.q-hero {
  text-align: center;
  padding: var(--nv-space-10) 0 0;
}

/* 顶部的渐变图标块 —— 给这一页一个视觉焦点 */
.q-hero__ic {
  width: 60px;
  height: 60px;
  border-radius: 20px;
  margin: 0 auto var(--nv-space-5);
  display: grid;
  place-items: center;
  background-image: var(--nv-gradient);
  box-shadow: var(--nv-shadow-brand);
}
.q-hero__ic .material-icons-outlined { font-size: 30px; color: #fff; }

.q-hero__t {
  margin: 0 0 var(--nv-space-2);
  font-size: clamp(1.5rem, 3.6vw, 2rem);
  font-weight: var(--nv-weight-bold);
  letter-spacing: -.03em;
  line-height: 1.22;
  color: var(--nv-text-primary);
}
.q-hero__d {
  margin: 0 auto;
  /* ★ 2026-10-16 修正：原 max-width 44ch 在中文下必然换行。
     ch 单位是字符「0」的宽度（15px 字号下约 8.5px），44ch ≈ 374px，
     而中文单字宽 ≈ 字号 = 15px → 一行只放得下约 24 字。
     本页副标题「输入订单号或下单时留下的联系方式，即可查看订单状态与卡密」
     共 28 字，需要约 420px，于是被挤成两行。

     改为 640px：容纳整句（420px）仍有 220px 余量，
     并留出足够空间应对其他语言（英文标签通常更长）。
     ★ 与下方搜索框的 max-width: 620px 视觉上对齐，不会显得突兀。
     ★ 窄屏由 max-width 自动收敛，不会撑出横向滚动条。 */
  max-width: 640px;
  color: var(--nv-text-muted);
  font-size: .9375rem;
  line-height: 1.65;
}

/* ── 大号搜索胶囊（本页的主角）──────────────────────────── */
.q-hero__form { max-width: 620px; margin: var(--nv-space-6) auto 0; }

.q-bigsearch {
  display: flex;
  align-items: center;
  gap: var(--nv-space-2);
  height: 60px;
  padding: 0 8px 0 20px;
  background-color: var(--nv-elevated);
  border: 1.5px solid var(--nv-border);
  border-radius: var(--nv-radius-pill);
  box-shadow: var(--nv-shadow-1);
  transition: border-color .25s var(--nv-ease-out, ease),
              box-shadow .25s var(--nv-ease-out, ease);
}
/* ★ 聚焦时整条容器亮起（而不是只给 input 加边框）——
     这是"搜索框是主角"的关键细节 */
.q-bigsearch:focus-within {
  border-color: var(--nv-brand);
  box-shadow: 0 0 0 4px var(--nv-brand-soft), var(--nv-shadow-2);
}

.q-bigsearch__ico {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  color: var(--nv-text-muted);
  transition: color .2s;
}
.q-bigsearch__ico .material-icons-outlined { font-size: 22px; }
.q-bigsearch:focus-within .q-bigsearch__ico { color: var(--nv-brand); }

.q-bigsearch__input {
  flex: 1;
  min-width: 0;
  height: 100%;
  border: none;
  background: none;
  outline: none;
  font-family: inherit;
  font-size: 1rem;
  color: var(--nv-text-primary);
}
.q-bigsearch__input::placeholder { color: var(--nv-text-faint); }

.q-bigsearch__btn {
  flex: 0 0 auto;
  height: 44px;
  padding: 0 22px;
  border: none;
  cursor: pointer;
  /* ★ 2026-10-16 统一：由 --nv-radius-pill（胶囊）改为 --nv-radius-btn。
     ★ 例外说明：这个按钮是「嵌在胶囊形搜索框内的内嵌按钮」，
       若跟着外面的 .q-bigsearch（border-radius: pill）走会显得方，
       所以此处用 btn 圆角但略小一档更协调 ——
       取 --nv-radius-md(.75rem=12px) 与容器内壁留出均匀呼吸感。
     实测：整条搜索框 60px 高、内边距 8px，按钮 44px 高，
           圆角 12px 时内外弧线协调，不再是「胶囊套胶囊」。 */
  border-radius: var(--nv-radius-btn);
  background-image: var(--nv-gradient);
  color: var(--nv-text-on-solid);
  font-family: inherit;
  font-size: .9375rem;
  font-weight: var(--nv-weight-bold);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  box-shadow: var(--nv-shadow-brand);
  transition: transform .2s var(--nv-ease-out, ease);
}
.q-bigsearch__btn:hover { transform: translateY(-1px); }
.q-bigsearch__btn .material-icons-outlined { font-size: 19px; }

/* ── 三条实在路径 ─────────────────────────────────────────── */
.q-helps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--nv-space-3);
  margin-top: var(--nv-space-8);
}
.q-help {
  padding: var(--nv-space-4);
  border-radius: var(--nv-radius-card);
  background-color: var(--nv-elevated);
  border: .5px solid rgba(var(--nv-border-soft-rgb), calc(var(--nv-border-soft-a) * 0.45));
  border: .5px solid color-mix(in srgb, var(--nv-border-soft) 45%, transparent);
}
.q-help > .material-icons-outlined {
  font-size: 22px;
  color: var(--nv-brand);
  display: block;
  margin-bottom: var(--nv-space-2);
}
.q-help h3 {
  margin: 0 0 5px;
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-bold);
  letter-spacing: -.01em;
  color: var(--nv-text-primary);
}
.q-help p {
  margin: 0;
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-muted);
  line-height: 1.6;
}

/* ── 窄屏 ─────────────────────────────────────────────────── */
@media (max-width: 760px) {
  .q-hero { padding-top: var(--nv-space-6); }
  .q-hero__ic { width: 52px; height: 52px; border-radius: 17px; }
  .q-hero__ic .material-icons-outlined { font-size: 26px; }
  .q-bigsearch { height: 56px; padding-left: 16px; }
  .q-bigsearch__btn { padding: 0 16px; }
  .q-bigsearch__btn span:last-child { display: none; }   /* 只留放大镜，避免挤 */
  .q-helps { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   8.1 查询结果 · 增强（2026-10-16，方案 A 落地）
   --------------------------------------------------------------------------
   从 demo 落地四项改进。★ 全部只新增样式，不改任何核心契约类。

   ① 统计条 .q-stats —— 「先看全局再看明细」
      查多笔订单时，用户最先想知道的是"我有几笔没付"，而不是逐张卡去数。
   ② 结果头 .q-results__hd —— 标题与计数同一基线，计数用等宽数字
   ③ 订单号旁 / 卡密区的复制按钮 —— 等宽+可选中只是基础，复制才是刚需
   ④ 待付款订单的左侧暖色条 + 「立即支付」CTA

   ★ 数字全部由 theme.js 的 initQueryStats() 从真实卡片算出，不写死。
   ★ 统计条用 hidden 属性控制"有数据才显示"，而整体显隐交给核心控制的父容器
     .order-results —— 所以它必须放在那个容器里面（见 Index/Query.html）。
   -------------------------------------------------------------------------- */

/* ① 结果头：标题 + 计数 */
/* ★★ 结果区与上方的间距（2026-10-16 修）：
   原先 .order-results 完全没有上边距，「查询结果」标题紧贴上方的内容
   （用户反馈"查询结果和上面靠得太近"）。
   用 padding-top 而不是 margin-top：它初始 display:none、
   由核心 show() 才出现，margin 在隐藏态参与折叠会让间距不可预测。 */
.order-results { padding-top: var(--nv-space-10); }

.q-results__hd {
  display: flex;
  align-items: baseline;
  gap: var(--nv-space-3);
  margin-bottom: var(--nv-space-4);
}
.q-results__count {
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}

/* ② 统计条 */
.q-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: var(--nv-space-3);
  margin-bottom: var(--nv-space-4);
}
.q-stat {
  padding: var(--nv-space-4);
  border-radius: var(--nv-radius-card);
  background-color: var(--nv-elevated);
  border: .5px solid rgba(var(--nv-border-soft-rgb), calc(var(--nv-border-soft-a) * 0.45));
  border: .5px solid color-mix(in srgb, var(--nv-border-soft) 45%, transparent);
}
.q-stat__l {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-muted);
  margin-bottom: 5px;
}
.q-stat__l .material-icons-outlined { font-size: 14px; }
.q-stat__v {
  display: block;
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.1875rem;
  font-weight: var(--nv-weight-bold);
  letter-spacing: -.02em;
  color: var(--nv-text-primary);
}
.q-stat__v.is-ok { color: var(--nv-success-ink); }
.q-stat__v.is-warn { color: var(--nv-warning-ink); }
.q-stat__v.is-price { color: var(--nv-price-promo); }

/* ③ 订单号复制按钮
      ★ 平时半透明：一次查 10 笔就有 10 个按钮，
        不能每个都做成抢眼的实心按钮，否则整页全是噪点。 */
.order-copy {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  cursor: pointer;
  border-radius: 6px;
  background: none;
  color: var(--nv-text-faint);
  opacity: .6;
  display: grid;
  place-items: center;
  transition: opacity .18s, background-color .18s, color .18s;
}
.order-copy:hover { opacity: 1; background-color: var(--nv-soft); color: var(--nv-brand-ink); }
.order-copy.is-done { opacity: 1; color: var(--nv-success-ink); }
.order-copy .material-icons-outlined { font-size: 14px; }

/* 卡密区的复制按钮（主操作位，做成有描边的胶囊） */
.card-copy {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 32px;
  padding: 0 13px;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-pill);
  background-color: var(--nv-elevated);
  color: var(--nv-text-secondary);
  font-family: inherit;
  font-size: var(--nv-fs-micro);
  font-weight: var(--nv-weight-bold);
  cursor: pointer;
  transition: border-color .2s, background-color .2s, color .2s;
}
.card-copy:hover {
  border-color: var(--nv-brand);
  color: var(--nv-brand-ink);
  background-color: var(--nv-brand-softer);
}
.card-copy.is-done {
  border-color: transparent;
  background-color: var(--nv-success-soft);
  color: var(--nv-success-ink);
}
.card-copy .material-icons-outlined { font-size: 16px; }

/* ④ 待付款订单：左侧暖色条，扫列表时一眼可辨 */
.order-item.is-pending { border-left: 3px solid var(--nv-warning-soft); }

.pending-note {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 0;
  font-size: var(--nv-fs-body);
  color: var(--nv-text-muted);
}
.pending-note .material-icons-outlined {
  font-size: 16px;
  color: var(--nv-text-faint);
  flex: 0 0 auto;
  margin-top: 2px;
}

/* 订单动作按钮组（待付款的「立即支付 / 取消订单」） */
.order-actions {
  display: flex;
  gap: var(--nv-space-2);
  margin-top: var(--nv-space-3);
  flex-wrap: wrap;
}
.order-act {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 38px;
  padding: 0 16px;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-elevated);
  color: var(--nv-text-secondary);
  font-family: inherit;
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-bold);
  cursor: pointer;
  transition: border-color .2s, background-color .2s, color .2s, transform .2s;
}
.order-act:hover {
  border-color: var(--nv-brand);
  color: var(--nv-brand-ink);
  background-color: var(--nv-brand-softer);
}
.order-act--primary {
  border-color: transparent;
  background-image: var(--nv-gradient);
  color: #fff;
  box-shadow: var(--nv-shadow-brand);
}
.order-act--primary:hover { color: #fff; transform: translateY(-1px); }
.order-act .material-icons-outlined { font-size: 17px; }

/* 窄屏：统计条两列，动作按钮撑满 */
@media (max-width: 760px) {
  .q-stats { grid-template-columns: repeat(2, 1fr); }
  .order-actions .order-act { flex: 1; justify-content: center; }
  .card-copy span:last-child { display: none; }
}

/* 复制成功的轻提示 —— 由 theme.js 的 _showCopyToast() 动态创建。
   ★ 不引 toastr / 不引任何库：一个类切换 + CSS 过渡就够了。
   ★ 位置固定在底部居中，不遮挡卡密本身（用户复制时视线还在卡密上）。 */
.nv-copy-toast {
  position: fixed;
  left: 50%;
  bottom: 32px;
  transform: translateX(-50%) translateY(12px);
  padding: 10px 18px;
  border-radius: var(--nv-radius-pill);
  background-color: var(--nv-text-primary);
  color: var(--nv-page);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-bold);
  box-shadow: var(--nv-shadow-2);
  opacity: 0;
  pointer-events: none;
  transition: opacity .28s var(--nv-ease-out, ease), transform .28s var(--nv-ease-out, ease);
  z-index: 9999;
}
.nv-copy-toast.is-show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* --------------------------------------------------------------------------
   9. 会员中心 · Dashboard
   -------------------------------------------------------------------------- */

@media (max-width: 899px) {
  /* 窄屏堆叠时给一点上下间距，避免两张卡贴一起 */
}

/* ══════════════════════════════════════════════════════════════════════
   10.1 分类徽章（2026-10-14：从右栏底部上移到标题下方的徽章行）
   ----------------------------------------------------------------------
   分类是「这是什么」的识别信息，用户在决定买不买时最先要确认的就是它。
   用品牌浅蓝底与中性的「已售 / 库存」区分开，一眼能认出这是分类标签。
   ══════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════
   10.2 付款方式区块（方案 A 排布）
   ----------------------------------------------------------------------
   ★★ 为什么用 2×2 网格而不是单列：
     实测单列竖排 4 个渠道要多占约 110px，把右栏顶高，
     破坏与左栏的等高平衡（原落差 290px 里有一部分就是它贡献的）。
     2×2 只多占约 55px，视觉上也更像"选一个渠道"的选项组。
   ★ .pay-list 是 item.js 的硬契约（JS 往里 append 支付方式），
     这里只加网格容器类 .pay-list--grid，不改类名。
   ══════════════════════════════════════════════════════════════════════ */

/* ★ .pay-list--grid 只是语义标记；真正的网格定义在下方
   「支付方式列表」那条 .pay-list 规则里（见该处注释：
   同等特异度下后定义者胜出，所以必须写在那里）。 */

.nv-block { margin-bottom: var(--nv-space-4); }

/* ══════════════════════════════════════════════════════════════════════
   10.3 详情页右栏 · 紧凑两列字段（方案 A 核心）
   ----------------------------------------------------------------------
   ★★ 这是把右栏高度压下来的关键一步。
     原来「联系方式 / 查询密码 / 购买数量 / 优惠券」是 4 个 .nv-block 竖排，
     每个约 70px（label 20 + input 42 + 间距），共约 280px。
     改成两列后同样的内容只占约 160px，立省 ~120px。

   ★ 实现要点：
     .vstack 是 <form> 本身，直接把它变成两列 grid 会破坏
     .cash-pay / .nv-widget 这些需要整行（span 2）的块。
     所以这里：form 用 grid 两列，凡是需要整行的子块单独 span 2。
     JS 用 serializeArray() 收字段，与视觉排布无关，不受影响。

   ★ 窄屏（<640px）必须回到单列 —— 两列在手机上是灾难。
   ══════════════════════════════════════════════════════════════════════ */
@media (min-width: 1100px) {
  /* 宽屏（此时右栏足够宽）才启用多列。
     ★★ 2026-10-15 用户要求：邮箱地址 / 购买数量 / 人机验证 三个字段并排一行。
        原来是两列，装不下三个（会有一个掉到下一行），故改为【三列】。

     ★ 列宽不是均分的 1:1:1，而是 1fr : .8fr : 1.4fr：
       人机验证里装的是【输入框 + 84px 验证图】两个元素，
       均分时输入框只剩约 97px（placeholder「图形验证码」实测要 70px，
       几乎顶格，输入后更看不清）。
       → 给第三列更多权重，把数量那列（步进器只需约 150px）省下的宽度让给它。 */
  .nv-detail .vstack {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, .8fr) minmax(0, 1.4fr);
    gap: var(--nv-space-3) var(--nv-space-4);
    align-items: start;
  }
  .nv-detail .vstack > .nv-block { margin-bottom: 0; }

  /* ★★ 哪些块必须占满整行？
     实测教训：最初用 `.nv-block[style*="display:block"]` 想匹配"条件显示"的块，
     但那把所有带内联 style 的块都命中了 —— 结果 6 个子块里 5 个被拉成整行，
     多列布局形同虚设（实测 cols 正确但每行只放得下 1 个）。

     → 改成【白名单】策略：默认占一列，只有明确需要整行的才 span 全部。
       这样新增字段时默认进多列，不会又踩同样的坑。 */

  /* 填充信息（自定义下单字段，数量不定，必须整行） */
  .nv-detail .vstack > .nv-widget,
  /* 付款方式区（独立卡片） */
  .nv-detail .vstack > .cash-pay,
  /* 自选账号（含说明文字，整行更好读） */
  .nv-detail .vstack > .nv-block:has(input[name="card_id"]),
  /* ★★ 宝贝类型 / SKU：
     它们的选项是一排横向胶囊，会自然换行。
     如果只占一列，多个档位就会挤在半边堆成好几行，右侧空着 ——
     实测 6 个档位在一列里排成 3 行，非常难看。
     规格属于"必选且可能很多"的输入，必须给整行宽度。 */
  .nv-detail .vstack > .nv-block:has(.nv-races),
  .nv-detail .vstack > .nv-block:has(.switch-race),
  .nv-detail .vstack > .nv-block:has(.switch-sku) {
    grid-column: 1 / -1;
  }

  /* ★ 注意【不】把人机验证加进白名单：
     它现在与邮箱、数量同处一行，各占一列。
     三列时若把它拉成整行，它会孤零零占一行，白费空间。 */
}

/* 中等宽度：三列会挤（每列不足 170px），回落到两列 */
@media (min-width: 1100px) and (max-width: 1279px) {
  .nv-detail .vstack { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 窄屏：确保回落到单列（默认 block 流即是单列，这里只是显式声明意图） */
@media (max-width: 1099px) {
  .nv-detail .vstack > .nv-block { grid-column: auto; }
}

/* ══════════════════════════════════════════════════════════════════════
   10.4 详情页右栏 · 紧凑化（降低输入框高度，压缩整块高度）
   ----------------------------------------------------------------------
   ★★ 2026-10-14 用户要求："适当降低输入框高度，让整个区域高度适当降低"。
     基础 .nv-input 的 min-height 是 --nv-tap-min（44px，为触屏点击区留的），
     在详情页这种"字段密集"的表单里偏高，4 个字段就多占 40px+。

     ★ 只作用于详情页右栏，不动全局 .nv-input ——
       登录/注册/查询等页面的 44px 点击区是有意义的，
       不能为了详情页把它们一起压小。
     ★ 44 → 38px 是被动式可达的（WCAG 建议交互目标 44px 是"目标大小"，
       但表单输入框有 label 与整行热区，38px 仍在可接受范围）。
     ══════════════════════════════════════════════════════════════════════ */
.nv-detail__info .nv-input {
  min-height: 38px;
  padding: var(--nv-space-2) var(--nv-space-3);
  font-size: 14px;            /* 比全局 16px 收一档，桌面端更精致 */
}

/* 标签与控件之间的间距也同步收一档（默认 8px → 6px） */
.nv-detail__info .nv-field { margin-bottom: var(--nv-space-3); }
.nv-detail__info .nv-label { margin-bottom: 6px; }

/* 数量步进同步收矮，与输入框等高 */
.nv-detail__info .nv-qty { height: 38px; }
.nv-detail__info .nv-qty__btn { height: 38px; }
.nv-detail__info .nv-qty__input { height: 36px; }

/* 下拉框（如"自动结算方式"）同步 */
.nv-detail__info .nv-select { min-height: 38px; }

/* .nv-pricebox 兼作 .abacus 时，核心会替换 .price 内的内容 */
.nv-pricebox.abacus .price {
  display: flex;
  align-items: baseline;
  gap: 1px;
  color: var(--nv-price-promo);
}

.nv-pricebox.abacus .price .unit {
  font-size: var(--nv-fs-heading);
  font-weight: var(--nv-weight-bold);
}

.nv-pricebox.abacus .price {
  font-size: 2.25rem;
  font-weight: var(--nv-weight-bold);
  line-height: 1;
  letter-spacing: -.03em;
}

@media (min-width: 768px) {
  .nv-pricebox.abacus .price { font-size: 2.75rem; }
}

/* 算价中：核心塞 spinner 进来 */
.nv-pricebox.abacus .price .icon-spin { color: var(--nv-brand); }

/* 支付方式列表：核心动态 append，这里给统一样式
   ★★ 2026-10-14：改为 2×2 网格（原来是单列 flex）。
     4 个渠道单列竖排要多占约 110px，把右栏顶高、
     破坏与左栏的等高平衡。2×2 只多占约 55px。
   ★ 必须写在这里（原 .pay-list 定义处），不能只在上面写 --grid 类 ——
     同等特异度下【后定义的规则胜出】，写在前面会被这条覆盖。
     （实测踩过：.pay-list--grid 在 1395 行、.pay-list 在 1522 行，
      最后 display 仍是 flex。） */
.pay-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--nv-space-2);
}

.pay-list > * {
  display: flex;
  align-items: center;
  gap: var(--nv-space-3);
  min-height: var(--nv-tap-min);
  padding: var(--nv-space-3) var(--nv-space-4);
  background-color: var(--nv-elevated);
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  cursor: pointer;
  transition: border-color var(--nv-dur-fast) var(--nv-ease-out),
              background-color var(--nv-dur-fast) var(--nv-ease-out);
}

/* ★ 支付图标与名称：核心注入的结构是 <a class="pay"><img><span>名称</span></a>。
   图标统一 30×30 且不参与压缩；名称过长时省略，避免撑破网格。 */
.pay-list > * > img {
  flex: 0 0 30px;
  width: 30px;
  height: 30px;
  object-fit: contain;
  border-radius: 7px;
}
.pay-list > * > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--nv-fs-caption);
}

/* 窄屏回单列：两个支付卡片挤在一行会看不清名称 */
@media (max-width: 520px) {
  .pay-list { grid-template-columns: 1fr; }
}

.pay-list > *:hover {
  border-color: var(--nv-border-brand);
  background-color: var(--nv-brand-softer);
}

/* 分享按钮 */
/* ── 分享按钮（图标 + 文字）─────────────────────────────────
   ★★ 2026-10-14 用户要求："分享按钮更明显一点，以 名字+按钮 的形式"
   原实现：纯图标徽章（.nv-badge--info + 一个 fa-share 图标），
          和旁边「已售 0」「库存 5000」这些纯信息徽章长得一样，
          用户完全看不出它可点。
   现在：· 加「分享」文字，语义直白
         · 品牌色描边 + 浅底，与灰色信息徽章视觉分层
         · hover / active 有反馈，强化"可操作"
   ★ 类名 shared-button 必须保留 —— 核心 item.js 的 _ShareItem()
     靠 `.shared-button` 绑 click（复制分享链接）。 */
.shared-button {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 10px;
  border: 1px solid rgba(var(--nv-brand-rgb), 0.45);
  border: 1px solid color-mix(in srgb, var(--nv-brand) 45%, transparent);
  border-radius: var(--nv-radius-pill);
  background-color: var(--nv-brand-softer);
  color: var(--nv-brand-ink);
  font-size: var(--nv-fs-micro);
  font-weight: var(--nv-weight-medium);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out),
              border-color var(--nv-dur-fast) var(--nv-ease-out),
              color var(--nv-dur-fast) var(--nv-ease-out),
              transform var(--nv-dur-fast) var(--nv-ease-out);
}
.shared-button i { font-size: 12px; }
.shared-button:hover {
  background-color: var(--nv-brand);
  border-color: var(--nv-brand);
  color: #fff;
}
.shared-button:active { transform: scale(.96); }
.shared-button:focus-visible {
  outline: 2px solid var(--nv-brand);
  outline-offset: 2px;
}

/* 验证码刷新
   ★ 2026-10-14：人机验证改为与「购买数量」共占一行（半宽）后，
     验证码图不能再挤占输入框空间 —— 这里收窄它的最小宽度，
     并让它随列宽自适应，避免在窄列里把输入框压成一条缝。 */
.captcha-img {
  flex: 0 0 auto;
  min-width: 84px;
  max-width: 40%;
  border: 1px solid var(--nv-border);
  object-fit: cover;
}
/* 窄列里的验证码输入框：允许收缩，保证图和框都在一行内 */
.nv-detail .captcha-input { min-width: 0; }

/* 自定义控件里的输入框对齐主题 */
.nv-widget .form-control {
  width: 100%;
  min-height: var(--nv-tap-min);
  padding: var(--nv-space-3) var(--nv-space-4);
  background-color: var(--nv-elevated);
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  font-size: 16px;
  color: var(--nv-text-primary);
}

.nv-widget .form-control:focus {
  border-color: var(--nv-brand);
  box-shadow: var(--nv-ring);
}

.nv-widget .form-check-input { margin-top: .3em; }

/* --------------------------------------------------------------------------
   11. 响应式显隐：分类胶囊 / 侧栏分类树
   --------------------------------------------------------------------------
   桌面端用左侧分类树（信息密度高，能看到二级分类）；
   窄屏用顶部横向胶囊（节省纵向空间，手指好点）。
   两者互斥显示，由 JS 按断点切换。
   -------------------------------------------------------------------------- */
.nv-cats--desktop { display: none; }

@media (min-width: 1024px) {
  .nv-cats--desktop { display: block; }
  .nv-cats--mobile { display: none; }
}

/* 侧栏粘性：滚动时分类树跟随
   ★★ 为什么还要给一个 max-height + overflow-y:auto：
      分类多的时候（本项目 10 个一级 × 若干二级 ≈ 1300px+），侧栏高度会远超
      右侧商品列表（10 条 ≈ 1000px）。grid 行高取两者最大值，
      于是主区下方空出一大片 → 页脚被推到很远（用户反馈"页脚太置底"）。
      把侧栏限制在视口内可滚动后，它就【不再撑高整行】，
      页脚位置改由右侧列表决定，视觉上贴合很多。
   ★ calc 里减去 header 与上下留白，保证粘住时底部不被裁。 */
.side-sticky {
  position: sticky;
  top: calc(var(--nv-header-h) + var(--nv-space-4));
  max-height: calc(100vh - var(--nv-header-h) - var(--nv-space-8));
  overflow-y: auto;
  overscroll-behavior: contain;
  /* 让滚动条不贴边、且不占位时也不抖动 */
  scrollbar-width: thin;
  scrollbar-gutter: stable;
}
/* WebKit 侧栏细滚动条：默认太粗，会挤掉内容宽度 */
.side-sticky::-webkit-scrollbar { width: 6px; }
.side-sticky::-webkit-scrollbar-track { background: transparent; }
.side-sticky::-webkit-scrollbar-thumb {
  background-color: rgba(var(--nv-border-rgb), calc(var(--nv-border-a) * 0.8));
  background-color: color-mix(in srgb, var(--nv-border) 80%, transparent);
  border-radius: 3px;
}
.side-sticky::-webkit-scrollbar-thumb:hover { background-color: var(--nv-border); }

/* 窄屏（非粘性）时不需要限高，避免小屏出现内层滚动 */
@media (max-width: 1023px) {
  .side-sticky {
    max-height: none;
    overflow-y: visible;
    scrollbar-gutter: auto;
  }
}

/* ==========================================================================
   12. 横向扁平卡片 · .nv-pcard--flat
   --------------------------------------------------------------------------
   商城列表页的主卡片形态。相比纵向卡片的取舍：
     · 图、标题、价格在同一水平线，扫视效率高，同屏可见更多商品
     · 右侧价格区做成独立浅色面板，让价格有明确落点

   ★ 本节的每条规则都写全（display / flex-direction / align-items 等），
     不依赖 .nv-pcard 的继承 —— 因为 .nv-pcard 是纵向卡片的默认形态，
     两边同时命中时，缺哪条属性就会塌成截图里那种错位布局。

   结构：
     <a class="nv-pcard nv-pcard--flat">
       <span class="nv-flat__thumb">图片</span>
       <span class="nv-flat__main">
         <span class="nv-flat__title">标题</span>
         <span class="nv-flat__tags">标签</span>
       </span>
       <span class="nv-flat__side">
         <span class="nv-flat__price">价格</span>
         <span class="nv-flat__cta">购买</span>
       </span>
     </a>

   注意：用 <span> 而非 <div> 承载，因为 a 内不能套块级元素。
   下面所有 *__ 元素都显式设了 display，不能省。
   ========================================================================== */

/* ── 列表容器 ─────────────────────────────────────────── */
/* .nv-list 本身只定义间距。
   布局（display/flex-direction）由上面第 3 节的 .item-list.nv-list 统一定义
   —— 因为核心 JS 会移除 nv-grid 类，必须挂在 .item-list 上才生效。 */
.nv-list {
  gap: 10px;
}

/* ── 卡片主体：横向三段式 ─────────────────────────────── */

.nv-flat__price .cur {
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-bold);
}

.nv-flat__price .amt {
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.25rem;
  font-weight: var(--nv-weight-bold);
  line-height: 1;
  letter-spacing: -.02em;
}

.nv-flat__price .was {
  font-size: var(--nv-fs-micro);
  color: var(--nv-price-original);
  text-decoration: line-through;
  text-decoration-color: var(--nv-price-strike);
  margin-left: 5px;
}

/* ── 交互态 ───────────────────────────────────────────── */
@media (hover: hover) {
}

/* ── 售罄态 ───────────────────────────────────────────── */
.nv-pcard--flat.is-soldout {
  opacity: .74;
  cursor: default;
}
.nv-pcard--flat.is-soldout .nv-flat__thumb img { filter: grayscale(.5); }
.nv-pcard--flat.is-soldout .nv-flat__price { color: var(--nv-text-faint); }
.nv-pcard--flat.is-soldout .nv-flat__cta {
  background-image: none;
  background-color: var(--nv-soft);
  color: var(--nv-text-faint);
  box-shadow: none;
}

/* ── 窄屏：图缩小、隐藏 CTA、价格面板收窄 ─────────────── */
@media (max-width: 640px) {
  .nv-flat__price .amt { font-size: 1.125rem; }
}

/* ==========================================================================
   13. 分页 · .nv-pager
   --------------------------------------------------------------------------
   商品列表底部的翻页控件。

   规则（避免页数很多时按钮铺满整行）：
     · 首页/末页恒显，当前页左右各留 2 页
     · 中间用省略号折叠
     · 窄屏只显示「上一页 / 页码 / 下一页」的简版

   结构：
     <nav class="nv-pager">
       <span class="nv-pager__total">共 382 件 · 32 页</span>
       <div class="nv-pager__list">
         <a class="nv-pager__btn">上一页</a>
         <a class="nv-pager__num is-on">1</a>
         <a class="nv-pager__num">2</a>
         <span class="nv-pager__gap">…</span>
         <a class="nv-pager__num">32</a>
         <a class="nv-pager__btn">下一页</a>
       </div>
     </nav>
   ========================================================================== */
.nv-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--nv-space-3);
  margin-top: var(--nv-space-6);
  padding-top: var(--nv-space-5);
  border-top: 1px solid var(--nv-border-soft);
}

.nv-pager__total {
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}
.nv-pager__total b {
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  color: var(--nv-text-secondary);
  font-weight: var(--nv-weight-medium);
}

/* ★★ 2026-10-15 对齐 Nuxt PaginationNav：
   把页码组包进一个圆角浅底容器，而不是让按钮各自飘在页面上。
   好处：① 一组控件在视觉上成为一个整体，不像散落的独立按钮
        ② 与 Nuxt 的 `rounded-xl ... p-1.5` 结构一致
        ③ 当前页仍用品牌色实心，在浅底上更跳 */
.nv-pager__list {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  padding: 5px;
  border: 1px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-lg);
  background-color: var(--nv-elevated);
}

/* 页码 / 上下页：统一 34px 按钮，容器内不再各自描边 */
.nv-pager__num,
.nv-pager__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 34px;
  padding-inline: 10px;
  border: 1px solid transparent;
  border-radius: var(--nv-radius-md);
  background-color: transparent;
  font-size: var(--nv-fs-caption);
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  color: var(--nv-text-secondary);
  cursor: pointer;
  transition:
    background-color var(--nv-dur-fast) var(--nv-ease-out),
    border-color var(--nv-dur-fast) var(--nv-ease-out),
    color var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-pager__btn { font-family: var(--nv-font); }

.nv-pager__num:hover,
.nv-pager__btn:hover:not(.is-disabled) {
  background-color: var(--nv-brand-softer);
  color: var(--nv-brand-ink);
}

/* 当前页：品牌实心 */
.nv-pager__num.is-on {
  background-image: var(--nv-gradient);
  border-color: transparent;
  color: #fff;
  font-weight: var(--nv-weight-bold);
  box-shadow: var(--nv-shadow-brand);
}

/* 首末页不可用时 */
.nv-pager__btn.is-disabled {
  opacity: .38;
  cursor: not-allowed;
  pointer-events: none;
}

.nv-pager__gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 34px;
  color: var(--nv-text-faint);
  user-select: none;
}

/* 窄屏：隐去数字页码，只留上下页 + 当前页码 */
@media (max-width: 640px) {
  .nv-pager {
    flex-direction: column;
    align-items: stretch;
    gap: var(--nv-space-3);
  }
  .nv-pager__list { justify-content: center; }
  .nv-pager__num, .nv-pager__gap { display: none; }
  .nv-pager__num.is-on { display: inline-flex; }
  .nv-pager__btn { flex: 1; max-width: 140px; }
}

/* ==========================================================================
   13.5 分页 · 「跳转到指定页」（2026-10-17）
   --------------------------------------------------------------------------
   ★ 商品页（theme.js renderPager）与文章页（服务端模板）共用这一套类名，
     所以样式只需写一份 —— 这正是"两处翻页样式一致"的落点。

   ★★ 结构已调整为【并入同一个容器】（用户反馈"不应该拆分成 2 个"）：
       <div class="nv-pager__list">
         …上一页 1 2 3 … 下一页…
         <span class="nv-pager__sep"></span>          ← 1px 竖线
         <form class="nv-pager__jump">前往 [N] 页 跳转</form>
       </div>

   所以 .nv-pager__jump 现在【不再有自己的边框/底色/padding】——
   它只是容器内的一个行内子项，自己再套一层白框就变成"框里套框"了。
   ========================================================================== */
.nv-pager__jump {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* ★ 无边框 / 无底色 / 无 padding —— 融入父容器 .nv-pager__list */
}

/* 页码组与跳转组之间的分隔线。
   ★ 用 1px 竖线而不是留大间距：
     两者同属一个控件组，间距过大又会回到"看起来是两块"的问题。
   ★ align-self: stretch 让它垂直撑满，高度随容器走（不写死像素）。 */
.nv-pager__sep {
  align-self: stretch;
  width: 1px;
  margin: 2px 4px;
  background-color: var(--nv-border-soft);
}

.nv-pager__jump-label,
.nv-pager__jump-unit {
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
  white-space: nowrap;
}
.nv-pager__jump-label { padding-left: 4px; }
.nv-pager__jump-unit  { padding-right: 2px; }

/* 输入框：去掉浏览器默认的上下箭头 + 数字微调器，
   与页码按钮同高、同圆角、等宽数字，避免宽度随位数跳动 */
.nv-pager__jump-input {
  width: 48px;
  height: 34px;
  padding: 0 6px;
  border: 1px solid transparent;
  border-radius: var(--nv-radius-md);
  background-color: var(--nv-soft);
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-secondary);
  text-align: center;
  outline: none;
  transition: border-color var(--nv-dur-fast) var(--nv-ease-out),
              background-color var(--nv-dur-fast) var(--nv-ease-out);
  /* ★ 隐藏 number 类型的微调箭头（Firefox / WebKit 各有写法） */
  -moz-appearance: textfield;
  appearance: textfield;
}
.nv-pager__jump-input::-webkit-outer-spin-button,
.nv-pager__jump-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.nv-pager__jump-input:focus {
  border-color: var(--nv-border-brand);
  background-color: var(--nv-elevated);
}

.nv-pager__jump-btn {
  height: 34px;
  padding: 0 12px;
  border: none;
  border-radius: var(--nv-radius-md);
  background-color: var(--nv-brand-solid);
  color: var(--nv-text-on-solid);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  cursor: pointer;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.nv-pager__jump-btn:hover { background-color: var(--nv-brand-hover); }

/* 窄屏：整条分页器仍然是一行，只是内部收紧。
   ★ 结构变化后（跳转已并入 __list），这里要处理的是【容器内的拥挤】：
     640px 以下数字页码本来就会被隐藏（见上面的规则），
     剩下「上一页 · 当前页 · 下一页 | 前往 N 页 跳转」——
     约 上一页84 + 页码34 + 下一页84 + 分隔9 + 跳转约170 = 381px，
     在 375px 手机上会溢出，所以把上下页与跳转按钮一起收窄。
   ★ .nv-pager__list 允许换行（flex-wrap 已在基础规则里设了），
     极端窄屏下跳转组会掉到第二行 —— 这比横向溢出好。 */
@media (max-width: 640px) {
  .nv-pager__list { justify-content: center; padding: 4px; gap: 2px; }
  .nv-pager__btn { flex: 0 1 auto; max-width: none; padding-inline: 10px; }
  .nv-pager__sep { margin: 2px 3px; }
  .nv-pager__jump { gap: 4px; }
  .nv-pager__jump-label { padding-left: 2px; }
  .nv-pager__jump-input { width: 40px; flex: 0 0 40px; }
  .nv-pager__jump-btn { padding: 0 9px; }
}


/* ==========================================================================
   14. 首页分区 · Section
   --------------------------------------------------------------------------
   ★ 这一节原本写在 _demo/index.html 的内联 <style> 里（35 个类，约 9800 字符）。
     迁移时发现：demo 能显示是因为内联样式，而真实模板只引 css/*.css，
     所以模板渲染出来中间一片空白。现已全部搬到这里。

   内容：
     .shop-layout / .side-*  —— 二分栏 + 左侧分类树
     .hero*                  —— 顶部横幅
     .nv-tone*               —— 标签色板
     .sec-head               —— 区块标题
   ========================================================================== */


/* --------------------------------------------------------------------------
   14.1 布局骨架 · 二栏（侧栏 268px + 主区）
   --------------------------------------------------------------------------
   ★ 248 → 268：商品全部下沉到二级分类后，侧栏要显示的层级多了一层 ——
     二级名至少 5 个字（「长视频平台」「海外流媒体」），
     248px 时被截断成「长视…」，反而看不清分类。
     容器已放宽到 1480，侧栏让出 20px 不影响主区观感。
   -------------------------------------------------------------------------- */
.shop-layout { display: grid; grid-template-columns: 1fr; gap: var(--nv-space-6); align-items: start; }
@media (min-width: 1024px) {
  .shop-layout { grid-template-columns: 268px minmax(0, 1fr); }
}

.side-card {
  background: var(--nv-elevated);
  border: .5px solid rgba(var(--nv-border-soft-rgb), calc(var(--nv-border-soft-a) * 0.45));
  border: .5px solid color-mix(in srgb, var(--nv-border-soft) 45%, transparent);
  border-radius: var(--nv-radius-card);
  padding: var(--nv-space-3);
  box-shadow: 0 1px 2px -1px rgba(0, 0, 0, .06), 0 8px 22px -14px rgba(0, 0, 0, .16);
}

.side-title {
  display: flex; align-items: center; gap: var(--nv-space-2);
  font-size: var(--nv-fs-caption); font-weight: var(--nv-weight-bold);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--nv-text-muted);
  padding: var(--nv-space-2) var(--nv-space-3) var(--nv-space-3);
}
.side-title .material-icons-outlined { font-size: 16px; color: var(--nv-brand); }

/* ── 一级分类：独立卡片容器 ────────────────────────────── */
.side-link {
  position: relative;
  display: flex; align-items: center; gap: var(--nv-space-3);
  min-height: 58px; padding: var(--nv-space-2) var(--nv-space-3);
  margin-bottom: 4px;
  border-radius: var(--nv-radius-btn);
  border: 1px solid transparent;       /* 占位，hover/选中时显形 */
  font-size: var(--nv-fs-body); font-weight: var(--nv-weight-medium);
  color: var(--nv-text-primary); cursor: pointer;
  transition:
    background-color var(--nv-dur-fast) var(--nv-ease-out),
    border-color var(--nv-dur-fast) var(--nv-ease-out),
    box-shadow var(--nv-dur-fast) var(--nv-ease-out),
    transform var(--nv-dur-fast) var(--nv-ease-out);
}

/* hover：浮起 + 品牌色描边 */
.side-link:hover {
  background-color: var(--nv-elevated);
  border-color: var(--nv-border-soft);
  box-shadow: var(--nv-shadow-1);
  transform: translateY(-1px);
}

/* 选中：品牌渐变底 + 白字（icon 方块反白） */
.side-link.is-on {
  background-image: var(--nv-gradient);
  border-color: transparent;
  color: #fff;
  box-shadow: var(--nv-shadow-brand);
}
.side-link.is-on .side-name { color: #fff; }
.side-link.is-on .side-desc { color: rgba(255, 255, 255, .78); }
.side-link.is-on .side-cnt { color: rgba(255, 255, 255, .9); }

/* 一级图标：44px 圆角方块 */
.side-ico {
  flex: 0 0 44px; width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
  font-size: 22px;
  overflow: hidden;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.side-ico > img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  border-radius: inherit;
}
.side-ico .material-icons-outlined,
.side-ico i { font-size: 22px; line-height: 1; }
.side-link:hover .side-ico { background-color: var(--nv-brand-soft); }
.side-link.is-on .side-ico {
  background-image: none;
  background-color: rgba(255, 255, 255, .22);
}

/* 「推荐商品」侧栏项的皇冠 —— 与商品卡片徽章同一套金色语言。
   ★ 侧栏图标位是 44×44 的方框，字形要跟着放大才显得"满"，
     直接复用 .side-ico 的 22px 字号会偏小、四周空荡。 */
.side-ico .nv-crown {
  font-size: 24px;
  line-height: 1;
  color: var(--nv-gold);
}
.nv-crown--sm { font-size: 1.05em; line-height: 1; }

/* 分类文字块 */
.side-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.side-name { line-height: 1.3; }
.side-desc {
  font-size: var(--nv-fs-micro);
  font-weight: var(--nv-weight-regular);
  color: var(--nv-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.side-cnt {
  margin-left: auto; flex-shrink: 0;
  font-size: var(--nv-fs-caption); color: var(--nv-text-faint);
  font-family: var(--nv-mono); font-variant-numeric: tabular-nums;
}

/* ── 可展开分组 · .side-group ──────────────────────────
   结构：
     <div class="side-group is-open?">
       <div class="side-row">
         <button class="side-toggle-all">  ← 一级：整行可点，纯展开/收起
       </div>
       <div class="side-sub">…二级分类…</div>
     </div>

   ★ 一级分类为什么是 <button> 而不是分类链接：
     商品全部挂二级分类，一级的 commodity_count 恒为 0。
     让它可点击切分类 = 点进去必然看到空列表，是明确的坏体验。
     → 一级行降级为"分组标题"，只负责展开/收起，不再是分类入口。

   ★ 折叠用 grid-template-rows 0fr → 1fr 过渡，而不是 max-height：
     max-height 需要猜一个够大的值，展开时高度不对会显得"跳"；
     grid 1fr 是内容真实高度，动画平滑且无需 JS 量高。
   -------------------------------------------------------------------------- */
.side-group {
  display: grid;
  /* 单行网格：标题行固定 auto，子列表可折叠 */
  grid-template-rows: auto 0fr;
  transition: grid-template-rows var(--nv-dur-normal) var(--nv-ease-out);
}
.side-group.is-open {
  grid-template-rows: auto 1fr;
}

.side-row {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}

/* 一级行：整行是一个按钮，占满宽度 */
.side-toggle-all {
  flex: 1;
  width: 100%;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--nv-space-2);
  padding: 6px var(--nv-space-2);
  border-radius: var(--nv-radius-card);
  text-align: left;
  cursor: pointer;
  color: inherit;
  transition:
    background-color var(--nv-dur-fast) var(--nv-ease-out),
    color var(--nv-dur-fast) var(--nv-ease-out);
}
.side-toggle-all:hover { background-color: var(--nv-brand-softer); }
/* 展开态给个浅底，一眼看出"这一组是展开的" */
.side-group.is-open .side-toggle-all { background-color: var(--nv-brand-softer); }

.side-arrow {
  flex: 0 0 auto;
  font-size: var(--nv-icon-md);
  color: var(--nv-text-faint);
  transition: transform var(--nv-dur-normal) var(--nv-ease-out),
              color var(--nv-dur-fast) var(--nv-ease-out);
}
.side-group.is-open .side-arrow { transform: rotate(180deg); }
.side-group.is-current .side-arrow,
.side-group.is-current .side-toggle-all .side-name { color: var(--nv-brand); }

/* 一级的计数：比二级的淡一档，避免和二级数字抢注意力 */
.side-toggle-all .side-cnt {
  font-weight: var(--nv-weight-medium);
  color: var(--nv-text-faint);
}

/* 二级容器：即折叠体
   ★ 0fr → 1fr 生效的前提是子元素 overflow:hidden。 */
.side-sub {
  /* 缩进 22 → 12px：二级名有 5 个字，原来 22px 缩进 + 32px 图标
     把可用宽度压到不足 80px，长名被截断。左侧色条已足够表达层级从属，
     不必再靠大缩进。 */
  margin: 2px 0 8px 12px;
  padding: 0 var(--nv-space-2) 0 var(--nv-space-2);
  border-left: 2px solid rgba(var(--nv-brand-rgb), 0.22);
  border-left: 2px solid color-mix(in srgb, var(--nv-brand) 22%, transparent);
  border-radius: 0 var(--nv-radius-btn) var(--nv-radius-btn) 0;
  background-color: rgba(var(--nv-brand-rgb), 0.035);
  background-color: color-mix(in srgb, var(--nv-brand) 3.5%, transparent);
  /* ★ 折叠必需的裁剪 —— 没有它，0fr 时内容仍会溢出来可见 */
  overflow: hidden;
  min-height: 0;
  /* 内边距参与折叠过渡会突然出现，改为展开后再给上间距 */
  transition: padding var(--nv-dur-normal) var(--nv-ease-out);
}
.side-group.is-open .side-sub {
  padding-top: var(--nv-space-2);
  padding-bottom: var(--nv-space-2);
}
/* 折叠态：彻底移出可聚焦序列，避免 Tab 键跳进看不见的链接 */
.side-group:not(.is-open) .side-sub { visibility: hidden; }

.side-sub .side-link {
  min-height: 40px;
  margin-bottom: 2px;
  padding: 4px var(--nv-space-2);
  border-radius: calc(var(--nv-radius-btn) - 2px);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-regular);
}
.side-sub .side-link:last-child { margin-bottom: 0; }

/* 二级 hover：更轻的反馈（不浮起，避免和一级抢） */
.side-sub .side-link:hover {
  background-color: var(--nv-elevated);
  border-color: transparent;
  box-shadow: none;
  transform: none;
}

/* 二级选中：不再用渐变满铺（层级更弱），改为浅品牌底 + 左侧色点 */
.side-sub .side-link.is-on {
  background-image: none;
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
  box-shadow: none;
}
.side-sub .side-link.is-on .side-name { color: var(--nv-brand-ink); }
.side-sub .side-link.is-on .side-cnt { color: var(--nv-brand-ink); }

/* 二级图标 28px（32 → 28，给文字再让 4px） */
.side-sub .side-ico {
  flex: 0 0 28px; width: 28px; height: 28px;
  border-radius: calc(var(--nv-radius-btn) - 2px);
  background-color: transparent;
}
.side-sub .side-ico .material-icons-outlined,
.side-sub .side-ico i { font-size: 16px; }
.side-sub .side-cnt { font-size: var(--nv-fs-micro); }
/* 二级名称：占满剩余宽度，超长才省略（正常情况下 5 个字能完整显示） */
.side-sub .side-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }




/* --------------------------------------------------------------------------
   14.3 响应式显隐：桌面走侧栏树 / 窄屏走顶部胶囊
   -------------------------------------------------------------------------- */
.nv-cats--desktop { display: none; }
@media (min-width: 1024px) {
  .nv-cats--desktop { display: block; }
  .nv-cats--mobile { display: none; }
}



/* --------------------------------------------------------------------------
   14.4 Hero 横幅
   ★ 装饰光斑必须用 radial-gradient + 容器内侧定位。
     用负偏移会跑出 overflow:hidden 被裁掉（曾踩过）。
   ★ .hero__grid 是 <span>，必须显式 display:block，
     否则 inline 元素上 inset:0 不生效。
   -------------------------------------------------------------------------- */
/* Hero：320px 高，内部用 flex 垂直居中 */
.hero {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 320px;
  border-radius: var(--nv-radius-card);
  background-image: var(--nv-gradient);
  color: #fff;
  padding: var(--nv-space-8) var(--nv-space-6);
  margin-bottom: var(--nv-space-6);
  box-shadow: var(--nv-shadow-brand);
}
@media (min-width: 768px) {
  .hero { padding: var(--nv-space-10) var(--nv-space-10); }
}
@media (min-width: 1280px) {
  .hero { padding: var(--nv-space-10) var(--nv-space-12); }
}
/* 装饰光斑：径向渐变 + 容器内定位（之前用负偏移跑出容器被 overflow 裁掉了） */
.hero::before,
.hero::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  background-image: radial-gradient(circle at 50% 50%, rgba(255,255,255,.30), rgba(255,255,255,0) 68%);
}
/* 右上大光斑：贴住容器右上角内侧，避免被 overflow:hidden 裁掉 */
.hero::before {
  width: 520px; height: 520px;
  right: -60px; top: -220px;
}
/* 右下小光斑 */
.hero::after {
  width: 340px; height: 340px;
  right: 140px; bottom: -170px;
  background-image: radial-gradient(circle at 50% 50%, rgba(255,255,255,.22), rgba(255,255,255,0) 70%);
}

/* 网格纹理：极淡，增加质感 */
.hero__grid {
  display: block;
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255,255,255,.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.06) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(ellipse 70% 80% at 78% 50%, #000 20%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 70% 80% at 78% 50%, #000 20%, transparent 75%);
}

.hero__inner {
  position: relative; z-index: 1;
  /* ★ 两栏：左文案 + 右服务承诺。1280 以上才分栏，
     窄屏堆叠 —— 否则左侧文案被压成"一行三四个字"，反而更难读。 */
  display: grid;
  gap: var(--nv-space-8);
  width: 100%;
}
@media (min-width: 1024px) {
  .hero__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
  }
}
.hero__main { max-width: 640px; }

.hero__eyebrow {
  display: inline-flex; align-items: center; gap: 6px;
  height: 24px; padding-inline: 11px; margin-bottom: var(--nv-space-3);
  border-radius: var(--nv-radius-pill);
  background: rgba(255, 255, 255, .20);
  font-size: var(--nv-fs-micro); font-weight: var(--nv-weight-medium);
}

.hero__title {
  font-size: clamp(1.375rem, 3vw, 1.875rem);
  font-weight: var(--nv-weight-bold);
  line-height: 1.15;
  letter-spacing: -.03em;
  margin-bottom: var(--nv-space-3);
}

.hero__desc {
  font-size: var(--nv-fs-caption);
  line-height: 1.6;
  color: rgba(255, 255, 255, .88);
  margin-bottom: var(--nv-space-4);
  max-width: 52ch;
}

.hero__actions { display: flex; flex-wrap: wrap; gap: var(--nv-space-3); }

/* ── Hero 主视觉按钮（深色渐变底上的两种按钮）────────────────────────
   ★ 2026-10-16 统一：圆角由 --nv-radius-pill（胶囊）改为
     --nv-radius-btn（12px 圆角矩形）——
     与全站按钮、卡片、输入框同一套圆角，消除「胶囊 vs 圆角矩形」的混用。

   ★ 这两个按钮在品牌渐变底上，所以配色语义与通用 .nv-btn 不同：
       主按钮 = 白底 + 品牌蓝字（在深底上形成最高对比）
       次按钮 = 半透明白底 + 白字 + 白描边（玻璃质感）
     这不是「重复设计」，而是【同色系底上的必然反色】，
     所以保留自定义配色，只对齐圆角与尺寸体系。
   ★ 硬编码色值改为引用 token，便于主题换色时跟随。 */
.hero__btn {
  display: inline-flex; align-items: center; gap: var(--nv-space-2);
  height: 38px; padding-inline: var(--nv-space-4);
  border-radius: var(--nv-radius-btn);          /* ← 由 pill 改为 btn 圆角 */
  font-size: var(--nv-fs-caption); font-weight: var(--nv-weight-medium);
  transition: transform var(--nv-dur-fast) var(--nv-ease-out),
              background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.hero__btn:hover { transform: translateY(-1px); }
.hero__btn--solid { background: var(--nv-text-on-solid); color: var(--nv-brand-ink); }
.hero__btn--ghost {
  background: rgba(255, 255, 255, .16);
  color: var(--nv-text-on-solid);
  border: 1px solid rgba(255, 255, 255, .32);
}
.hero__btn--ghost:hover { background: rgba(255, 255, 255, .26); }

/* --------------------------------------------------------------------------
   Hero 右侧 · 服务承诺
   --------------------------------------------------------------------------
   ★ 为什么是"三条承诺"而不是数字统计：
     数字（商品数/成交数）在演示环境里是编的，站长上线后也未必维护，
     摆上去等于给他埋一个"数据对不上"的坑。
     而"付款即发 / 售后保障 / 全天候售后"是**业务事实**，
     不依赖任何统计口径，站长不用管、也不会过期。
   -------------------------------------------------------------------------- */
.hero__points {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 1024px) {
  .hero__points { width: 260px; }
}

.hero__point {
  display: flex; align-items: center; gap: var(--nv-space-3);
  padding: var(--nv-space-3) var(--nv-space-4);
  border-radius: var(--nv-radius-card);
  /* 半透明白底 + 描边：在渐变背景上"玻璃感"，比纯色块更轻 */
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .20);
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.hero__point:hover { background: rgba(255, 255, 255, .18); }

.hero__point-ico {
  flex: 0 0 34px; width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--nv-radius-btn);
  background: rgba(255, 255, 255, .22);
  font-size: 15px;
}
.hero__point-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.hero__point-txt b { font-size: var(--nv-fs-caption); font-weight: var(--nv-weight-medium); }
.hero__point-txt small { font-size: var(--nv-fs-micro); color: rgba(255, 255, 255, .76); }

/* Hero 数据条 */



/* --------------------------------------------------------------------------
   14.6 区块标题
   -------------------------------------------------------------------------- */
.sec-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--nv-space-3); margin-bottom: var(--nv-space-4);
}
.sec-head h2 {
  font-size: var(--nv-fs-heading); font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-ls-heading);
}
.sec-head a { font-size: var(--nv-fs-caption); }



/* --------------------------------------------------------------------------
   16. 订单查询结果 · Query Results
   --------------------------------------------------------------------------
   ★★ 为什么必须单独写这一节：
     结果区由核心 assets/user/controller/index/query.js 动态生成，
     用的是它自己的一套类名（.order-item / .card-* / .goods-* / .shipment-*）。
     主题里【一个都没定义过】→ 结果渲染成"一堵没有排版的文字墙"：
     商品图撑满整行、订单信息挤成一列、状态徽章没有颜色区分。

     这一节把这些类名按 idcub 的色板与留白纪律重新组织：
       卡片层：  .order-item          订单卡
       头部层：  .order-header/.order-status/.status-badge   状态
       信息层：  .order-basic/.order-no/.order-time/.payment-*  订单元信息
       金额层：  .order-right/.order-amount/.amount-*        右上金额
       商品层：  .goods-section/.goods-thumb/.goods-image/.goods-details/.goods-name/.goods-meta/.goods-sku
       卡密层：  .card-section/.card-header/.card-title/.shipment-*/.card-display/
                 .card-password-section/.card-password-input/.view-card-btn/.card-content
     ⚠ 这些类名是核心契约，【不能改】，只能在外观层覆盖。

   ★ 宽度与查询表单统一（都用 --q-max = 100%，即页面容器宽度）。
   -------------------------------------------------------------------------- */
.order-results,
.no-results,
.loading-state {
  max-width: var(--q-max);
}

/* 结果标题与卡片间距 */
.order-results > .nv-section-title { margin-top: var(--nv-space-8); }

/* --------------------------------------------------------------------------
   16.1 订单卡容器
   -------------------------------------------------------------------------- */
.order-list {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-5);
}

.order-item {
  position: relative;
  background-color: var(--nv-elevated);
  border: .5px solid rgba(var(--nv-border-soft-rgb), calc(var(--nv-border-soft-a) * 0.45));
  border: .5px solid color-mix(in srgb, var(--nv-border-soft) 45%, transparent);
  border-radius: var(--nv-radius-card);
  box-shadow: 0 1px 2px -1px rgba(0, 0, 0, .06), 0 8px 22px -14px rgba(0, 0, 0, .16);
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   16.2 卡片头部：左侧订单号与时间，右侧状态徽章 + 金额
   -------------------------------------------------------------------------- */
.order-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--nv-space-4);
  flex-wrap: wrap;
  padding: var(--nv-space-4) var(--nv-space-5);
  background-color: var(--nv-soft);
  border-bottom: 1px solid var(--nv-border-soft);
}

.order-basic {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}

/* 订单号：等宽 + 可选中（用户要复制它去查单） */
.order-no {
  display: flex;
  align-items: center;
  gap: 4px;
  font-family: var(--nv-mono);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-text-primary);
  user-select: all;
}
.order-no-text { color: var(--nv-text-primary); }

.order-time,
.payment-time { display: flex; align-items: center; gap: 4px; }

/* 支付方式：图标 + 文字 */
.payment-dst {
  display: flex;
  align-items: center;
  gap: 5px;
}
.payment-method {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--nv-text-secondary);
}
.payment-icon {
  width: 16px;
  height: 16px;
  object-fit: contain;
  border-radius: 3px;
}

/* 状态区：徽章 + 右侧金额 */
.order-status {
  display: flex;
  align-items: center;
  gap: var(--nv-space-3);
  flex-wrap: wrap;
  margin-left: auto;
}
.status-badge .fa-duotone,
.status-badge i,
.status-badge .material-icons-outlined { font-size: 13px; }

/* ★ 状态配色用语义色板（三档：已完成 / 待处理 / 已失效），
   这样扫一眼就能区分订单处在哪个阶段。 */

/* 右侧金额 */
.order-right {
  display: flex;
  align-items: center;
  gap: var(--nv-space-4);
}
.order-amount {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}
.amount-label {
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-muted);
}
.amount-value {
  display: inline-flex;
  align-items: baseline;
  gap: 1px;
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.25rem;
  font-weight: var(--nv-weight-bold);
  letter-spacing: -.02em;
  color: var(--nv-price-promo);
}
.amount-value .amount-number { font-size: inherit; }

/* --------------------------------------------------------------------------
   16.3 商品区：缩略图 + 名称 + 规格
   --------------------------------------------------------------------------
   ★ 关键修复：核心给 .goods-thumb > img 没有约束尺寸，
     图片会按原始大小撑满整行（截图里那张大山图）。
     这里把缩略图固定成 72×72 的圆角方块。
   -------------------------------------------------------------------------- */
.goods-section {
  display: flex;
  align-items: flex-start;
  gap: var(--nv-space-4);
  padding: var(--nv-space-4) var(--nv-space-5);
  border-bottom: 1px solid var(--nv-border-soft);
}

.goods-thumb {
  flex: 0 0 72px;
  width: 72px;
  height: 72px;
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-soft);
  overflow: hidden;
}
.goods-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  margin: 0;
}

.goods-details {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-2);
}

.goods-name {
  margin: 0;
  font-size: var(--nv-fs-body);
  font-weight: var(--nv-weight-bold);
  line-height: 1.4;
  color: var(--nv-text-primary);
}

.goods-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* 规格徽章（核心给了 a-badge 系列类，这里统一成 idcub 的小胶囊） */
.goods-sku {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 22px;
  padding-inline: var(--nv-space-2);
  border-radius: var(--nv-radius-chip);
  font-size: var(--nv-fs-micro);
  font-weight: var(--nv-weight-medium);
  white-space: nowrap;
}
.goods-sku.a-badge-primary { background-color: var(--nv-brand-soft); color: var(--nv-brand-ink); }
.goods-sku.a-badge-success { background-color: var(--nv-success-soft); color: var(--nv-success-ink); }
.goods-sku.a-badge-warning { background-color: var(--nv-warning-soft); color: var(--nv-warning-ink); }
.goods-sku.a-badge-danger  { background-color: var(--nv-danger-soft);  color: var(--nv-danger-ink); }

/* --------------------------------------------------------------------------
   16.4 宝贝内容（卡密区）
   -------------------------------------------------------------------------- */
.card-section {
  padding: var(--nv-space-4) var(--nv-space-5) var(--nv-space-5);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--nv-space-3);
  flex-wrap: wrap;
  margin-bottom: var(--nv-space-3);
}

.card-title {
  display: flex;
  align-items: center;
  gap: var(--nv-space-2);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-text-primary);
}
.card-title .fa-duotone,
.card-title .material-icons-outlined { color: var(--nv-brand); }

.shipment-title { display: inline-flex; align-items: center; gap: 4px; }
.shipment-status { font-size: var(--nv-fs-caption); color: var(--nv-text-muted); }

/* 发货状态徽章 */
.shipment-badge {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding-inline: var(--nv-space-2);
  border-radius: var(--nv-radius-pill);
  font-size: var(--nv-fs-micro);
  font-weight: var(--nv-weight-bold);
}
.shipment-badge.shipment-paid {
  background-color: var(--nv-success-soft);
  color: var(--nv-success-ink);
}
.shipment-badge.shipment-waiting {
  background-color: var(--nv-warning-soft);
  color: var(--nv-warning-ink);
}

/* 卡密正文：等宽 + 可整体选中 + 换行 */
.card-display {
  padding: var(--nv-space-4);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-soft);
  border: 1px solid var(--nv-border-soft);
  font-family: var(--nv-mono);
  font-size: var(--nv-fs-caption);
  line-height: 1.8;
  color: var(--nv-text-primary);
  white-space: pre-wrap;
  word-break: break-all;
  user-select: text;
  max-height: 320px;
  overflow: auto;
}

/* 需要查询密码才能看卡密 */
.card-password-section {
  display: flex;
  align-items: center;
  gap: var(--nv-space-2);
  flex-wrap: wrap;
  padding: var(--nv-space-4);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-soft);
  border: 1px dashed var(--nv-border);
}

/* ★ .form-control 是核心输入框类，覆盖成 idcub 的输入外观 */
.card-password-input,
.card-password-section .form-control {
  flex: 1;
  min-width: 180px;
  min-height: 38px;
  padding: var(--nv-space-2) var(--nv-space-3);
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-elevated);
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-primary);
}
.card-password-input:focus { outline: none; border-color: var(--nv-border-brand); box-shadow: var(--nv-ring); }

/* 查看卡密按钮 */
.view-card-btn,
.card-password-section .btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 38px;
  padding-inline: var(--nv-space-4);
  border: 0;
  border-radius: var(--nv-radius-btn);
  background-image: var(--nv-gradient);
  color: #fff;
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  white-space: nowrap;
  cursor: pointer;
  transition: filter var(--nv-dur-fast) var(--nv-ease-out);
}
.view-card-btn:hover,
.card-password-section .btn-primary:hover { filter: brightness(1.06); color: #fff; }

/* 卡密加载态 */
.card-loading {
  display: flex;
  align-items: center;
  gap: var(--nv-space-2);
  padding: var(--nv-space-4);
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}

/* 无密码时直接把卡密展示出来 */
.card-content-no-password { margin-top: var(--nv-space-2); }
.card-content { display: flex; flex-direction: column; gap: var(--nv-space-2); }

/* --------------------------------------------------------------------------
   16.5 查询结果区的加载 / 空态
   -------------------------------------------------------------------------- */
.order-results .nv-section-title {
  font-size: var(--nv-fs-heading);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-ls-heading);
}

/* --------------------------------------------------------------------------
   16.6 窄屏
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .order-header { padding: var(--nv-space-3) var(--nv-space-4); }
  .order-status { width: 100%; margin-left: 0; justify-content: space-between; }
  .goods-section { padding: var(--nv-space-3) var(--nv-space-4); }
  .goods-thumb { flex-basis: 60px; width: 60px; height: 60px; }
  .card-section { padding: var(--nv-space-3) var(--nv-space-4) var(--nv-space-4); }
}

/* ==========================================================================
   首页 · 推荐专区（2026-10-14 新增）
   --------------------------------------------------------------------------
   ★ 形态：横向滚动轨道（overflow-x:auto）
     推荐商品可能有 6-20 个，竖排会把主列表挤到很下面；
     横滑既省纵向空间，又能一屏看到 3-4 个。

   ★ 卡片比主列表的 .acg-card 更"重"（带大图）：
     推荐位本来就是"要吸引点击"的位置，
     与下方紧凑列表形成明确的视觉层次。
   ========================================================================== */
.featured {
  margin-bottom: var(--nv-space-6, 24px);
}

/* 推荐专区的标题行：与下方「热销」区块统一为 .sec-head 的基础视觉，
   只额外处理"标题 + 右侧翻页按钮"的对齐。
   ★ 用 align-items:center 而非 baseline：
     baseline 是给"文字 + 文字"用的；这一行右边是 30px 的圆形按钮，
     按基线对齐会让按钮相对文字下沉，看着像没对齐。 */
.sec-head--featured {
  align-items: center;
  margin-bottom: var(--nv-space-3, 12px);
}
.sec-head--featured h2 {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.sec-head--featured h2 .material-icons-outlined {
  font-size: 20px;
  color: var(--nv-price-promo, #e8590c);
}

/* --------------------------------------------------------------------------
   推荐专区 · 左右滚动按钮
   --------------------------------------------------------------------------
   ★ 为什么需要它：轨道隐藏了原生滚动条（否则下沿会压出一条各浏览器
     粗细不一的灰线），同时卡片是"切边"的 —— 用户看不出右边还有内容。
     按钮既提供操作手段，也是"这里能滚"的视觉提示。

   ★ 布局：靠 margin-left:auto 推到行尾
     （.sec-head 本身是 flex + space-between，但标题是唯一的左项，
       用 auto 更稳，不依赖 justify-content）。
   -------------------------------------------------------------------------- */
.feat-nav {
  margin-left: auto;
  display: inline-flex;
  gap: 6px;
  /* 轨道只有一行内容时不显示（由 JS 判断内容是否溢出后移除 hidden） */
}
.feat-nav[hidden] { display: none; }

.feat-nav__btn {
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--nv-border, #e5e5e7);
  background: var(--nv-elevated, #fff);
  color: var(--nv-text-secondary, #515154);
  cursor: pointer;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out),
              border-color var(--nv-dur-fast) var(--nv-ease-out),
              color var(--nv-dur-fast) var(--nv-ease-out);
}
.feat-nav__btn .material-icons-outlined { font-size: var(--nv-icon-md); }
.feat-nav__btn:hover:not(:disabled) {
  border-color: var(--nv-brand, #2563eb);
  color: var(--nv-brand, #2563eb);
  background: var(--nv-brand-softer, rgba(37, 99, 235, .07));
}
/* 到头 / 到尾 → 置灰。
   ★ 用 disabled 而不是隐藏：按钮位置固定，用户不会因为按钮突然消失
     而找不到方向感（前后箭头永远在原地）。 */
.feat-nav__btn:disabled {
  opacity: .35;
  cursor: default;
}
/* 触摸屏上手指滑动更自然，按钮反而占地方 → 只在有指针设备时显示 */
@media (hover: none) {
  .feat-nav { display: none !important; }
}

/* 横向轨道 */
.featured__track {
  display: flex;
  gap: var(--nv-space-3, 12px);
  overflow-x: auto;
  padding: 4px 2px 6px;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  /* ★ 隐藏原生横向滚动条：它是"系统级"控件，在各浏览器里粗细/圆角/颜色不一致，
     直接露出来会把卡片下沿压出一条突兀的灰线，破坏整块的整洁度。
     轨道本身仍可横向滑动（触控板双指 / 鼠标 Shift+滚轮 / 触摸拖动）。
     下面两条是「隐形但仍可滚」的标准写法：WebKit 走 ::-webkit-scrollbar 宽度归零，
     Firefox 走 scrollbar-width:none。 */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.featured__track::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* 推荐卡片 */
.feat-card {
  flex: 0 0 196px;
  width: 196px;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--nv-border-soft, #f0f0f2);
  border-radius: var(--nv-radius-card, 14px);
  background: var(--nv-elevated, #fff);
  text-decoration: none;
  overflow: hidden;
  scroll-snap-align: start;
  transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.feat-card:hover {
  border-color: var(--nv-border-brand, rgba(37, 99, 235, .45));
  box-shadow: 0 10px 26px -16px rgba(0, 0, 0, .3);
  transform: translateY(-2px);
}

/* 图区外层：作为推荐角标的定位锚点。
   ★ 角标不能放进 .feat-card__thumb —— 那里 hover 时会被 scale(1.04) 放大，
     角标会跟着一起缩放。这一层不参与 transform，所以角标保持稳定。 */
.feat-card__media {
  position: relative;
  display: block;
  overflow: hidden;
}

/* 图：固定 16:9，用背景图（与核心一致的做法，避免 img 被主题样式干扰） */
.feat-card__thumb {
  display: block;
  width: 100%;
  height: 110px;
  background-color: var(--nv-soft, #f5f5f7);
  background-size: cover;
  background-position: center;
  transition: transform .3s var(--nv-ease-out, ease);
}
.feat-card:hover .feat-card__thumb { transform: scale(1.04); }

/* 推荐角标：与商品横幅的 .bar-flag 同款，保证「推荐」全站一种表达。
   浅金底 + 深棕金文字（--nv-gold-soft / --nv-gold-ink）——
   直接用饱和金 + 白字对比度只有 2.1:1，远不够 AA。 */
.feat-card__flag {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  height: 21px;
  padding: 0 8px;
  border-radius: var(--nv-radius-pill, 999px);
  background: rgba(255, 255, 255, .92);
  color: var(--nv-gold-ink, #7a4a00);
  box-shadow: inset 0 0 0 1px var(--nv-gold-border, rgba(180, 83, 9, .34)),
              0 2px 6px -2px rgba(0, 0, 0, .28);
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  pointer-events: none;
  backdrop-filter: blur(6px);
}
[data-theme="dark"] .feat-card__flag {
  background: rgba(28, 28, 30, .88);
  color: var(--nv-gold-ink, #fcd34d);
}
/* 售罄卡：角标压暗，不给"可买"的暗示 */
.feat-card.is-out .feat-card__flag { filter: grayscale(.55); opacity: .8; }

.feat-card__body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 10px 12px 11px;
  min-width: 0;
}

.feat-card__name {
  font-size: 13px;
  font-weight: var(--nv-weight-medium, 500);
  line-height: 1.4;
  color: var(--nv-text-primary, #1d1d1f);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* ★★ 固定为 2 行高度（2026-10-17 补）
     -webkit-line-clamp 只限制"最多 2 行"，单行标题仍只占 1 行高 ——
     于是同一排里有的标题 18px、有的 35px，
     下方的分类胶囊与价格行因此【参差不齐】，横向滚动时尤其明显。
     → min-height 锁定 2 行（13px × 1.4 × 2 ≈ 36.4px），
       单行标题下方留白、两行标题正好占满，价格行始终对齐。
     ★ 用 em 而非 px：字号若有变体（如窄屏 12.5px），高度会跟着缩放。 */
  min-height: 2.8em;
  transition: color .15s ease;
}
.feat-card:hover .feat-card__name { color: var(--nv-brand, #2563eb); }

.feat-card__cat {
  align-self: flex-start;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--nv-soft, #f2f2f4);
  font-size: 11px;
  color: var(--nv-text-muted, #86868b);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.feat-card__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  margin-top: 2px;
}

.feat-card__price {
  font-family: var(--nv-mono, ui-monospace, monospace);
  font-variant-numeric: tabular-nums;
  font-size: 17px;
  font-weight: var(--nv-weight-bold, 700);
  letter-spacing: -.02em;
  color: var(--nv-price-promo, #e8590c);
  white-space: nowrap;
}
.feat-card__price > i {
  font-style: normal;
  font-size: 11px;
  font-weight: var(--nv-weight-bold, 700);
  margin-right: 1px;
}

.feat-card__stock {
  font-size: 11px;
  color: var(--nv-text-faint, #a1a1a6);
  white-space: nowrap;
}

/* 售罄态：整卡降透明度 */
.feat-card.is-out { opacity: .55; }
.feat-card.is-out:hover { transform: none; }
.feat-card.is-out .feat-card__stock { color: var(--nv-danger-ink, #c9342f); }

/* 窄屏：卡片小一点，一屏能露 2.5 个（暗示可横滑） */
@media (max-width: 640px) {
  .feat-card { flex-basis: 156px; width: 156px; }
  .feat-card__thumb { height: 88px; }
  .feat-card__name { font-size: 12.5px; }
  .feat-card__price { font-size: 15px; }
}
/* ══════════════════════════════════════════════════════════════════════════
   18. 全局提示（toastr）· 对齐 Nuxt Toast 规范
   --------------------------------------------------------------------------
   ★ 为什么需要覆盖：
     toastr 是 vendor 库（assets/common/css/toastr.min.css），自带一套
     2012 年的皮肤：圆角 3px、内边距左侧 50px（留给 base64 PNG 图标）、
     硬编码红 #bd362f、阴影 rgb(153,153,153)。与本主题的设计语言完全不同。

   ★ 为什么不去改 vendor 文件：
     它是第三方依赖，改动会在升级时丢失，也污染上游代码。
     正确做法是在主题层【更高特异度覆盖】—— 这也是我们一贯的边界纪律。

   ★ 对齐目标（Nuxt 4.3 Toast.vue）：
     · 圆角 rounded-xl（12px）     · 内边距 px-5 py-4（20/16px）
     · 最小宽 280px                · 柔和深阴影（shadow-2xl）
     · 位置 top-4 right-4（16px）  · 窄屏移到底部（bottom-20）
     · 颜色用语义令牌而非硬编码
   ══════════════════════════════════════════════════════════════════════════ */

/* 位置：12px → 16px，与 Nuxt 的 top-4 right-4 一致 */
#toast-container.toast-top-right {
  top: 16px;
  right: 16px;
}

#toast-container > div {
  /* 去掉 toastr 的 base64 图标与左侧 50px 留白，改为右侧图标方案 */
  background-image: none !important;
  padding: 14px 16px !important;
  min-width: 280px;
  max-width: 360px;
  border-radius: var(--nv-radius-lg) !important;
  border: .5px solid var(--nv-border-soft);
  box-shadow: 0 18px 38px -24px rgba(0, 0, 0, .30),
              0 10px 24px -18px rgba(0, 0, 0, .26) !important;
  font-size: var(--nv-fs-caption);
  line-height: 1.55;
  color: var(--nv-text-primary);
  opacity: .98;
}

/* 语义色：用令牌，不用 toastr 的硬编码色。
   ★ 用 -soft 浅底 + -ink 深字，而不是实心饱和色 ——
     提示是"告知"不是"行动点"，实心红会抢走主 CTA 的注意力。 */
#toast-container > .toast-error {
  background-color: var(--nv-danger-soft) !important;
  color: var(--nv-danger-ink) !important;
}
#toast-container > .toast-success {
  background-color: var(--nv-success-soft) !important;
  color: var(--nv-success-ink) !important;
}
#toast-container > .toast-warning {
  background-color: var(--nv-warning-soft) !important;
  color: var(--nv-warning-ink) !important;
}
#toast-container > .toast-info {
  background-color: var(--nv-info-soft) !important;
  color: var(--nv-info-ink) !important;
}

/* 进度条：跟随语义色 */
#toast-container > div:hover {
  box-shadow: 0 22px 44px -26px rgba(0, 0, 0, .34),
              0 12px 28px -20px rgba(0, 0, 0, .28) !important;
}

/* 关闭按钮：改为低调的当前色，hover 才加重 */
#toast-container > div .toast-close-button {
  color: currentColor;
  opacity: .45;
  font-size: 18px;
  text-shadow: none;
}
#toast-container > div .toast-close-button:hover { opacity: .85; }

/* 窄屏：移到底部居中，与拇指操作区一致（对齐 Nuxt 的 max-sm:bottom-20） */
@media (max-width: 640px) {
  #toast-container.toast-top-right {
    top: auto;
    right: 12px;
    left: 12px;
    bottom: 80px;
  }
  #toast-container > div { min-width: 0; max-width: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   16. 商品内页 · 重设计（2026-10-15）
   --------------------------------------------------------------------------
   分栏结构：左 = 主图卡（图 + 服务承诺），右 = 信息卡 + 下单卡，
   详情改为「一张卡片 + 浅灰表头」的 Tabs。
   配色全部沿用既有令牌，与页头/页脚同源。

   ★★ 两条硬约束（改样式时务必遵守）：
     1. 核心 item.js 会 `$('.abacus .price').html(...)` 整体重写价格节点，
        所以「单位」等附加文案必须放在 .price【之外】，否则会被冲掉。
     2. 下面所有新类名都带 `item-` / `nv-pair` 前缀，**不与核心契约类冲突**；
        .vstack / .abacus / .switch-race / .cash-pay / .pay-list /
        .captcha-img / .captcha-input / .nv-qty__btn 等一律保持原样。
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 两栏骨架 ──────────────────────────────────────────────────────────
   ★ align-items:stretch + 左栏卡 flex:1 → 左右【等高】。
     但选择器必须排除右栏容器（:not(.item-split__side)），
     否则右栏的两张卡也会被 flex:1 拉伸，内容浮顶、下方留白（踩过）。 */
.item-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--nv-space-6);
  align-items: stretch;
}
@media (min-width: 1000px) {
  /* 左右约 47 : 53 —— 右栏表单字段多，给它略宽的一侧 */
  .item-split { grid-template-columns: minmax(0, .94fr) minmax(0, 1.06fr); gap: var(--nv-space-6); }
}

/* ── 左栏：主图 ────────────────────────────────────────────────────────
   ★ 图区 flex:1 吃掉卡片剩余高度，让「承诺条」自然贴到卡片底部 ——
     这样左右两栏底边对齐，不会在左下方留一块空白。
   ★ object-fit:cover：高度被拉长时是【上下裁切】而非【压扁】，
     画面主体不变形。

   ★★ 关键：整条 flex 链都要能"收缩"。
     卡片的 flex 容器若是默认 `min-height:auto`，内容（大图）会把卡片顶大，
     图就溢出到承诺条之上（实测：卡片 704px、图链 628px，承诺条被压在图上）。
     → 必须逐层 `min-height:0`：列 → 卡片 → gallery → 图容器。
       最后在 .nv-gallery__main 上给 min-height 兜底，保证图不会缩到看不见。 */
.item-split__media { display: flex; flex-direction: column; min-height: 0; }
.item-card {
  background-color: var(--nv-elevated);
  border: 1px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-card);
  box-shadow: var(--nv-shadow-2);
  overflow: hidden;   /* ★ 圆角由它统一裁切，子块不再自己带角 */
}
.item-split__media > .item-card { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.item-split__side { display: flex; flex-direction: column; gap: var(--nv-space-4); }
/* ★ .nv-gallery / .nv-gallery__main 的基础规则已在 §6.1 定义（含 min-height:0
   的 flex 链说明），这里不再重复，避免两处规则打架。

   ★★ 2026-10-16 用户要求移除主图的 hover 效果：
      「左侧主图，不要鼠标放上去闪动的效果」——原规则是
          .item-card--media:hover .nv-gallery__main img { transform: scale(1.03); }
      已删除。理由：主图是纯展示内容、不是可点入口，
      放大动效会让用户以为「这张图能点开」，属于误导性反馈。
      注意挂点是 .item-card--media（整张卡）而不是图片本身，
      所以鼠标划过下方服务承诺条也会触发缩放，更容易误触。 */

/* 图上的浮层：推荐角标（左上）与库存（左下） */
.item-flag {
  position: absolute;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 28px;
  padding: 0 12px;
  border-radius: var(--nv-radius-pill);
  font-size: var(--nv-fs-caption);
  z-index: 2;
  pointer-events: none;
}
.item-flag {
  top: 14px; left: 14px;
  background-image: var(--nv-gradient);
  color: #fff;
  font-weight: 600;
  box-shadow: var(--nv-shadow-brand);
}
.item-flag--stock {
  top: auto; left: 14px; bottom: 14px;
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(10px);
  color: var(--nv-success-ink);
  font-weight: 500;
}
.item-flag--stock .material-icons-outlined { font-size: 15px; }
[data-theme="dark"] .item-flag--stock { background: rgba(28, 28, 30, .9); }

/* 服务承诺：图卡底部的三段横条（与图共用一张卡） */
.item-assure {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background-color: var(--nv-border-soft);
  border-top: 1px solid var(--nv-border-soft);
  flex: 0 0 auto;
}
.item-assure > div {
  background-color: var(--nv-elevated);
  padding: 15px 8px;
  text-align: center;
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-secondary);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.item-assure .material-icons-outlined { font-size: 19px; color: var(--nv-success); }

/* ── 右栏 · 信息卡 ─────────────────────────────────────────────────── */
.item-head { padding: var(--nv-space-6) var(--nv-space-6) var(--nv-space-5); }
.item-head__title {
  font-size: 22px;
  line-height: 1.32;
  letter-spacing: var(--nv-ls-title);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-text-primary);
  margin: 0;
}
.item-head__tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  margin-top: var(--nv-space-3);
}
.item-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 24px;
  padding: 0 9px;
  border: 0;
  border-radius: var(--nv-radius-chip);
  background-color: var(--nv-soft);
  color: var(--nv-text-secondary);
  font-size: var(--nv-fs-micro);
  font-family: inherit;
  font-weight: var(--nv-weight-medium);
  white-space: nowrap;
  cursor: default;
}
span.item-tag { cursor: default; }
.item-tag--ship { background-color: var(--nv-success-soft); color: var(--nv-success-ink); }
.item-tag--danger { background-color: var(--nv-danger-soft); color: var(--nv-danger-ink); }
.item-tag .material-icons-outlined { font-size: 14px; }
/* 分享按钮可点 */
button.item-tag.shared-button { cursor: pointer; transition: background-color var(--nv-dur-fast) var(--nv-ease-out); }
button.item-tag.shared-button:hover { background-color: var(--nv-brand-soft); color: var(--nv-brand-ink); }

/* 价格区
   ★ 去掉原先的彩色渐变面板 —— 价格本身够醒目，再套一层边框是过度装饰。
     只留一条极淡的分隔线把价格与上方标题轻轻分开。 */
.item-price {
  margin-top: var(--nv-space-4);
  padding-top: var(--nv-space-4);
  border-top: 1px solid var(--nv-border-soft);
  display: flex;
  align-items: baseline;
  gap: 5px;
  flex-wrap: wrap;
}
.item-price__abacus { display: inline-flex; align-items: baseline; }
/* 核心契约：.abacus .price 是算价后会被整体重写的节点 */
.abacus .price,
.item-price .price {
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  font-size: 42px;
  font-weight: 800;
  letter-spacing: -.034em;
  line-height: 1;
  color: var(--nv-price-promo);
}
.item-price .price .unit { font-size: 20px; font-weight: 600; }
/* 计价单位：取自当前选中的规格名（由 theme.js 的 initItemPriceUnit 控制显隐） */
.item-price__unit {
  font-size: var(--nv-fs-body);
  color: var(--nv-text-muted);
  font-weight: var(--nv-weight-medium);
  margin-left: 2px;
}
.item-price__unit > span { display: none; }
.item-price__unit > span.is-on { display: inline; }
/* 会员价/原价副行：仅当存在会员价时渲染 */
.item-price__sub {
  display: flex;
  gap: var(--nv-space-4);
  margin-top: 10px;
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-muted);
}
.item-price__sub b { color: var(--nv-success-ink); font-weight: var(--nv-weight-bold); }
.item-price__sub s { color: var(--nv-text-faint); }

/* ── 右栏 · 下单卡 ─────────────────────────────────────────────────── */
.item-form { padding: var(--nv-space-5) var(--nv-space-6) var(--nv-space-6); }

/* 字段标签统一加重一档，让表单区有"可操作性" */
.item-form .nv-label {
  display: block;
  margin-bottom: 8px;
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-text-secondary);
}
.item-form .nv-block { margin-bottom: var(--nv-space-4); }
.nv-req { font-style: normal; color: var(--nv-danger-ink); }
.nv-hint {
  margin-top: 6px;
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-faint);
}

/* 商品页 .nv-hint 里的行内链接（手机 / QQ 商品的「卡密请到订单查询页查看」）。
   刻意做成行内而不是块级按钮：它是一句提示的收尾，不该在表单里多占一行高度。
   字号继承 .nv-hint（micro）。

   ★★ 2026-10-16 用户反馈「当前有下划线 不好看」——去掉常驻下划线。
     改成：常态靠【品牌色 + 中等字重】表达"这是可点的"，
     下划线只在 hover 时出现（表示"你正指到它"）。
     理由：11px 的小字上，下划线会和文字笔画挤在一起、
     把一行提示割成碎块；而品牌蓝本身已经和周围灰色提示形成足够对比，
     不需要再来一条线重复声明"我是链接"。 */
.nv-hint__link {
  color: var(--nv-brand-ink);
  font-weight: var(--nv-weight-medium);
  text-decoration: none;
  transition: color var(--nv-dur-fast) var(--nv-ease-out);
}
.nv-hint__link:hover,
.nv-hint__link:focus-visible {
  color: var(--nv-brand);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* 规格按钮：选中态加品牌色描边 + 浅底 + 外发光 */
.item-form .nv-race {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 64px;
  height: 40px;
  padding: 0 15px;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  background-color: transparent;
  color: var(--nv-text-secondary);
  font-size: 13.5px;
  transition: border-color var(--nv-dur-fast) var(--nv-ease-out),
              background-color var(--nv-dur-fast) var(--nv-ease-out),
              color var(--nv-dur-fast) var(--nv-ease-out),
              box-shadow var(--nv-dur-fast) var(--nv-ease-out);
}
.item-form .nv-race:hover {
  border-color: var(--nv-border-brand);
  color: var(--nv-brand-ink);
}
/* ★ 核心给的是 .is-primary（不是 .is-active），选中态必须跟它走 */
.item-form .nv-race.is-primary {
  border-color: var(--nv-brand);
  background-color: var(--nv-brand-softer);
  color: var(--nv-brand-ink);
  font-weight: var(--nv-weight-medium);
  box-shadow: 0 0 0 3px var(--nv-brand-softer);
}
.item-form .nv-races { display: flex; flex-wrap: wrap; gap: 9px; }

/* 输入框 */
.item-form .nv-input {
  width: 100%;
  height: 42px;
  padding: 0 13px;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  background-color: transparent;
  color: var(--nv-text-primary);
  font-size: 13.5px;
  font-family: inherit;
  transition: border-color var(--nv-dur-fast) var(--nv-ease-out),
              box-shadow var(--nv-dur-fast) var(--nv-ease-out);
}
.item-form .nv-input::placeholder { color: var(--nv-text-faint); }
.item-form .nv-input:focus {
  outline: none;
  border-color: var(--nv-border-brand);
  box-shadow: 0 0 0 3px var(--nv-brand-softer);
}

/* 数量 + 人机验证并排一行（压缩纵向高度）
   ★ 数量列 auto（步进器固定宽），验证码列 1fr（要吃满剩余放 8 位输入 + 图）。
   ★ 窄屏 <560px 退回单列 —— 硬撑两列会把验证码图压扁。 */
.nv-pair {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--nv-space-3);
  align-items: start;
}
.nv-pair > .nv-block { margin-bottom: 0; }
@media (max-width: 560px) {
  .nv-pair { grid-template-columns: minmax(0, 1fr); }
  .nv-pair > .nv-block { margin-bottom: var(--nv-space-4); }
}

/* 步进器（沿用核心 .nv-qty 结构，只调尺寸与配色） */
.item-form .nv-qty {
  display: inline-flex;
  align-items: center;
  height: 42px;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  overflow: hidden;
}
.item-form .nv-qty__btn {
  width: 38px;
  height: 40px;
  border: 0;
  background-color: transparent;
  color: var(--nv-text-secondary);
  font-size: 15px;
  cursor: pointer;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.item-form .nv-qty__btn:hover:not(:disabled) { background-color: var(--nv-soft); color: var(--nv-text-primary); }
.item-form .nv-qty__btn:disabled { opacity: .35; cursor: not-allowed; }
.item-form .nv-qty__input {
  width: 56px;
  height: 40px;
  border: 0;
  border-left: 1px solid var(--nv-border-soft);
  border-right: 1px solid var(--nv-border-soft);
  background-color: transparent;
  color: var(--nv-text-primary);
  font-size: 14px;
  font-family: inherit;
  text-align: center;
}
.item-form .nv-qty__input:focus { outline: none; background-color: var(--nv-brand-softer); }

/* 验证码图：给个浅底，否则在某些背景下看不出边界 */
.item-form .captcha-img {
  height: 42px;
  flex: 0 0 auto;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-soft);
  cursor: pointer;
}
.item-form .captcha-input { min-width: 0; }

/* ── 已登录 · 卡密去向提示 ──────────────────────────────────────────
   ★ 为什么需要它：已登录用户的 order.contact 会被服务端替换成 16 位随机串
     （Order.php:861-863），所以模板在已登录时【不渲染】联系方式输入框 ——
     让用户填一个必然被丢弃的字段是欺骗。
   ★ 但「卡密去哪拿」是下单前最要紧的疑问。与其让他付款后去猜，
     不如就在他原本以为要填邮箱的那个位置直接答掉，并给一条直达出路。
   ★ 视觉定位：这是「告知 + 出路」，不是警告也不是错误 ——
     所以用最轻的品牌浅底 + 中性描边，不用黄 / 红。
     重量必须低于上方要用户操作的控件（规格 / 优惠券），否则会抢注意力。 */
.item-login-hint {
  display: flex;
  gap: var(--nv-space-3);
  padding: var(--nv-space-3);
  border: 1px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-brand-softer);
}
.item-login-hint__ico {
  flex: 0 0 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--nv-radius-chip);
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
}
.item-login-hint__ico .material-icons-outlined { font-size: 17px; }
.item-login-hint__body { display: block; min-width: 0; }
.item-login-hint__title {
  display: block;
  font-size: 13px;
  font-weight: var(--nv-weight-medium);
  line-height: 1.4;
  color: var(--nv-text-primary);
}
.item-login-hint__text {
  display: block;
  margin-top: 3px;
  font-size: var(--nv-fs-micro);
  line-height: 1.55;
  color: var(--nv-text-muted);
}
.item-login-hint__link {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-top: var(--nv-space-2);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-brand-ink);
  text-decoration: none;
}
.item-login-hint__link .material-icons-outlined { font-size: 14px; }
.item-login-hint__link:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ── 批发价阶梯表（核心生成，主题重塑） ──────────────────────────────
   ★ 这张表是【核心 JS 生成】的，DOM 与类名固定：
       <table class="table wholesale-table mt-1 mb-0">…
     由 item.js 的 _SetWholesaleMsg() 插到 .qty-group 之后。
     .table 是 Bootstrap 的表格类 —— 不接管的话会渲染成灰底 + 1px 全边框 +
     斑马纹，与主题「只留横线、极淡」的表格语言完全不是一路。
   ★ 只做减法：去掉 Bootstrap 的框、斑马纹与竖线，只留最淡的横向分隔线；
     单价列右对齐 + 等宽 tabular-nums，让阶梯价能沿小数点对齐扫读。 */
.item-form .wholesale-table {
  width: 100%;
  margin-top: var(--nv-space-2);
  border-collapse: collapse;
  font-size: var(--nv-fs-caption);
  color: var(--nv-text-secondary);
}
.item-form .wholesale-table thead th {
  padding: 6px 0;
  border: 0;
  border-bottom: 1px solid var(--nv-border-soft);
  background-color: transparent;
  font-size: var(--nv-fs-micro);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-text-muted);
  text-align: left;
}
.item-form .wholesale-table tbody td {
  padding: 5px 0;
  border: 0;
  border-bottom: 1px solid var(--nv-border-soft);
  background-color: transparent;
}
.item-form .wholesale-table tbody tr:last-child td { border-bottom: 0; }
.item-form .wholesale-table tbody td:last-child,
.item-form .wholesale-table thead th:last-child {
  text-align: right;
  font-family: var(--nv-mono);
  font-variant-numeric: tabular-nums;
  color: var(--nv-text-primary);
}
.item-form .wholesale-table thead th:last-child { color: var(--nv-text-muted); }

/* 支付方式：JS 填充 .pay-list。图标按渠道类型上色，底走中性 */
.item-form .pay-list--grid { display: flex; flex-wrap: wrap; gap: 8px; }
.item-form .pay-list .pay {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  padding: 0 12px;
  border: 1px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-pill);
  background-color: var(--nv-soft);
  color: var(--nv-text-secondary);
  font-size: var(--nv-fs-micro);
  cursor: pointer;
  transition: border-color var(--nv-dur-fast) var(--nv-ease-out),
              background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.item-form .pay-list .pay:hover { border-color: var(--nv-border-brand); background-color: var(--nv-brand-softer); }
.item-form .pay-list .pay.is-active { border-color: var(--nv-brand); background-color: var(--nv-brand-soft); color: var(--nv-brand-ink); }

/* ── 详情 Tabs：整块做成一张卡片 + 浅灰表头 ────────────────────────────
   ★ 表头与内容区共用同一外框，才有"这是一组 Tab"的整体感。
     若把 tabbar 只写成一条下边框、tabbody 再单独套个盒子，
     两者边框对不上，看起来像"一排字浮在一个方框上方"。 */
.item-tabs {
  margin-top: var(--nv-space-8);
  background-color: var(--nv-elevated);
  border: 1px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-card);
  box-shadow: var(--nv-shadow-2);
  overflow: hidden;
}
/* ── 详情 Tabs ────────────────────────────────────────────────────────
   视觉：回到「灰色长条 + 选中下划线」的形态（用户否掉了胶囊按钮版）。
   ★ 但切换反馈做明显：下划线用品牌渐变、加高到 3px，
     并配 250ms 缓动 —— 让它"滑"过去而不是硬切。
     纯 CSS transition 做不到真正的位置滑动（伪元素无法跨元素过渡），
     所以改用 JS 给下划线一个 translateX 过渡，见 theme.js::initItemTabsScroll。
   ★ 保留 .item-tabbar / .item-tab / .is-active（theme.js 依赖），只换视觉。 */
.item-tabbar {
  position: relative;
  display: flex;
  gap: 2px;
  padding: 0 10px;
  background-color: var(--nv-soft);
  border-bottom: 1px solid var(--nv-border-soft);
}
.item-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 52px;
  padding: 0 22px;
  border: 0;
  background-color: transparent;
  color: var(--nv-text-muted);
  font-size: 14px;
  font-family: inherit;
  font-weight: var(--nv-weight-medium);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--nv-dur-fast) var(--nv-ease-out),
              background-color var(--nv-dur-fast) var(--nv-ease-out);
}
.item-tab .material-icons-outlined {
  font-size: 18px;
  color: var(--nv-text-faint);
  transition: color var(--nv-dur-fast) var(--nv-ease-out);
}
/* hover：极淡的品牌底 + 图标先亮起来，预告"可以点" */
.item-tab:hover { color: var(--nv-text-secondary); }
.item-tab:hover .material-icons-outlined { color: var(--nv-brand); }

/* 选中：文字加重、图标用品牌色 —— 与未选中拉开明确层级 */
.item-tab.is-active { color: var(--nv-text-primary); font-weight: var(--nv-weight-bold); }
.item-tab.is-active .material-icons-outlined { color: var(--nv-brand); }

/* 滑动下划线：整条由 JS 定位的单一元素承载，跨 tab 平滑过渡。
   ★ 不用 .item-tab.is-active::after —— 伪元素跟着按钮走，
     切换时是"消失+出现"，做不出滑动感。 */
.item-tabbar__ink {
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 3px;
  border-radius: 3px 3px 0 0;
  background-image: var(--nv-gradient);
  box-shadow: 0 2px 8px -2px var(--nv-border-brand);
  pointer-events: none;
  /* 位置/宽度由 JS 写 style；过渡只作用于这两个属性，
     避免首次定位时也动起来（首帧 JS 会先禁用过渡） */
  transition: transform .25s var(--nv-ease-out),
              width .25s var(--nv-ease-out);
}
.item-tabbody { padding: var(--nv-space-6) var(--nv-space-8); }
.item-tabbody[hidden] { display: none; }

/* 购买须知 / 售后服务：由主题配置的多行文本渲染成【带图标的列表】
   ------------------------------------------------------------------
   ★ 模板用 `explode("\n", $setting.xxx)` 逐行拆开，每行一个 <li>。
     （不能用 `|split` 修饰符 —— 实测本项目 Smarty 没有注册它，
       报 "unknown modifier 'split'"。`explode()` 作为函数可用，
       且能直接放进 foreach，见模板注释。）
   ★ 图标逐行渲染，因为每行内容不同，无法用 CSS 统一加。
     · 购买须知 → check_circle（"请确认/请注意"的语义）
     · 售后服务 → support_agent（客服语义）
   ------------------------------------------------------------------ */
.item-lines {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.item-lines > li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: 14px;
  line-height: 1.75;
  color: var(--nv-text-secondary);
}
.item-lines > li > .material-icons-outlined {
  flex: 0 0 auto;
  font-size: 17px;
  margin-top: 1px;
  color: var(--nv-brand);
}
/* 售后服务那条用成功色，与"须知"的提示蓝区分开 */
#tab-service .item-lines > li > .material-icons-outlined { color: var(--nv-success); }

/* ── 窄屏：单列堆叠 ───────────────────────────────────────────────── */
@media (max-width: 999px) {
  .item-split { grid-template-columns: minmax(0, 1fr); }
  .nv-gallery__main { min-height: 260px; }
  .item-head__title { font-size: 20px; }
  .abacus .price, .item-price .price { font-size: 36px; }
  .item-tabbody { padding: var(--nv-space-5) var(--nv-space-5); }
  .item-tab { padding: 0 14px; font-size: 13.5px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   下单确认弹窗 + 主 CTA
   ══════════════════════════════════════════════════════════════════════════
   ★ 从 demo（.workbuddy/demo-item-3ways.html）迁移。
     交互链路：填邮箱 → 点「立即购买」→ 弹窗确认邮箱 + 选付款方式 → 去支付。

   ★★ 为什么付款方式要在弹窗里选（而不是像原来那样点一下就直接下单）：
      原来的流程里，付款方式是一排小胶囊，**点哪个就直接下单了** ——
      顾客没有"回头看一眼邮箱填对没"的机会，邮箱填错只能等卡密发丢。
      加一层确认，把「邮箱」和「付款方式」放在同一屏做最后复核，
      而且邮箱回显出来看，比让顾客自己回想可靠得多。
   ────────────────────────────────────────────────────────────────────── */

/* 表单内这份 .pay-list 只作数据源（theme.js 从中读取支付渠道），不再展示。
   ★ 用 !important 是因为核心 item.js 会给它 fadeIn/fadeOut（写内联 style），
     不加则会在算价成功后又被显示出来。 */
.cash-pay .pay-list {
  display: none !important;
}

/* 下单入口条：只负责「按钮与上方内容的间距」。
   ★ 刻意不做盒子（无底、无描边、无内边距）——
     付款方式已移到弹窗里选，这里只剩一个按钮，
     再套一层灰底框就是「空盒子装一个按钮」，是纯噪音。
   ★★ 名字不能叫 .nv-buybar —— 那个已在上面 929 行被
      「购买栏（sticky 玻璃条）」占用，同名会让这套 sticky +
      玻璃底 + 描边 + 阴影全部叠到按钮上。
      （本文件此前已因 .nv-block 同名踩过一次，别再犯。） */
.nv-buy-cta {
  margin-top: var(--nv-space-6);
}

/* ── 主 CTA：满宽蓝色渐变 ── */
.nv-buy {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  height: 50px;
  /* 上边距为 0：容器里 .pay-list 是隐藏的，按钮上方没有可见内容，
     留白交给容器 .nv-buy-cta 统一控制。 */
  border: 0;
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-brand-solid);
  color: #fff;
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--nv-shadow-brand);
  transition:
    filter var(--nv-dur-fast) var(--nv-ease-out),
    transform var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-buy:hover {
  filter: brightness(1.06);
}

.nv-buy:active {
  transform: scale(.985);
}

.nv-buy .material-icons-outlined {
  font-size: 20px;
}

/* ── 邮箱未填 / 格式错：错误态 ──
   ★ 复用主题已有的 .is-invalid 约定（原本只有 .nv-textarea 有样式，
     .nv-input 一直缺 —— 所以旧版的邮箱校验其实只弹提示、输入框不变色）。
     这里把 .nv-input 这一半补上，两处共用同一套视觉语言。
   ★ 选择器写两次类名是故意的：.nv-input:focus 的特异性与 .nv-input.is-invalid
     相同，而 focus 规则在文件里更靠后 → 同分时后者胜 → 红色描边会被
     聚焦时的蓝色描边盖掉。而点「立即购买」时恰好会 focus()，冲突必现。
     提到 0,3,0 才能压过 focus，不必动用 !important。 */
.nv-input.is-invalid.is-invalid {
  border-color: var(--nv-danger);
  background-color: var(--nv-danger-soft);
}

.nv-input.is-invalid.is-invalid:focus {
  border-color: var(--nv-danger);
  box-shadow: 0 0 0 3px var(--nv-danger-soft);
}

/* 邮箱框抖动：连续点击也要能重放，所以由 JS 先摘类再触发回流 */
@keyframes nv-shake {
  0%, 100% { transform: translateX(0); }
  18% { transform: translateX(-5px); }
  36% { transform: translateX(5px); }
  54% { transform: translateX(-3px); }
  72% { transform: translateX(3px); }
  88% { transform: translateX(-1px); }
}

.nv-shake {
  animation: nv-shake .42s var(--nv-ease-out);
}

@media (prefers-reduced-motion: reduce) {
  .nv-shake { animation: none; }
}

/* ── 弹窗 ── */
.nv-confirm {
  position: fixed;
  inset: 0;
  z-index: 1080;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--nv-space-4);
  background-color: rgba(0, 0, 0, .42);
  -webkit-backdrop-filter: blur(8px) saturate(150%);
  backdrop-filter: blur(8px) saturate(150%);
  opacity: 0;
  transition: opacity var(--nv-dur-normal) var(--nv-ease-out);
}

.nv-confirm.is-open {
  display: flex;
  opacity: 1;
}

.nv-confirm__panel {
  width: 100%;
  max-width: 432px;
  max-height: calc(100vh - var(--nv-space-8));
  overflow-y: auto;
  background-color: var(--nv-elevated);
  border-radius: var(--nv-radius-card);
  box-shadow: 0 40px 90px -28px rgba(0, 0, 0, .5);
  transform: translateY(14px) scale(.985);
  opacity: .6;
  transition:
    transform var(--nv-dur-normal) var(--nv-ease-out),
    opacity var(--nv-dur-normal) var(--nv-ease-out);
}

.nv-confirm.is-in .nv-confirm__panel {
  transform: none;
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .nv-confirm__panel { transition: none; transform: none; opacity: 1; }
}

/* 头部 */
.nv-confirm__head {
  display: flex;
  align-items: flex-start;
  gap: var(--nv-space-3);
  padding: var(--nv-space-5) var(--nv-space-5) 0;
}

.nv-confirm__ico {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
  border-radius: 11px;
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
}

.nv-confirm__ico .material-icons-outlined { font-size: 21px; }

.nv-confirm__titles { min-width: 0; flex: 1; }

.nv-confirm__title {
  margin: 0;
  font-size: var(--nv-fs-body);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-ls-heading);
  color: var(--nv-text-primary);
}

.nv-confirm__sub {
  margin: 3px 0 0;
  font-size: var(--nv-fs-caption);
  line-height: 1.5;
  color: var(--nv-text-muted);
}

.nv-confirm__x {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 10px;
  background-color: transparent;
  color: var(--nv-text-faint);
  cursor: pointer;
  transition:
    background-color var(--nv-dur-fast) var(--nv-ease-out),
    color var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-confirm__x:hover {
  background-color: var(--nv-soft);
  color: var(--nv-text-primary);
}

.nv-confirm__x .material-icons-outlined { font-size: 19px; }

.nv-confirm__body { padding: var(--nv-space-4) var(--nv-space-5) 0; }

/* 邮箱回显 */
.nv-confirm__mail {
  display: flex;
  align-items: center;
  gap: var(--nv-space-3);
  padding: var(--nv-space-3) var(--nv-space-4);
  border: 1px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-soft);
}

.nv-confirm__mail-ico {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 9px;
  background-color: var(--nv-elevated);
  color: var(--nv-brand-ink);
}

.nv-confirm__mail-ico .material-icons-outlined { font-size: 19px; }

.nv-confirm__mail-txt { min-width: 0; flex: 1; }

.nv-confirm__mail-lbl {
  margin: 0 0 2px;
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-muted);
}

.nv-confirm__mail-val {
  margin: 0;
  font-size: var(--nv-fs-body);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nv-confirm__edit {
  flex: 0 0 auto;
  height: 30px;
  padding: 0 12px;
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-pill);
  background-color: transparent;
  color: var(--nv-text-secondary);
  font-family: inherit;
  font-size: var(--nv-fs-micro);
  cursor: pointer;
  transition:
    border-color var(--nv-dur-fast) var(--nv-ease-out),
    color var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-confirm__edit:hover {
  border-color: var(--nv-border-strong);
  color: var(--nv-text-primary);
}

/* 付款方式 */
.nv-confirm__pay { margin-top: var(--nv-space-5); }

.nv-confirm__hint {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 var(--nv-space-3);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-text-secondary);
}

.nv-confirm__hint .material-icons-outlined {
  font-size: 17px;
  color: var(--nv-text-muted);
}

.nv-confirm__list {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-2);
}

/* 每个支付渠道 = 一张可选卡片（而不是一排小胶囊）。
   ★ 卡片能承载图标 + 名称 + 说明三段信息，胶囊只能塞两个字。 */
.nv-confirm__item {
  display: flex;
  align-items: center;
  gap: var(--nv-space-3);
  width: 100%;
  padding: 13px 15px;
  border: 1.5px solid var(--nv-border-soft);
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-elevated);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition:
    border-color var(--nv-dur-fast) var(--nv-ease-out),
    background-color var(--nv-dur-fast) var(--nv-ease-out),
    box-shadow var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-confirm__item:hover {
  border-color: var(--nv-border-brand);
  background-color: var(--nv-brand-softer);
}

.nv-confirm__item.is-on {
  border-color: var(--nv-brand);
  background-color: var(--nv-brand-softer);
  box-shadow: 0 0 0 3px var(--nv-brand-softer);
}

.nv-confirm__item-ico {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
  border-radius: 10px;
  background-color: var(--nv-soft);
  overflow: hidden;
}

.nv-confirm__item.is-on .nv-confirm__item-ico { background-color: var(--nv-elevated); }

.nv-confirm__item-ico img {
  width: 24px;
  height: 24px;
  object-fit: contain;
  display: block;
}

.nv-confirm__item-txt { flex: 1; min-width: 0; }

.nv-confirm__item-name {
  display: block;
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 右侧对勾：与左侧图标形成"两端确认"，比只换边框色更明确 */
.nv-confirm__item-tick {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex: 0 0 22px;
  border: 1.5px solid var(--nv-border);
  border-radius: 50%;
  color: #fff;
  transition:
    background-color var(--nv-dur-fast) var(--nv-ease-out),
    border-color var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-confirm__item.is-on .nv-confirm__item-tick {
  border-color: transparent;
  background-image: var(--nv-gradient);
}

.nv-confirm__item-tick .material-icons-outlined {
  font-size: 15px;
  opacity: 0;
  transform: scale(.6);
  transition:
    opacity var(--nv-dur-fast) var(--nv-ease-out),
    transform var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-confirm__item.is-on .nv-confirm__item-tick .material-icons-outlined {
  opacity: 1;
  transform: none;
}

/* ── 弹窗内的优惠券（可折叠） ──────────────────────────────────────────
   ★ 从商品表单迁移过来（2026-10-16，用户要求降低表单高度）。
   ★ 折起时只占一行小字，展开才出现输入框 —— 多数顾客没有券，
     常驻一个空输入框等于给弹窗平白加一层视觉噪音。 */
.nv-confirm__coupon {
  margin-top: var(--nv-space-4);
  border-top: 1px solid var(--nv-border-soft);
  padding-top: var(--nv-space-3);
}

.nv-confirm__coupon-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 4px 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-text-secondary);
  cursor: pointer;
  transition: color var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-confirm__coupon-toggle:hover { color: var(--nv-brand); }

.nv-confirm__coupon-toggle > .material-icons-outlined:first-child {
  font-size: 17px;
  color: var(--nv-text-muted);
}

.nv-confirm__coupon-caret {
  margin-left: auto;
  font-size: 19px;
  color: var(--nv-text-faint);
  transition: transform var(--nv-dur-normal) var(--nv-ease-out);
}

.nv-confirm__coupon-toggle[aria-expanded="true"] .nv-confirm__coupon-caret {
  transform: rotate(180deg);
}

.nv-confirm__coupon-body { padding-top: var(--nv-space-2); }

.nv-confirm__coupon-tip {
  margin: var(--nv-space-2) 0 0;
  font-size: var(--nv-fs-micro);
  line-height: 1.5;
  color: var(--nv-text-faint);
}

@media (prefers-reduced-motion: reduce) {
  .nv-confirm__coupon-caret { transition: none; }
}

/* 底部 */
.nv-confirm__foot {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-2);
  padding: var(--nv-space-5);
}

.nv-confirm__go {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  height: 48px;
  border: 0;
  border-radius: var(--nv-radius-btn);
  background-color: var(--nv-brand-solid);
  color: #fff;
  font-family: inherit;
  font-size: 15px;
  font-weight: var(--nv-weight-bold);
  cursor: pointer;
  box-shadow: var(--nv-shadow-brand);
  transition:
    filter var(--nv-dur-fast) var(--nv-ease-out),
    transform var(--nv-dur-fast) var(--nv-ease-out);
}

.nv-confirm__go:hover:not(:disabled) { filter: brightness(1.06); }
.nv-confirm__go:active:not(:disabled) { transform: scale(.985); }

/* 未选付款方式时禁用：降饱和 + 禁用光标，而不是直接隐藏 —— 
   按钮始终在位，顾客才知道"选完就能点"。 */
/* 未选付款方式时禁用。
   ★ 原先用 filter: saturate(.4) 去饱和 —— 结果是整颗按钮发灰发闷，
     用户反馈「颜色不够蓝」。改用主题既有约定 .nv-btn:disabled 的
     opacity:.45：保留蓝色色相、只降低存在感，不可点依然一眼可辨。 */
.nv-confirm__go:disabled {
  opacity: .45;
  cursor: not-allowed;
}

.nv-confirm__go .material-icons-outlined { font-size: 19px; }

.nv-confirm__tip {
  margin: 0;
  text-align: center;
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-faint);
}

.nv-confirm__tip b {
  color: var(--nv-text-muted);
  font-weight: var(--nv-weight-medium);
}

/* ==========================================================================
   16. 窄屏分类抽屉（2026-10-17 新增）
   --------------------------------------------------------------------------
   替代原「24 个二级分类平铺成一条横滑胶囊」：
   那条总宽 1972px、视口 361px —— 只能看 4 个、要横滑 6 屏，
   且一二层级被压平，用户看不出「长视频平台」属于「视频会员」。

   ★ 只在窄屏渲染这套 DOM（桌面端用左栏分类树，不受影响）。
   ★ 触控目标全部 ≥44px（WCAG / Apple HIG 下限）。
   ========================================================================== */

/* ── 触发器行：一行，「全部分类」+ 当前选中 ── */
.mcat-bar {
  display: flex;
  align-items: center;
  gap: var(--nv-space-2);
  /* ★★ 2026-10-17 去掉横向 padding（用户反馈「和下面的商品宽度不一样」）
     实测：容器 .mcat-bar 是 16~377（与商品卡片一致），
     但加了 `padding: 0 16px` 后，内部按钮变成 32~146 —— 左缘内缩 16px，
     与商品卡片的左缘不在同一条垂直线上。

     → 改 padding: 0，让按钮直接贴容器边缘，与卡片共用同一条视觉轴线。
     ★ 上下留白仍保留（承接推荐位 / 与列表分隔）。 */
  padding: 0;
  margin: var(--nv-space-5) 0 var(--nv-space-4);
}

.mcat-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  height: 44px;
  padding: 0 var(--nv-space-4);
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-pill);
  background-color: var(--nv-elevated);
  color: var(--nv-text-primary);
  font-family: inherit;
  font-size: var(--nv-fs-body);
  font-weight: var(--nv-weight-bold);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out),
              border-color var(--nv-dur-fast) var(--nv-ease-out);
}
.mcat-trigger:active {
  background-color: var(--nv-brand-softer);
  border-color: var(--nv-border-brand);
}
.mcat-trigger .material-icons-outlined {
  font-size: 18px;
  color: var(--nv-text-muted);
}

/* 当前分类：常显，让用户随时知道"我在看什么"
   ★★ 2026-10-17 改为与「全部分类」同款（用户选方案 1：两个控件统一样式）
   原来它是「无描边 + 浅蓝底」，而左边按钮是「白底 + 描边」——
   两个并排的控件形态不一致，看起来像两个不相干的东西。

   现在统一为：白底 + 1px 描边 + 胶囊，只靠【文字颜色】区分主次：
     · 左「全部分类」= 操作入口 → 深色文字
     · 右「当前分类」= 状态显示 → 品牌色文字 + 品牌描边（弱化但可辨） */
.mcat-current {
  flex: 1;
  min-width: 0;
  height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--nv-space-4);
  border: 1px solid var(--nv-border);
  border-radius: var(--nv-radius-pill);
  background-color: var(--nv-elevated);
  color: var(--nv-brand-ink);
  font-size: var(--nv-fs-caption);
  font-weight: var(--nv-weight-bold);
  overflow: hidden;
}
.mcat-current .material-icons-outlined {
  font-size: 16px;
  flex: 0 0 16px;
}
.mcat-current > span:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 「全部商品」时：还没有选到具体分类 → 退为中性色，与"已选中"区分 */
.mcat-current.is-all {
  color: var(--nv-text-secondary);
  border-color: var(--nv-border-soft);
}

/* ── 遮罩 ── */
.mcat-scrim {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background-color: rgba(0, 0, 0, .42);
  opacity: 0;
  transition: opacity var(--nv-dur-normal) var(--nv-ease-out);
}
.mcat-scrim.is-open { opacity: 1; }

/* ── 底部抽屉 ── */
.mcat-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1001;
  max-height: 84dvh;
  display: flex;
  flex-direction: column;
  background-color: var(--nv-elevated);
  border-radius: 20px 20px 0 0;
  box-shadow: var(--nv-shadow-3);
  transform: translateY(100%);
  transition: transform var(--nv-dur-normal) var(--nv-ease-out);
  padding-bottom: env(safe-area-inset-bottom);
}
.mcat-sheet.is-open { transform: translateY(0); }

.mcat-sheet__grab {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background-color: var(--nv-border-strong);
  margin: 8px auto 0;
  flex: 0 0 auto;
}

.mcat-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px 10px;
  flex: 0 0 auto;
}
.mcat-sheet__title {
  font-size: var(--nv-fs-heading);
  font-weight: var(--nv-weight-bold);
}
.mcat-sheet__close {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-right: -10px;
  border: 0;
  border-radius: 50%;
  background-color: var(--nv-soft);
  color: var(--nv-text-secondary);
  cursor: pointer;
}
.mcat-sheet__close .material-icons-outlined { font-size: 20px; }

/* 内容区
   --------------------------------------------------------------------------
   ★★★ 2026-10-17 去掉了 mask-image（用户反馈「有些许遮挡」「展开后图片没显示」）

   原先写的是：
     mask-image: linear-gradient(to bottom, transparent 0, #000 8px,
                                 #000 calc(100% - 12px), transparent 100%)
   意图是「内容超出时上下渐隐，提示还有内容」。
   但 mask 是把元素【渐隐成透明】，不是"阴影"—— 实测后果：
     · 顶部 8px：「全部商品」的渐变底被切掉一条，看着像被遮住
     · 底部 12px：最后一个二级项（选中态蓝底）整块半透明
       （用户截图里的「效率工具」就是这样）
     · 展开分组后，二级可能正好落在底部渐隐区，看起来像"没显示"

   → 改为纯 padding 留白：
       顶部 8px + 底部 16px，任何状态都不会被裁切。
     内容超出时靠滚动条自然提示，不需要视觉小把戏。
   -------------------------------------------------------------------------- */
.mcat-sheet__body {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--nv-space-2) var(--nv-space-3) var(--nv-space-4);
}

/* ── 「全部商品」 ── */
.mcat-all {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding: 0 var(--nv-space-3);
  margin-bottom: 4px;
  border-radius: var(--nv-radius-md);
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
  font-size: 15px;
  font-weight: var(--nv-weight-bold);
  border: 1px solid transparent;
}
/* ★★ 必须显式压掉 .nv-cat.is-active 的渐变底
   ------------------------------------------------------------------
   .mcat-all 同时带 .nv-cat 类（为了复用 initCategoryBar 的切换逻辑），
   于是会命中旧规则：
       .nv-cat.is-active { background-image: var(--nv-gradient) }
   它用的是 background-image，而我只设了 background-color ——
   图片优先于颜色，结果「全部商品」变成一整块品牌渐变，
   比下面的一级分组还抢眼（截图里就是这样）。

   → 显式 background-image: none 把它压回去。
   ★ 调试方法：CDP 的 CSS.getMatchedStylesForNode 能直接看到
     是哪条规则赢了，比猜快得多。 */
.mcat-all.is-active,
.mcat-all.is-primary {
  background-image: none;
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
}
.mcat-all .material-icons-outlined { font-size: 20px; }
.mcat-all__name { flex: 1; min-width: 0; }
.mcat-all.is-active { border-color: var(--nv-border-brand); }

/* ── 一级分组 ──
   ★★★ 2026-10-17 重新分配「视觉重量」（用户反馈）
   问题：二级是白底+描边+圆角的卡片，一级却是裸行 ——
         结果"次要的东西比主要的东西更重"，层级颠倒。
         而且二级比一级更高（视觉上），一级也没有背景色。

   原则：**一级 = 容器（重），二级 = 选项（轻）**
     · 一级：浅灰底 + 圆角 + 展开/选中时品牌描边 → 像一个"盒子"
     · 二级：无边框透明行 + 缩进 → 像盒子里的"选项"

   ★ 一级之间留 4px，让它看起来是「一叠独立的卡片」而不是一张长表。 */
.mcat-group { margin-bottom: 4px; }

.mcat-group__head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 50px;
  padding: 4px var(--nv-space-3);
  /* ★ 一级有底色 + 描边 —— 这是「容器」的语言 */
  border: 1px solid transparent;
  border-radius: var(--nv-radius-md);
  background-color: var(--nv-soft);
  color: var(--nv-text-primary);
  font-family: inherit;
  font-size: 15px;
  font-weight: var(--nv-weight-bold);
  text-align: left;
  cursor: pointer;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out),
              border-color var(--nv-dur-fast) var(--nv-ease-out);
}
.mcat-group__head:active { background-color: #ececef; }

/* 展开中：换成品牌浅底 + 实描边，让它成为"当前打开的盒子" */
.mcat-group.is-open .mcat-group__head {
  background-color: var(--nv-brand-soft);
  border-color: var(--nv-brand-solid);
}

.mcat-group__img,
.mcat-group__ico {
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  border-radius: 4px;
  object-fit: cover;
  color: var(--nv-text-muted);
}
.mcat-group__ico { font-size: 20px; }

.mcat-group__name { flex: 1; min-width: 0; }
.mcat-group__cnt {
  font-family: var(--nv-mono);
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-faint);
}
.mcat-group__arrow {
  font-size: 20px;
  flex: 0 0 20px;
  color: var(--nv-text-faint);
  transition: transform var(--nv-dur-fast) var(--nv-ease-out);
}
.mcat-group.is-open .mcat-group__arrow { transform: rotate(180deg); }
.mcat-group.is-open .mcat-group__name,
.mcat-group.is-open .mcat-group__ico { color: var(--nv-brand-ink); }
.mcat-group.is-open .mcat-group__head { color: var(--nv-brand-ink); }

/* 当前选中项所在的分组（且未展开）：左上一条短竖线做提示
   ★ 一级现在已有灰底，原先的「左侧整条 2px 竖线」会显得脏 ——
     改为「仅左侧圆角处一段短线」，轻到不影响整体，又能标出位置。
   ★ 展开态的样式优先级更高（见上面的 .is-open 规则），
     所以这条只在"收起但选中在其中"时生效。 */
.mcat-group[data-has-current="1"]:not(.is-open) .mcat-group__head {
  background-color: var(--nv-brand-softer);
  box-shadow: inset 2px 0 0 var(--nv-brand-solid);
}
.mcat-group[data-has-current="1"]:not(.is-open) .mcat-group__name {
  color: var(--nv-brand-ink);
}

/* 二级列表
   ★★★ 2026-10-17 重新设计（用户反馈）
   --------------------------------------------------------------------------
   原来二级是「白底 + 1px 描边 + 圆角」的卡片，问题有三：
     · 一级是裸行、二级却是卡片 → 次要元素比主要元素更"重"，层级颠倒
     · 二级边框几乎顶到容器边缘，与一级的 12px 内缩不在同一根轴上
     · 二级行高 44px 却包着卡片，视觉上比 50px 的裸行一级"更大"

   现在：二级 = **无边框透明行**（选项），一级 = 有色块的盒子（容器）。
     · 去掉 border / background，只保留 hover / 选中时的浅底
     · 左右各缩进 8px，落在"盒子内部"的位置上
     · 左侧保留竖线做层级连接（唯一的辅助线，不再叠加边框）
     · 行高仍 44px（触控下限），但视觉重量靠"无边框"自然变轻

   节奏（由内到外）：
     二级 ↔ 二级        8px
     一级底部 → 首个二级 8px
     末个二级 → 下个一级 12px
   -------------------------------------------------------------------------- */
.mcat-group__subs {
  display: none;
  padding: 4px 0 10px 8px;
}
.mcat-group.is-open .mcat-group__subs { display: block; }

.mcat-sub {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  /* ★ 左 40px 留给竖线 + 文字起点；右 12px 与一级的内边距对齐 */
  padding: 4px var(--nv-space-3) 4px 40px;
  /* ★ 无边框 / 无底色 —— 这是"选项"的语言 */
  border: 1px solid transparent;
  border-radius: var(--nv-radius-md);
  background-color: transparent;
  font-size: 14px;
  color: var(--nv-text-secondary);
  position: relative;
  cursor: pointer;
  transition: background-color var(--nv-dur-fast) var(--nv-ease-out),
              border-color var(--nv-dur-fast) var(--nv-ease-out);
}
/* 行间距：除第一个之外都留 8px */
.mcat-sub + .mcat-sub { margin-top: 2px; }
/* 层级连接线 —— 一眼看出"这是上一级的子项" */
.mcat-sub::before {
  content: '';
  position: absolute;
  left: 27px;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 50%;
  background-color: var(--nv-border);
}
.mcat-sub:active { background-color: var(--nv-soft); }
/* 悬停：与 :active 同色但更淡 —— 无边框行靠底色反馈"可点" */
@media (hover: hover) {
  .mcat-sub:hover { background-color: var(--nv-brand-softer); }
}
.mcat-sub__name { flex: 1; min-width: 0; }
.mcat-sub__cnt {
  font-family: var(--nv-mono);
  font-size: var(--nv-fs-micro);
  color: var(--nv-text-faint);
}
/* 选中：浅蓝底 + 勾选（不只靠颜色，色盲也能分辨）
   ★★★ 2026-10-17 必须压掉 .nv-cat.is-active 的渐变底
   --------------------------------------------------------------------------
   .mcat-sub 同时带 .nv-cat 类（复用 initCategoryBar 的切换逻辑），
   于是命中旧规则：
       .nv-cat.is-active, .nv-cat.is-primary {
         background-image: var(--nv-gradient);
         color: #fff;
       }

   后果（实测）：background-image 是图片、优先级高于 background-color，
   所以二级选中项变成【品牌蓝渐变底】；而 .mcat-sub.is-active 的
   color: var(--nv-brand-ink) 特异性更高，把文字又改回深蓝 ——
   最终是【蓝底蓝字】，几乎看不清。

   → 显式 background-image: none 压回浅蓝底 + 深蓝字。
   ★ 同样的坑在 .mcat-all 上也踩过一次（见上文），
     根因都是「复用了 .nv-cat 类却不要它的皮肤」。 */
.mcat-sub__check {
  font-size: 17px;
  color: var(--nv-brand-solid);
  width: 0;
  opacity: 0;
  overflow: hidden;
  transition: opacity var(--nv-dur-fast) var(--nv-ease-out),
              width var(--nv-dur-fast) var(--nv-ease-out);
}
.mcat-sub.is-active,
.mcat-sub.is-primary {
  background-image: none;
  background-color: var(--nv-brand-soft);
  color: var(--nv-brand-ink);
  font-weight: var(--nv-weight-bold);
}
.mcat-sub.is-active::before { background-color: var(--nv-brand-solid); }
.mcat-sub.is-active .mcat-sub__check { width: 17px; opacity: 1; }
.mcat-sub.is-active .mcat-sub__cnt { color: var(--nv-brand-ink); opacity: .8; }

/* 无货的二级：沿用原有降级（灰显 + 不可点） */
.mcat-sub.is-disabled {
  opacity: .45;
  cursor: not-allowed;
}
.mcat-sub.is-disabled:active { background: transparent; }

/* 桌面端不渲染这套 DOM；万一渲染了也隐藏，避免与左栏分类树重复 */
@media (min-width: 1024px) {
  .mcat-bar { display: none; }
}
