/* ============================================================
   闲鱼自动化工作台 · 设计系统
   中性底 + 白卡片 + 闲鱼黄点缀 + 深色主按钮
   ============================================================ */
:root {
  --bg: #F4F5F7;
  --surface: #FFFFFF;
  --surface-2: #F8F9FB;
  --border: #E7E9EE;
  --border-strong: #D5D9E0;
  --text: #14161A;
  --text-2: #666D78;
  --text-3: #9AA0AA;
  --brand: #FFC400;
  --brand-ink: #7A5B00;
  --brand-soft: #FFF6D9;
  --ink: #1A1D23;
  --green: #15803D;  --green-soft: #DCFCE7;
  --amber: #B45309;  --amber-soft: #FEF3C7;
  --red: #DC2626;    --red-soft: #FEE2E2;
  --blue: #4338CA;   --blue-soft: #E0E7FF;
  /* ★ 09-27 采集来源徽标专用（涛爷令：**按平台配色**）—— 闲鱼=黄 · 拼多多=红 · 淘宝=紫 · 1688=橙。
     ⚠️ 这是**平台品牌辨识色**，不是语义色（危险红 / 成功绿 / 警示琥珀…别拿来混用）；
        统一走「低饱和浅底 + 同色系深字」，深浅底都压得住。
     ⚠️「选中 / 激活」高亮全站仍**只认 --brand 闲鱼黄**，跟这里无关。 */
  --violet: #6D28D9; --violet-soft: #EDE9FE;
  --orange: #9A3412; --orange-soft: #FFEDD5;  /* ★当文字色压深（4.42→5.9:1）*/
  --r-lg: 14px; --r-md: 10px; --r-sm: 7px;
  --shadow-1: 0 1px 2px rgba(20, 22, 26, .04);
  --shadow-2: 0 8px 24px rgba(20, 22, 26, .09);
  /* ★★★ 09-29 七次令：**字号缩放因子 `--fs`** —— 全站 font-size 一律写成
     `calc(Npx * var(--fs))`，桌面 `--fs:1`（= 原值），手机档（≤720px）调小 → **一处控制手机端整体字级**，
     不必散改上百处。⚠️ 只作用于 font-size，**不动间距**（padding/margin 另有手机档规则）。 */
  --fs: 1;
  /* ★★★ 09-29 涛爷令（测试）：**全站界面字体改「幼圆 YouYuan」**。
     例外只有两处，它们在本文件里各自显式钉了书法体族、不受这里影响：
       · `.brand-name`「闲大爷」= STXingkai 行楷
       · `.topbar-in .say li` 六句宣传语 = STKaiti 楷体
     ⚠️ 等宽字体 `--mono`（代码/日志框）**不动** —— 那是技术排版，非界面文案。
     ⚠️ 本机 CDP 实测 YouYuan 可用；若环境缺字体会**静默退化**成雅黑
        → 由 `tools/probe_head_fonts.py` 机器实证把关，别肉眼看。 */
  --font: "YouYuan", "幼圆", "Microsoft YaHei", "PingFang SC", sans-serif;
  --mono: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
}

* , *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; }
/* ★★★ 09-29 涛爷令（三次）：**全站界面字体默认加粗** → 在 `body` 落一个全局默认 font-weight:700。
   ⚠️⚠️ 必须写在 `font:` 简写**之后** —— `font: 14px/1.6 ...` 会把 font-weight 重置为 normal，
      写前面会被吃掉（这是 shorthand 的坑，不是笔误）。
   ★ 例外两处**保持原样不动**（它们各自显式声明，本就不受这里影响）：
       · `.brand-name`「闲大爷」  = 行楷 STXingkai 600
       · `.topbar-in .say li` 六句宣传语 = 楷体 STKaiti 600
   ★ 为什么只改 400/500、不动那 47 处 600？—— 本机 `SIMYOU.TTF`（幼圆）**只有单一 Regular、没有
     Bold 文件**（行楷/楷体同理）→ 浏览器对**所有 >500 的字重做同一套合成加粗**，
     600 与 700 **视觉完全一致**。真正会「比周围正文更细」的只有原先 400/500 的 15 处，
     已逐处提到 700；600/700 保持原值即可（少动一处少一分风险）。
   ⚠️ 等宽 `--mono`（代码/日志框）未单独排重 → 一并继承加粗；若日志太糊可在那几个选择器上回退。
   ⚠️ 加粗会让字面略厚（合成描边），导航可用宽已重跑 tools/probe_nav_fit.py 复核不折行。 */
body {
  background: var(--bg); color: var(--text);
  font: 14px/1.6 var(--font); -webkit-font-smoothing: antialiased;
  font-weight: 700;
}
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: #D5D9E0; border-radius: 6px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: #C0C5CE; }

/* ---------------- 顶栏 ---------------- */
.topbar {
  position: sticky; top: 0; z-index: 40;
  background: rgba(255, 255, 255, .93);
  backdrop-filter: saturate(1.7) blur(9px);
  border-bottom: 1px solid var(--border);
}
/* ★ 2026-09-28 D05「一体式」：顶栏两区 —— 上排「品牌左 + 六句右」、下排「导航 + 登录」。
   ⚠️ 用 grid 不用 flex-wrap：flex 是「宽度不够才换行」，宽屏下导航会爬回第一排；
      grid 直接钉死行列，行为确定。窄屏在下面 @media(<=1266) 回退成 flex。
   ★ 2026-09-28 v2「拉大间距」（涛爷令：间距太窄 → 优化排版）：
     padding 10/24 → 18/32/16；列间距 18 → 28；**行间距 4 → 14**（原先上下排几乎贴在一起）。 */
.topbar-in {
  max-width: 1600px; margin: 0 auto; padding: 16px 32px 15px;
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; column-gap: 28px; row-gap: 16px;
}
.brand { grid-column: 1; grid-row: 1; display: flex; align-items: center; gap: 22px; font-size: calc(16.5px * var(--fs)); font-weight: 600; letter-spacing: -.2px; white-space: nowrap; }
/* ★★★ 09-29 涛爷令：LOGO + 「闲大爷」下方加一行副标「闲鱼自动一体式工作台」。
   版式 = 经典 lockup：LOGO 悬左，右侧竖栈「站名（行楷 42px）→ 副标（无衬线 12.5px）」。
   ⚠️ 竖栈总高 ≈ 48 + 1 + 16 ≈ 65px，**略小于 LOGO 72px** → `.brand` 高度仍由 LOGO 决定
      （顶栏总高不变，`shot_head.py` 的 same_row 判据不受影响）。
   ⚠️ 副标宽度刻意**调成与站名等宽**（≈134px：10 字 × (12.5 + 1.3)），锁成一个方正的矩形；
      副标若比站名宽，品牌块会变宽、挤掉六句/导航的可用宽（见 probe_nav_fit.py）。
   ⚠️ 副标走**界面字体**（`var(--font)`，09-29 起 = 幼圆）而非楷/行楷：站名已是书法体，
      副标是功能性说明，需要明确的主从对比；同一字体堆两行会糊成一片。
   ⚠️ 颜色 #7A6440 是**暖褐灰**（同族降调），对白底对比度 ≈ 5.2:1 → 过 AA（≥4.5:1）。 */
.brand-txt { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; }
.brand-sub {
  font-family: var(--font);
  font-size: calc(16.5px * var(--fs)); font-weight: 700; letter-spacing: 1.7px;
  color: #7A6440; line-height: 1.28; white-space: nowrap;
}
.brand-mark {
  width: 29px; height: 29px; border-radius: 9px; background: var(--brand);
  display: grid; place-items: center; font-size: calc(15.4px * var(--fs)); font-weight: 700; color: #1A1D23;
}
/* ★ D05：品牌位放大 —— LOGO 72 / 站名「闲大爷」40（原 40/22 的 ≈1.8 倍）
   ★ v2（09-28 涛爷令）：站名换**国风字体**「华文行楷 STXingkai」（本机 CDP 实测可用）→ 42px、字距放开 2.6px。
   ⚠️ 行楷/楷体**无粗体**，font-weight 写 800 只会被合成加粗（发糊）→ 降回 600。
   ⚠️ 族末必须 serif 兜底；⚠️ 别写 `HYZhongHeiTi`/`FZCuHeiSongJianTi`（本机缺 → 静默退化）。 */
.brand-logo { width: 96px; height: 96px; border-radius: 50%; display: block; flex: none; box-shadow: 0 6px 18px rgba(160, 112, 20, .18); }
.brand-name {
  font-family: 'STXingkai', 'STKaiti', 'KaiTi', serif;
  font-size: calc(56px * var(--fs)); font-weight: 600; letter-spacing: 3.4px; line-height: 1.15; color: #2B2118;
}
/* ★ D05：六句宣传语 —— 顶栏右上、**强制单行**（nowrap）、字号不缩（否掉设计稿里的 13px）。
   ⚠️ 正好 6 个 <li>、一格一句；圆点走 CSS 伪元素（写进文本 → 守卫 textContent 不等 → 失败）。
   ★ v2（09-28 涛爷令）：句间距 22 → 30（拉开呼吸）；六句换**国风字体**「华文楷体 STKaiti」
     （楷体端正、比行楷好认，与站名行楷同属书法系）；字号 17 → 18。
   ★ v3（09-29 涛爷令·**整块 ×1.25**，与品牌整排同步放大）：字号 18 → 22.5；装饰尺寸一并等比
     跟上，否则「字变大、间距不动」会显挤：句间距 30 → 37.5、li 内 gap 9 → 11.25、
     字距 .8 → 1、圆点 6 → 7.5。⚠️ 六句 nowrap 整条随之变宽（≈917 → ≈1146）——
     这是**新的横向硬约束**，断点必须重测（tools/probe_nav_fit.py），别沿用旧值。 */
.topbar-in .say {
  grid-column: 2 / 4; grid-row: 1; justify-self: end;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: 6px 37.5px; min-width: 0;
  margin: 0; padding: 0; list-style: none;
}
.topbar-in .say li {
  display: flex; align-items: center; gap: 11.25px;
  font-family: 'STKaiti', 'KaiTi', 'STXingkai', serif;
  font-size: calc(22.5px * var(--fs)); font-weight: 600; color: #4A3700;
  letter-spacing: 1px; white-space: nowrap;
}
.topbar-in .say li::before {
  content: ""; width: 7.5px; height: 7.5px; border-radius: 50%; background: #FFC400; flex: none;
}
/* ★★★ 09-29 涛爷令（二次）：导航 9 板块改**整体居中**（取消原先「靠左与副标左缘对齐」）。
   —— ⚠️⚠️ 必须 `grid-column: 1 / -1`（**跨满 3 列**）。只跨 `1 / 3` 时，nav 盒子的右缘停在
      col2 末尾、**右外侧还剩一个 `column-gap`(28px)**，居中就整体左偏 14px（实测左富余 159.4
      vs 右富余 187.5，差 28 = 一个列间距）。跨满后 nav 盒子 == 完整内容区 → 才是真居中。
   ★ 同时「间隔再宽一点点」：gap 8 → **14px**。
   ★★ 通用规则：**以后新增导航板块默认沿用居中排版**（`flex` + `justify-content:center`
      会自动把新项并入居中序列，无需改版式；只要别写死 margin-left）。
   ⚠️ 窄屏 @media(<=1266) 切成横向滚动后，居中会让溢出内容**两端都被裁掉**
      → 该媒体查询里显式 `justify-content: flex-start` 顶回左端。 */
.nav { grid-column: 1 / -1; grid-row: 2; display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; }
/* ★ v2（09-28）：导航 9 板块 → **胶囊状轮廓**（全圆角 + 描边）。
   ★ v3（09-28 涛爷令）两项：
     ① 字体「傻大个」→ 弃用雅黑（17.5px 又大又平）+ 字号 17.5 → **16px** + 字重 500 + 字距 .2px。
        ⚠️ v3 原写死 **Noto Sans SC**；**09-29 涛爷令改走 `var(--font)` = 幼圆**（全站字体测试）。
     ② 胶囊轮廓「再加粗一点点」→ 描边 **1px → 1.5px**，色 #D5D9E0 → **#C9CED8**（深一档，1.5px 才看得见）。
        ⚠️ `--border`(#E7E9EE) 在白底上几乎看不见，别用。
   ⚠️⚠️ 胶囊内边距**不能贪大**：`tools/probe_nav_fit.py` 实测——导航可用宽 = 视口 − 356；
      曾 `9px 17px` → 1016px（1280 起就折两行）。⚠️ 改 padding / gap / font-size 后**必须重跑 probe_nav_fit.py**；
      更窄的屏走 flex-wrap 自然折行（9 项仍全可见），≤1120 再切成横向滚动。 */
.nav a {
  padding: 8px 14px; border-radius: 999px; font-size: calc(17.6px * var(--fs));
  font-family: var(--font);
  font-weight: 700; letter-spacing: .2px;
  border: 1.5px solid #C9CED8; background: var(--surface);
  color: var(--text-2); text-decoration: none; transition: .16s; white-space: nowrap; line-height: 1.3;
}
.nav a:hover { background: var(--brand-soft); border-color: #F2CE6A; color: var(--brand-ink); }
.nav a.on {
  background: linear-gradient(135deg, #FFD54A, var(--brand)); border-color: #F5B700;
  color: #5C4300; font-weight: 700; box-shadow: 0 3px 10px rgba(255, 196, 0, .38);
}
.nav a.dis { opacity: .4; pointer-events: none; }
/* ★★★ 09-29 涛爷令：顶栏「当前店铺登录态 + 重新登录」**两个元素全站取消显示**
   —— 要看店铺登录状态，去「店铺管理」即可（accounts.js:225 每张店铺卡自带「登录 / 重新登录」）。
   ⚠️ 用 display:none 而**不是删 DOM**：app.js:2485 `$('#btnRelogin').onclick = rlOpen`
      **没有判空**，元素被删会抛 TypeError、打断该页后续初始化。
   ⚠️ 隐藏后 col3(auto) 塌缩为 0 —— `.say`(grid-column 2/4) 与 `.nav`(1/3) 跨列仍合法，
      导航可用宽反而**变大**（更不易折行）；已重跑 tools/probe_nav_fit.py 复核。
   ★★ 2026-10-09 修正：09-29 是「取消**重新登录**按钮」，**不是取消这个格子**。
      个人中心入口（#wbAcctEntry）需要挂在这里 —— 本格子 grid-column 3 / row 2
      落在导航行右侧的天然空档里（导航 9 项居中占 1016px，内容区 1600px ⇒ 右侧余 ~290px），
      **不用改任何 grid 定位、不压品牌、不挤导航**，比新开容器干净。
      ⇒ 因此容器恢复 `display:flex`，改为**单独隐藏里面那两个旧元素**。 */
.topbar-right { display: flex; grid-column: 3; grid-row: 2; justify-self: end; margin-left: 0; align-items: center; gap: 14px; }
/* 🔴 09-29 撤销的「当前店铺登录态 + 重新登录」：容器恢复显示后，**必须逐个隐藏**，
   否则会把涛爷明令下线的按钮放回界面。DOM 保留（app.js:2485 依赖它不抛错）。 */
.topbar-right > .login-pill,
.topbar-right > #btnRelogin { display: none !important; }
/* ★ 个人中心入口（common.js initAccountEntry 动态挂载）：
   昵称是用户输入 ⇒ 长度不可控 ⇒ `max-width` + `overflow:hidden` + `text-overflow:ellipsis`，
   超长昵称**只截断显示**，绝不撑破顶栏把导航挤走。 */
#wbAcctEntry {
  display: inline-flex; align-items: center; gap: 6px;
  max-width: 190px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; flex: none;
}
.login-pill {
  display: inline-flex; align-items: center; gap: 7px; padding: 5px 11px;
  border-radius: 20px; font-size: calc(13.8px * var(--fs)); background: var(--green-soft); color: var(--green); font-weight: 700;
}
.login-pill.off { background: var(--red-soft); color: var(--red); }
.login-pill i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* ★ 响应式：**<=1570** 退回 flex（grid 三列在窄屏会被挤到溢出）。
   ⚠️ 临界点都实测过（tools/probe_nav_fit.py），**取两条约束里更严的那条**：
      ① 六句一行需宽 **1146px**（22.5px 实测；`justify-self:end`，放不下时会**从左侧越出格子**、压到品牌）
         ＋ 品牌块宽 **300px** ＋ 两个 `column-gap` 56px = **1502px 才放得下一行**
         ⇒ 内容区必须 ≥ 1502 → `.topbar-in`/`.wrap` 的 `max-width` 由 **1360 放宽到 1600**（内容宽 1536）；
            视口 < 1566 时内容宽不足 → **六句自动折行**（`.say { flex-wrap: wrap }`），不再压品牌。
      ② 导航 9 胶囊需宽 **949px**，但 `grid-column:1/-1` 跨满整行（可用 = 视口 − 64）
         → 视口 ≥ 1013 即可 —— **不再是瓶颈**（§二十三 居中改造后口径变了）。
      ⇒ ① 是硬约束 → 断点取 **1570**（比临界 1566 留 4px 余量，grid 档下保证不折行）。
   ★★ 09-29 四次令「品牌整排再加大」把品牌 228 → 300 ⇒ 断点上移 1266 → 1296。
   ★★ 09-29 五次令「六句同步 ×1.25」（18→22.5px）把六句 917 → 1146
      ⇒ **内容区放宽 1360→1600、断点上移 1296 → 1570**。
      六句宽度钉死，品牌/六句一发胖只能靠**放宽内容区或提前回退**腾地方 —— 零和的。
   ★ 六句已 `flex-wrap: wrap`（grid 档也允许折行）——即便某宽度算漏，最坏也只是折两行，**不会压品牌**。
   下面 flex 档：六句降 19.4px 并允许换行、导航切成横向滚动。 */
@media (max-width: 1570px) {
  .topbar-in { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; }
  .brand { grid-column: auto; grid-row: auto; }
  /* ⚠️ 六句 nowrap 时自己是 777px、**不能收缩** → 768 这种窄屏会直接顶出视口；
     降级档必须放开换行 + min-width:0（宽屏「必须一行」的约束只对 grid 档生效）。 */
  .topbar-in .say { grid-column: auto; grid-row: auto; justify-self: auto; margin-left: auto; gap: 8px 20px; flex-wrap: wrap; min-width: 0; }
  .topbar-in .say li { font-size: calc(19.4px * var(--fs)); }   /* ★ 09-29 四次令：随主档 ×1.25（15.5→19.4）；间距维持压缩档原值 */
  /* ⚠️ 导航降级后仍单行；`min-width:0` 才允许它收缩 → 与「重新登录」同排不折行。
     ⚠️⚠️ 必须显式 `justify-content: flex-start`：横向滚动 + 居中会让**溢出内容两端被裁掉**
        （第一项左半截点不到）—— 宽屏的居中只属于 grid 档。 */
  .nav { grid-column: auto; grid-row: auto; justify-content: flex-start; flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: 2px; }
  .nav::-webkit-scrollbar { display: none; }
  .nav a { padding: 8px 16px; font-size: calc(17.1px * var(--fs)); }
  .topbar-right { grid-column: auto; grid-row: auto; justify-self: auto; margin-left: auto; flex: none; }
}

/* ---------------- 布局 ---------------- */
.wrap { max-width: 1600px; margin: 0 auto; padding: 22px 24px 64px; }
.phead { display: flex; align-items: flex-end; gap: 16px; margin-bottom: 18px; flex-wrap: wrap; }
.phead h1 { font-size: calc(22px * var(--fs)); font-weight: 600; margin: 0 0 4px; letter-spacing: -.3px; }
.phead p { margin: 0; font-size: calc(14.3px * var(--fs)); color: var(--text-2); }
.quota { display: flex; flex-direction: column; align-items: flex-end; gap: 7px; min-width: 168px; margin-left: auto; }
.quota .qv { display: flex; align-items: baseline; gap: 6px; font-size: calc(13.8px * var(--fs)); color: var(--text-2); }
.quota .qv b { font-size: calc(16.5px * var(--fs)); font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.bar { width: 100%; height: 5px; border-radius: 3px; background: var(--border); overflow: hidden; }
.bar i { display: block; height: 100%; border-radius: 3px; background: var(--brand); transition: width .4s; }
.bar.hot i { background: var(--red); }

/* ---------------- 采集模式页签（2026-09-15 取代原「指标卡」） ---------------- */
/* 🔴 2026-10-26 涛爷令：「5 个模块并排成一排，不要分成两排。」
   原来写死 `repeat(4, ...)` ⇒ 第 5 个（采集记录）被挤到第二排。
   ✅ 改成 5 列，同时**压缩卡片内边距/图标/字号**，否则 1600 宽下每格只剩
      ~290px，「粘已挑好的链接，一行一条，直接拉资料」这种描述会换行撑高。
   ⚠️ 描述文字改成**单行截断**（ellipsis）—— 宁可省略号也别折成两行把卡片顶歪。 */
.modes {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px; margin-bottom: 18px;
}
.mode {
  display: flex; align-items: center; gap: 10px; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  /* 5 列并排 ⇒ 内边距收窄一档，给文字腾空间 */
  padding: 12px 13px; font-family: inherit; transition: .15s;
  min-width: 0;
}
.mode:hover { border-color: var(--border-strong); }
.mode.on {
  border-color: var(--brand); background: var(--brand-soft);
  box-shadow: 0 0 0 1px var(--brand) inset;
}
.mode .mi { font-size: calc(24.2px * var(--fs)); line-height: 1; flex: none; }
.mode .mt { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.mode .mt b { font-size: calc(15.4px * var(--fs)); font-weight: 600; }
.mode .mt i {
  font-style: normal; font-size: calc(12.2px * var(--fs)); color: var(--text-2);
  /* 5 列并排：描述**单行截断**，⛔ 不许折成两行把卡片顶高 */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mode.on .mt b { color: var(--brand-ink); }
/* 窄屏：5 列太挤 ⇒ 描述文字先隐藏（图标+标题仍可点） */
@media (max-width: 1280px) {
  .mode .mt i { display: none; }
  .mode { padding: 11px 10px; gap: 8px; }
  .mode .mi { font-size: calc(20px * var(--fs)); }
}
@media (max-width: 860px) { .modes { grid-template-columns: 1fr; } }

/* ---------------- 指标卡（KPI 已下线，样式保留备用） ---------------- */
.kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 18px; }
.kpi { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 15px 16px; }
.kpi .lb { font-size: calc(13.2px * var(--fs)); color: var(--text-2); margin-bottom: 8px; }
.kpi .vv { font-size: calc(26.4px * var(--fs)); font-weight: 600; letter-spacing: -.5px; font-variant-numeric: tabular-nums; line-height: 1; }
.kpi .vv small { font-size: calc(14.3px * var(--fs)); color: var(--text-3); font-weight: 700; }
.kpi .sub { font-size: calc(12.7px * var(--fs)); color: var(--text-3); margin-top: 7px; }

/* ---------------- 面板 ---------------- */
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 18px 20px; margin-bottom: 18px; }
.panel-h { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
.panel-h h2 { font-size: calc(15.4px * var(--fs)); font-weight: 600; margin: 0; }
.panel-h .sp { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* ---------------- 关键词输入 ---------------- */
.kwbox {
  display: flex; flex-wrap: wrap; gap: 7px; align-items: center; min-height: 44px;
  padding: 7px 9px; border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--surface); margin-bottom: 16px; transition: .15s;
}
.kwbox:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(255, 196, 0, .22); }
.kwbox input { border: 0; outline: 0; background: transparent; flex: 1; min-width: 170px; font-size: calc(14.9px * var(--fs)); padding: 5px 4px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px; background: var(--brand-soft);
  color: var(--brand-ink); border-radius: var(--r-sm); padding: 4px 8px; font-size: calc(13.8px * var(--fs)); font-weight: 700;
}
.chip b { cursor: pointer; opacity: .45; font-weight: 700; font-size: calc(16.5px * var(--fs)); line-height: 1; }
.chip b:hover { opacity: 1; }

/* ---------------- 整店 / 收藏 的来源输入 ---------------- */
.srcbox {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  padding: 7px 10px 7px 16px; border: 1px solid var(--border-strong);
  border-radius: var(--r-md); background: var(--surface); transition: .15s;
}
.srcbox:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(255, 196, 0, .22); }
.srcbox input { border: 0; outline: 0; background: transparent; flex: 1; min-width: 240px; font-size: calc(14.9px * var(--fs)); padding: 6px 0; }
.srchint { font-size: calc(13.2px * var(--fs)); color: var(--text-2); white-space: nowrap; }
.srchint.ok { color: var(--ok, #1a7f4b); }
.srchint.err { color: var(--danger, #c0392b); }
.srcnote {
  margin: 12px 0 4px; padding: 12px 12px; font-size: calc(13.8px * var(--fs)); line-height: 1.75;
  color: var(--text-2); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--r-md);
}
.srcnote b { color: var(--text); }
.srcnote a { color: var(--brand-ink); }

/* ---------------- 链接采集（2026-09-27 涛爷令）----------------
   形态：白卡片 + 一条「标题 / 动作」头 + 大文本框 + 一行统计。
   ★ 文本框刻意给足高度（min-height 240px，可继续往下拖）—— 用户一次
     可能粘几十上百条链接，框太小等于让人盲填。字号用等宽体，链接对齐好认。 */
.urlbox {
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden; transition: .15s;
}
.urlbox:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(255, 196, 0, .22); }
.urlbox-h {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 12px; background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.urlbox-t { font-size: calc(13.8px * var(--fs)); font-weight: 600; color: var(--text); }
.urlbox-t em {
  font-style: normal; font-weight: 700; font-size: calc(12.7px * var(--fs)); color: var(--text-3); margin-left: 8px;
}
.urlbox-a { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.urlbox-a label.btn { cursor: pointer; }         /* <label> 冒充按钮：文件选择器要它 */
.urlbox textarea {
  display: block; width: 100%; box-sizing: border-box;
  min-height: 240px; max-height: 60vh; resize: vertical;
  border: 0; outline: 0; background: transparent; padding: 12px 14px;
  font-family: var(--mono); font-size: calc(13.8px * var(--fs)); line-height: 1.85; color: var(--text);
}
.urlbox textarea::placeholder { color: var(--text-3); font-family: var(--font); }
.urlbox-f {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 12px; border-top: 1px solid var(--border); background: var(--surface-2);
  font-size: calc(13.2px * var(--fs)); color: var(--text-2);
}
.urlstat { font-variant-numeric: tabular-nums; }
.urlstat.ok { color: var(--green); font-weight: 600; }
.urlstat.bad { color: var(--red); font-weight: 600; }
.urlstat-e { margin-left: auto; color: var(--text-3); }
.urlstat-e.bad { color: var(--red); font-weight: 600; }

/* 识别结果预览：每行「平台徽标 + 链接 + 状态」 */
.urlprev {
  margin-top: 10px; border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); max-height: 240px; overflow: auto;
}
.urlprev .urow {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 12px; font-size: calc(13.2px * var(--fs)); border-bottom: 1px solid var(--border);
}
.urlprev .urow:last-child { border-bottom: 0; }
.urlprev .urow .uidx { flex: none; width: 26px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.urlprev .urow .uurl {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--mono); color: var(--text-2);
}
.urlprev .urow .uwhy { flex: none; max-width: 46%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--red); }
.urlprev .urow.bad { background: var(--red-soft); }
.urlprev .urow.dup { background: var(--amber-soft); }
.urlprev .urow.dup .uwhy { color: var(--amber); }
.urlprev .uhead {
  position: sticky; top: 0; z-index: 1; background: var(--surface-2);
  padding: 7px 12px; font-size: calc(12.7px * var(--fs)); font-weight: 600; color: var(--text-2);
  border-bottom: 1px solid var(--border);
}

/* ---------------- 参数 ---------------- */
.params { display: flex; gap: 20px; align-items: flex-end; flex-wrap: wrap; }
/* ★ 09-27 涛爷令：链接采集「极简」—— 收起条件区里的**所有字段**，
   ⚠️⚠️ 只隐 `.field`，**绝不动 `.params` 里的 `.actions`**（采集/停止按钮就住在里面，
     整块 `.params` 藏掉会把按钮一起藏了）。用类而非 inline style：切回别的模式 `remove('slim')`
     后，本来就 display:none 的 #fldHeadless 靠自己的 inline 值天然还原。 */
.params.slim > .field { display: none; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: calc(12.7px * var(--fs)); color: var(--text-2); }
.field input, .field select {
  padding: 8px 10px; border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); font-size: calc(14.3px * var(--fs)); outline: 0; min-width: 92px; transition: .15s;
}
.field input:focus, .field select:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(255, 196, 0, .22); }
.actions { margin-left: auto; display: flex; gap: 9px; }

/* 区间输入（价格 / 想要数）*/
.rng { display: flex; align-items: center; gap: 6px; }
.rng span { color: var(--text-3); font-size: calc(13.2px * var(--fs)); }
.rng input { width: 76px; min-width: 0; }
.rng input::placeholder { color: var(--text-3); }

/* 控制台分组：筛选条件 / 采集参数 */
.cond { border-top: 1px dashed var(--border); padding-top: 15px; margin-top: 15px; }
.cond:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
/* ★ 09-27 涛爷令：链接采集「极简」—— 标题/字段收起后去掉分隔虚线，让底部
   「采集 / 停止」干净地贴在文本框下面（⚠️ 按钮留在 .cond 里不动，避免位置漂移）。 */
.cond.bare { border-top: 0; padding-top: 0; margin-top: 0; }
/* ★ 09-27 涛爷令：极简下只剩「采集 / 停止」按钮，与上方链接文本框之间**要留呼吸间距**
   （原先把 .cond 的 padding/margin 全清了 → 按钮紧贴文本框，毫无缝隙）。
   ⚠️ 只给**含 `.actions` 的那块**的按钮组加上边距：第一块（只剩被 `.slim` 隐藏的字段、
      自身高度 0）不受影响，不会凭空多出一段空白。 */
.params.slim > .actions { margin-top: 16px; }
.cond-h {
  display: flex; align-items: baseline; gap: 8px;
  font-size: calc(12.7px * var(--fs)); color: var(--text-2); margin-bottom: 10px;
}
.cond-h::before {
  content: ''; width: 3px; height: 11px; border-radius: 2px;
  background: var(--brand); transform: translateY(1px); flex: none;
}
.cond-h em { font-style: normal; color: var(--text-3); font-size: calc(12.1px * var(--fs)); }

/* ---------------- 按钮（2026-09-16 涛爷：全站统一闲鱼黄渐变胶囊） ---------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 20px; border-radius: 999px; border: none;
  background: linear-gradient(135deg, #FFD54A, var(--brand));
  color: #5C4300; font-size: calc(14.9px * var(--fs)); font-weight: 700; cursor: pointer;
  transition: .15s; white-space: nowrap;
  box-shadow: 0 2px 8px rgba(255, 196, 0, .35);
}
.btn:hover:not(:disabled) {
  transform: translateY(-1px);
  background: linear-gradient(135deg, #FFDA5E, #FFC91F);
  box-shadow: 0 4px 12px rgba(255, 196, 0, .45);
}
.btn:disabled { opacity: .45; cursor: not-allowed; }
/* ★ 2026-10-09 补（涛爷「一键更新登录态」进度面板实测抓到）：
   `.btn` 是 `display:inline-flex` —— **作者样式会盖掉 `hidden` 属性**，
   于是 `btn.hidden = true` 完全不起作用（按钮照旧显示）。
   全站铁律：带 display 的类必须补 `[hidden]{display:none}`。 */
.btn[hidden] { display: none; }
/* ★ 09-27（UI/UX 清单 CRITICAL · focus-states）：键盘焦点必须可见 ——
   极简后链接采集页只剩「采集 / 停止」两个按钮当唯一交互入口，焦点不可见 = 键盘用户找不到。
   ⚠️ 用 `:focus-visible`：**只在键盘/需要时**出现，鼠标点击**不显示**（零视觉打扰）。
   输入框 / 文本框容器早已有品牌黄 `focus-within` 环，这里给按钮补齐同一套。 */
.btn:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(255, 196, 0, .5), 0 2px 8px rgba(255, 196, 0, .45);
}
/* primary / ghost 与默认统一为黄胶囊（涛爷：统一样式，不再做深色/透明弱化） */
.btn.sm { padding: 5px 15px; font-size: calc(13.8px * var(--fs)); }
/* 危险动作：红胶囊（形状统一，颜色保留警示） */
.btn.danger {
  background: linear-gradient(135deg, #F87171, #DC2626); color: #fff;
  box-shadow: 0 2px 8px rgba(220, 38, 38, .30);
}
.btn.danger:hover:not(:disabled) {
  background: linear-gradient(135deg, #F98080, #E13434);
  box-shadow: 0 4px 12px rgba(220, 38, 38, .40);
}
/* 强制解锁等警告动作：琥珀胶囊（比 danger 温和、比黄胶囊醒目） */
.btn.warn {
  background: linear-gradient(135deg, #FDE68A, #FCD34D); color: #92500A;
  box-shadow: 0 2px 8px rgba(251, 191, 36, .35);
}
.btn.warn:hover:not(:disabled) { background: linear-gradient(135deg, #FEE68F, #FBC92E); }
/* AI 弹窗头部那颗：同胶囊但更紧凑（配合 30px 标题行高） */
.btn.capsule {
  padding: 5px 18px; font-size: calc(14.3px * var(--fs));
}

/* ---------------- 运行态 ---------------- */
.run { display: none; }
.run.on { display: block; }
.run-h { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand); }
.dot.live { animation: pulse 1.4s infinite; }
.dot.done { background: var(--green); }
.dot.err { background: var(--red); }
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(255, 196, 0, .25); }
  50% { box-shadow: 0 0 0 8px rgba(255, 196, 0, .05); }
}
.run-meta { margin-left: auto; display: flex; gap: 16px; font-size: calc(13.8px * var(--fs)); color: var(--text-2); }
.run-meta b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.log {
  background: #0F1115; border-radius: var(--r-md); padding: 12px 14px;
  font: 12px/1.8 var(--mono); color: #C7CBD1; max-height: 236px; overflow: auto;
  white-space: pre-wrap; word-break: break-all;
}
.log .g { color: #7EE787; }
.log .y { color: #F2CC60; }
.log .r { color: #FF7B72; }
.log .d { color: #6E7681; }

/* ---------------- 商品网格 ---------------- */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); gap: 14px; }
.pcard {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  overflow: hidden; cursor: pointer; transition: .18s; display: flex; flex-direction: column;
}
.pcard:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); border-color: var(--border-strong); }
.pcard .ph { aspect-ratio: 1/1; background: var(--surface-2); position: relative; overflow: hidden; }
.pcard .ph img { width: 100%; height: 100%; object-fit: cover; display: block; transition: .35s; }
.pcard:hover .ph img { transform: scale(1.045); }
.pcard .ph .n { position: absolute; right: 7px; bottom: 7px; background: rgba(16, 18, 22, .72); color: #fff; font-size: calc(12.1px * var(--fs)); padding: 2px 7px; border-radius: 5px; }
.pcard .bd { padding: 10px 11px 12px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
/* ★ 10-13：卡片上的「✨ AI 助手」入口（选品库删除后二创直接挂在这里）。
   ⚠️ 必须占满整行（align-self:stretch）且 margin-top:auto —— 否则它会跟上面的
   标签行挤在一起，且卡片高度不齐时按钮位置参差。 */
.pcard .card-ai { align-self: stretch; margin-top: auto; justify-content: center; }
.pcard .row1 { display: flex; align-items: baseline; gap: 6px; }
.price { font-size: calc(17.6px * var(--fs)); font-weight: 600; color: #C2410C; font-variant-numeric: tabular-nums; }
.want { font-size: calc(12.7px * var(--fs)); color: var(--text-2); }
.ptitle {
  font-size: calc(13.8px * var(--fs)); line-height: 1.5; margin: 0; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 37px;
}
.tags { display: flex; gap: 5px; flex-wrap: wrap; margin-top: auto; }
.tag { font-size: calc(12.1px * var(--fs)); padding: 2.5px 7px; border-radius: 6px; font-weight: 700; white-space: nowrap; }
.t-clean { background: var(--green-soft); color: var(--green); }
.t-warn  { background: var(--amber-soft); color: var(--amber); }
.t-risk  { background: #FFEDD5; color: #C2410C; }
.t-high  { background: var(--red-soft); color: var(--red); }
.t-dup   { background: var(--blue-soft); color: var(--blue); }
.t-gray  { background: #F1F2F4; color: #52525B; }
.t-kw    { background: var(--brand-soft); color: var(--brand-ink); }
.t-ai    { background: var(--green-soft); color: var(--green); }
/* ★★★ 2026-10-15：上架天数标签 —— **绿色系**（越绿=越新，是采集要挑的货）。
   ⚠️ `.t-listed-unknown` 必须**明显区别于**有值的样子（灰 + 虚线边），
      否则「未知」会被当成又一个正常标签扫过去 —— 而「未知」和「今天上架」
      语义完全相反（见 app.js `ldTxt`），看错就是选错货。 */
.t-listed { background: var(--green-soft); color: var(--green); font-weight: 600; }
.t-listed-unknown {
  background: #F1F2F4; color: #71717A;
  border: 1px dashed #A1A1AA; font-weight: 500;
}
/* ★ 09-27 涛爷令：采集来源徽标**按平台配色** —— 闲鱼=黄（本家）· 拼多多=红 · 淘宝/天猫=紫 · 1688=橙。
   ⚠️ key → class 的映射**唯一口径**在 `common.js:WB_PLAT_CLS`（`wbPlatCls()` / `wbPlatBadge()`）；
      这里只负责上色，别在 js 里另写一份映射（改一处漏一处 = 同平台两页颜色不一样）。
   ⚠️ 未知平台一律回落中性灰 `.t-gray`（别硬套某个平台色）。 */
.t-goofish { background: var(--brand-soft);  color: var(--brand-ink);  font-weight: 600; }  /* 闲鱼 · 黄 */
.t-pdd     { background: var(--red-soft);    color: var(--red);        font-weight: 600; }  /* 拼多多 · 红 */
.t-taobao  { background: var(--violet-soft); color: var(--violet);     font-weight: 600; }  /* 淘宝/天猫 · 紫 */
.t-1688    { background: var(--orange-soft); color: var(--orange);     font-weight: 600; }  /* 1688 · 橙 */
/* 旧类保留（若别处仍引用不至于失样式）；新代码一律走上面四个平台类。 */
.t-plat    { background: var(--violet-soft); color: var(--violet);     font-weight: 600; }
/* ★ 09-18 客服入口标识：鱼小铺=蓝色（走卖家工作台客服） */
.t-shop  { background: var(--blue-soft); color: var(--blue); }
/* ★ 09-20 定时发布（原「草稿箱」）：已排期=蓝（到点自动发布）/ 已自动同步=橙 */
.t-blue  { background: var(--blue-soft); color: var(--blue); }
.t-orange{ background: var(--amber-soft); color: #B45309; }
/* ★ 09-23 审计 B2：卡片上的「发到哪个账号」角标（队列与定时发布共用） */
.tag.t-acct { background: var(--surface-3, var(--surface-2)); color: var(--text-2); border: 1px solid var(--border); }

/* ★ 09-20 通用卡片网格（定时发布等列表用；itemmgr 页内也各有一份同款定义） */
.kgrid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(214px, 1fr)); }

/* 选品勾选：卡片左上角勾选框 + 选中高亮 */
/* 🔴🔴 ⑲ 勾选框不许贴卡片圆角（15:55 涛爷报障）
   ────────────────────────────────────────────────────────────
   实测铁证（`tools/_probe_pick2.py`，`/` 与 `/listing` 各 24/1 个 `.pcard`）：
     `.pcard` = 圆角 **22px** + `overflow:hidden`
     `.pick`  = `left:0; top:0` 命中区 34×34
     ⇒ 勾选框 rect 距卡片左边 **-2px**、上边 **-2px**（贴在圆角弧上）
     ⇒ 22px 圆弧正好把 15px 的小方块**切掉一整块**，只露一半 ⇒ 难点中

   ✅ 正解：勾选框**往卡片里挪 10px**，同时把命中区从 34 提到 **40×40**，
     圆角改成「跟卡片同弧」的四角圆，让它看起来是**扣在卡片里的一枚圆钮**，
     不是**挂在角上被切一半的方块**。
     · `left/top: 10px`  ⇒ 完全脱离 22px 圆弧的死区，不会再被裁
     · 40×40 命中区⇒ 好点，比原来 34 大了 18%
     · `inset:10px` 的 input 只占中间 20×20，**仍在** label 内 ⇒
       **不改变层级关系** ⇒ 10-12 那个「勾选框被压回 .grid 底下」的坑不会复发
   ⚠️ **只改位置/尺寸/圆角，一个属性都不许碰 `z-index` 和 `position`**
      —— 这两个是 10-12 修复「点不动」的命根子，碰了立刻复发。 */
.pcard .pick {
  position: absolute; left: 10px; top: 10px; z-index: 5;
  width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .94);
  border: 1px solid var(--border);
  border-radius: 12px;              /* 圆角内收 ⇒ 视觉上"扣"在卡内，不再撞 22px 弧 */
  box-shadow: var(--shadow-1); cursor: pointer;
  transition: box-shadow .18s ease, border-color .18s ease, background .18s ease;
}
/* hover 反馈：只加厚边与扩散，**禁止 transform**（会自激闪烁，见 _verify_shadow） */
.pcard .pick:hover {
  border-color: var(--brand);
  box-shadow: 0 3px 0 #EBD49E, 0 10px 18px -8px rgba(180, 130, 10, .5);
}
/* 卡片选中时勾选框跟着染黄，和 `.picked` 描边呼应 */
.pcard.picked .pick { border-color: var(--brand); background: var(--brand-soft); }

/* 🔴 ⑲-2 input 必须**居中**，不能贴 label 左上角
   ────────────────────────────────────────────────────────────
   实测根因：老规则 `.pick input { position:absolute; inset:0; width:15px; height:15px }`
   里 `inset:0` 先把 `left/top/right/bottom` 全设成 0，后面的 `width/height:15px`
   只能覆盖尺寸、**盖不掉 left/top** ⇒ 小方块被**顶到 label 左上角**而不是居中
   ⇒ 再叠加 label 自己贴卡片 `left:0;top:0` ⇒ 方块整个骑在 22px 圆弧上被切。

   ✅ 解法：**放弃绝对定位**，让 flex 居中（`.pick` 已是 flex + center）。
     ⚠️ 为何敢去掉 `inset:0`？因为 `<label>` 包 input 时，**点 label 任意位置
       浏览器原生就会切换勾选**（10-12 当初加 `inset:0` 是误解）。
     ⚠️ 尺寸 15→20px：跟新 40×40 命中区匹配，小方块也更好点。
   🚨🚨 10-18 二改（涛爷报障：「勾选框很大，但真正勾选的位置只有四分之一」）：
     这不是错觉，是**三条规则叠在同一个 input 上打架**留下的坑（见下方原 `:614`/`:616`）：
       :605 `position:static; inset:auto; width:20px`  ← 打算用 flex 居中
       :614 `position:absolute; inset:0; 100%×100%`   ← 改成铺满（10-12 那次）
       :616 `width:15px; height:15px`                ← 又把尺寸改回去，**但没改位置**
     ⇒ 最终生效的是 `position:absolute` + `left:0;top:0` + `15×15`
     ⇒ 20px 的方块被**顶在 40×40 label 的左上角**，正好只占左上四分之一 ⇒ 与截图完全吻合。

     ✅ 正解：**input 铺满 label（透明）负责命中**，**可见的勾选框改用 label 的
        `::after` 自绘并用 `:has(:checked)` 上色** —— 两者彻底解耦：
          · 命中区 = 整个 40×40（保住 10-12「点 label 空白处也能切换」）
          · 可见框 = flex 正中 20×20，**绝无可能再跑到角上**
        ⚠️ 用 `:has()` 是因为勾选状态在 input 上、而要染色的框是它的父级 label；
           Chromium 105+ / Firefox 121+ / Safari 15.4+ 全支持，本项目只跑 Chromium。 */
.pcard .pick input {
  position: absolute; inset: 0;
  width: 100%; height: 100%; margin: 0;
  cursor: pointer; opacity: 0;      /* 透明但仍可命中：铺满 label */
}
.pcard { position: relative; }
/* 可见的勾选框：绝对居中于 40×40 label，20×20 */
.pcard .pick::after {
  content: ''; position: absolute;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 20px; height: 20px; border-radius: 6px;
  border: 1.5px solid var(--border); background: #fff;
  box-sizing: border-box;
  transition: border-color .16s ease, background .16s ease;
  pointer-events: none;             /* 不吃事件，事件全给铺满的 input */
}
.pcard .pick:hover::after { border-color: var(--brand); }
/* 勾上：染黄 + 打勾（勾用两条边框旋转 45° 画出来，不依赖字体/图片） */
.pcard .pick:has(input:checked)::after {
  background: var(--brand); border-color: var(--brand);
}
.pcard .pick:has(input:checked)::before {
  content: ''; position: absolute;
  left: 50%; top: 50%; transform: translate(-50%, -52%) rotate(45deg);
  width: 5px; height: 9px;
  border: solid #fff; border-width: 0 2px 2px 0;
  pointer-events: none;
}
/* 🚨🚨 BUG 修复（10-12，涛爷报障「选品库无法单个勾选，只能全选」）
   ────────────────────────────────────────────────────────────
   实测铁证（tools/_audit_checkboxes.py / 事件序列追踪）：
     第1次点 label.pick → input.click=true,  input.change=true   ✅
     第2次点 label.pick → 【事件序列完全为空】                    ❌
     第3次点 label.pick → input.click=false                       ❌ 状态反了
   且第 2 次点击前 `elementFromPoint(勾选框中心)` 从 `INPUT` 变成了 `DIV.grid`
   —— **勾选框自己从光标底下挪走了**（坐标从 60,485,15×15 变成 62,486,15×14）。

   根因 = **勾选动作引发了重排（reflow）**，两处叠加：
     ① `.pcard:hover` 有 `transform: translateY(-6px) rotateX(7deg)`
        （1743-1755 段那段 3D 立体感样式）；
     ② `.pcard.picked` 换了 `border-color` + 加了 `box-shadow: 0 0 0 2px`
        —— 2px 的 spread **撑大了盒子**，网格列宽随之重算。
   两者都会让卡片/勾选框位移，而鼠标停在原地，下一次点击就落在别的元素上
   ⇒ 用户观感 = 「单选点不动，只有全选（点在固定位置的工具条上）有效」。

   ✅ 正解（两条都改）：
     A. 勾选框 `label.pick` 直接**铺满左上角 26×26 命中区**并吃掉冒泡，
        让「点卡片任意处也能切换勾选」成为可能（下面 JS 同步改）；
     B. `.picked` 的高亮改用**不改变盒子尺寸**的 `outline`（不参与布局）
        + `background` 微调，彻底消除 spread 撑大问题。
        ⚠️ **不要**用 `box-shadow: 0 0 0 2px` 做选中态，它必然撑盒子。
        （`.pcard:hover` 的 3D 变换保留 —— 它是视觉效果，
          且已由 JS 在勾选瞬间主动失焦/复位，不再影响命中。） */
/* 🔴 ⑲-3 选中态不许用 `outline`（16:10涛爷报「这都不对齐啊」）
   ────────────────────────────────────────────────────────────
   实测（`tools/_probe_align.py`）：静止态卡片 top 全 = 258、勾选框 pickTop 全 = 270，
   **几何本来是齐的**。看起来不对的元凶是选中卡那条 `outline` ——
   `outline-offset:-2px` 把描边画在盒子**内侧**，于是选中的那张卡
   视觉上「边框变粗 + 内容显小一圈」，跟旁边没选中的卡对不齐。

   ✅ 正解：`outline` 换成 **`inset box-shadow`**。
     · inset 阴影**画在内容层之下**，不改变盒子尺寸（同样满足 10-12 铁律
       「不许用 spread 撑盒子」），但**不会让边框显得变粗** ⇒ 选中卡与邻卡视觉齐平。
     · 描边宽度收到 2px，配 `outline` 的视觉重量一致。
   ⚠️ 铁律回顾：`.picked` 的任何高亮都**不得改变盒子尺寸**
      （`box-shadow` 非 inset spread / border-width 加粗 / padding 变化，全部禁止）。 */
.pcard.picked {
  border-color: var(--brand);
  box-shadow: inset 0 0 0 2px var(--brand), var(--shadow-1);
  background: var(--brand-soft);
}
/* ⚠️ 10-12：勾选框改成贴左上角 0,0（命中区 34×34）⇒ 这个「选入日期」角标
   原来靠 left:8px 避让，现在会重叠，挪到右下角（与 `.ph .n` 图数角标同侧，
   但它在 bottom、不同元素不打架）。 */
.pcard .ph .selmark { right: 7px; left: auto; bottom: 7px; background: var(--green); }
.selbar {
  display: inline-flex; align-items: center; gap: 9px;
  background: var(--brand-soft); border: 1px solid var(--brand-ink);
  border-radius: var(--r-sm); padding: 4px 10px;
}
.selbar .ck { display: inline-flex; align-items: center; gap: 5px; font-size: calc(13.8px * var(--fs)); cursor: pointer; }
.selbar .ck input { accent-color: var(--brand); }
.selinfo { font-size: calc(13.8px * var(--fs)); color: var(--text-2); white-space: nowrap; }
/* ★ 勾选内容提示（09-18）：说明状态按钮为什么可用/不可用 */
.selhint { font-size: calc(12.7px * var(--fs)); color: var(--text-3); white-space: nowrap; }
.selhint.warn { color: var(--brand); font-weight: 600; }

/* ---------------- 选品库 ---------------- */
.sel-group { margin-top: 6px; }
.sel-group + .sel-group { margin-top: 20px; }
.sel-group-h {
  display: flex; align-items: center; gap: 9px;
  margin: 4px 0 11px; padding-bottom: 7px; border-bottom: 1px dashed var(--border);
}
.sel-group-h b { font-size: calc(14.9px * var(--fs)); }
.sel-group-h .cnt { font-size: calc(13.2px * var(--fs)); color: var(--text-2); }
/* ★ 分组全选（09-17 涛爷需求：未上架 / 已上架 各自单独全选） */
.sel-group-h .grp-all {
  margin-left: auto; font-size: calc(13.2px * var(--fs)); color: var(--text-2);
  cursor: pointer; user-select: none; white-space: nowrap;
  padding: 2px 9px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); transition: .15s;
}
.sel-group-h .grp-all:hover { color: var(--text); border-color: var(--brand); }
.sel-group-h .grp-all input { accent-color: var(--brand); margin: 0 4px 0 0; vertical-align: -1px; }
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
.kpi { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); padding: 12px 15px; display: flex; flex-direction: column; gap: 3px; }
.kpi .kpi-l { font-size: calc(12.7px * var(--fs)); color: var(--text-2); }
.kpi b { font-size: calc(23.1px * var(--fs)); font-variant-numeric: tabular-nums; }

/* AI 助手弹窗（改文案 / 生图）
   注意：必须写成 .rmodal.ai-modal —— .rmodal 的同名属性定义在文件更后面，
   只用 .ai-modal 会被后者按同优先级覆盖（踩过） */
.rmodal.ai-modal { width: min(920px, 95vw); max-height: 90vh; }
.ai-modal .rmodal-h { position: relative; padding-right: 62px; align-items: center; }
.rmodal-x {
  position: absolute; right: 14px; top: 14px;
  width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-2);
  font-size: calc(14.3px * var(--fs)); line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.rmodal-x:hover { background: var(--surface-2); color: var(--ink); }
/* 改文案：左右两栏严格等高对齐 */
.ai-cols {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px; align-items: stretch;
}
@media (max-width: 760px) { .ai-cols { grid-template-columns: 1fr; } }
.ai-col { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.ai-h {
  display: flex; align-items: center; gap: 8px; height: 30px;
  font-size: calc(13.8px * var(--fs)); color: var(--text-2);
}
.ai-h .sp { flex: 1; }
.ai-box {
  height: 300px; box-sizing: border-box; overflow: auto;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 12px 12px; font-size: calc(14.1px * var(--fs)); line-height: 1.8; color: var(--text);
  white-space: pre-wrap; word-break: break-word;
}
.ai-box.edit {
  background: var(--surface); resize: none; font-family: inherit; outline: 0;
  width: 100%; display: block;
}
.ai-box.edit:focus { border-color: var(--brand); }
.ai-hint { margin-top: 10px; font-size: calc(13.8px * var(--fs)); color: var(--amber); min-height: 18px; }
/* ★ 2026-10-20 商机 P0-②：敏感词复检的**高危**态（level 3 ⇒ 禁用「采用」）
   🔴 红色只给「真拦住了」用 —— 「有提醒但可采用」仍走黄（--amber）。 */
.ai-hint.is-danger { color: var(--red); font-weight: 600; }
/* ★ 2026-10-20 商机 P1-④：改写风格下拉（动态注入到 AI 助手弹窗）
   🔴 用**现有控件样式**（不新造视觉语言）；⛔ 不加宽（弹窗那行本来就挤）。 */
.ai-style-sel {
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text); border-radius: var(--r-sm);
  padding: 4px 8px; font-size: calc(12.4px * var(--fs));
  max-width: 132px; margin-right: 6px;
}
.ai-foot {
  padding: 12px 20px; border-top: 1px solid var(--border);
  display: flex; align-items: center; gap: 9px;
}
.ai-foot .sp { flex: 1; }

/* AI 三页签（文案 / 生图 / 生视频） */
.ai-tabs {
  display: flex; gap: 4px; padding: 0 20px;
  border-bottom: 1px solid var(--border);
}
.ai-tabs button {
  appearance: none; border: 0; background: none; cursor: pointer;
  padding: 9px 14px 10px; font-size: calc(14.3px * var(--fs)); font-family: inherit;
  color: var(--text-2); border-bottom: 2px solid transparent;
}
.ai-tabs button:hover { color: var(--ink); }
.ai-tabs button.on { color: var(--brand-ink); font-weight: 600; border-bottom-color: var(--brand); }
.ai-pane { display: flex; flex-direction: column; min-height: 0; }
.ai-act { display: flex; align-items: center; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
.ai-act .ai-hint { margin-top: 0; }
/* 以图生图：参考图 / 结果图左右对称（2026-09-15 重排，提示词横跨整宽） */
.img-cols {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 18px; align-items: start;
}
@media (max-width: 760px) { .img-cols { grid-template-columns: 1fr; } }
.img-col { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.img-frame {
  position: relative; width: 100%; max-width: 372px; margin: 0 auto;
  aspect-ratio: 1 / 1;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-md); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.img-frame img { width: 100%; height: 100%; object-fit: contain; display: block; }
.img-empty {
  padding: 0 18px; text-align: center;
  font-size: calc(13.8px * var(--fs)); color: var(--text-2); line-height: 1.7;
}
.img-prompt { height: 96px; }
.desc-preview {
  font-size: calc(13.2px * var(--fs)); color: var(--text-2); line-height: 1.65;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.desc-preview.ai { color: var(--green); }
/* ★ 10-13：文案为空时的占位提示（别让人点进「修改图文」才发现是空的）。
   虚线底 + 弱化色，明确是「提示」不是「文案内容」。 */
.desc-preview.dim {
  color: var(--text-3); font-style: normal;
  border: 1px dashed var(--border-strong); border-radius: var(--r-sm);
  padding: 6px 10px; background: transparent;
  -webkit-line-clamp: 2;
}
.pcard .ops { display: flex; gap: 6px; }
.pcard .ops .btn { flex: 1; }

/* ---------------- 筛选 ---------------- */
.filters { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* ★ 09-18 涛爷：勾选条（全选/已选N件/操作按钮）一律推到第一行最右边。
   筛选控件是 flex-wrap 的，用 margin-left:auto 把它顶到行尾 —— 窄屏换行后
   它自成一行贴右，仍然好看。三页（采集结果/选品库/自动上架）共用这条。 */
.filters .selbar { margin-left: auto; }
/* ★ 09-27 涛爷令：「采集结果 / 选品列表」筛选行**整行平铺 + 左对齐**。
   ⚠️ 这两页的筛选行用的是 `.sp.filters`（**没有 `.fill`**）→ 继承 `.panel-h .sp{margin-left:auto}`
      被整体推到右侧，实测左边空 **44px**（采集结果）/ 9px（选品列表），左边缘与标题 h2 对不齐。
   这里只改「占位与对齐」：`flex-basis:100%` 独占一整行 + `margin-left:0` 左对齐；
   右端仍由上面 `.filters .selbar{margin-left:auto}` 顶到行尾 → 左右两端都贴齐。
   ⚠️ **控件宽度一律不动** —— `.filters.fill` 那套收窄规则是上架/商品管理页专用的，别搬过来。 */
.sp.filters:not(.fill) { flex: 1 1 100%; margin-left: 0; }
.filters input, .filters select {
  padding: 7px 10px; border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  font-size: calc(13.8px * var(--fs)); background: var(--surface); outline: 0;
}
.filters input:focus, .filters select:focus { border-color: var(--brand); }
.seg { display: flex; background: var(--surface-2); border-radius: var(--r-sm); padding: 3px; gap: 2px; }
.seg button {
  border: 0; background: transparent; padding: 6px 12px; border-radius: 6px;
  font-size: calc(13.8px * var(--fs)); color: var(--text-2); cursor: pointer; transition: .12s;
}
.seg button.on { background: linear-gradient(135deg, #FFD54A, var(--brand)); color: #5C4300; font-weight: 700; box-shadow: 0 2px 6px rgba(255, 196, 0, .35); }

/* ---------------- 分页 ---------------- */
.pager { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 22px; font-size: calc(14.3px * var(--fs)); color: var(--text-2); }
/* 🔴🔴 `.pager` 的 `display:flex` 特异性 (0,1,0) **压过** HTML `hidden` 属性
   （hidden 只是 UA 层的 `display:none`，优先级最低）
   ⇒ JS 里 `pager.hidden = true` 对 `.pager` **完全无效**！
   症状：退款页签下，订单的「上一页 / 第 1/222 页 / 下一页」漏在退款表上方。
   ✅ 正解：加这个显式规则，`.is-hidden` 由 JS 挂上。 */
.pager.is-hidden, .ordwrap.is-hidden { display: none !important; }
.pager b { color: var(--text); font-variant-numeric: tabular-nums; }

/* ---------------- 骨架 / 空态 ---------------- */
.sk { background: linear-gradient(90deg, var(--surface-2) 25%, #EEF0F4 37%, var(--surface-2) 63%); background-size: 400% 100%; animation: sk 1.3s infinite; }
@keyframes sk { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
.sk-card { height: 268px; border-radius: var(--r-lg); border: 1px solid var(--border); }
.empty { padding: 64px 20px; text-align: center; color: var(--text-3); font-size: calc(14.9px * var(--fs)); }
.empty b { display: block; font-size: calc(16.5px * var(--fs)); color: var(--text-2); font-weight: 700; margin-bottom: 6px; }

/* ---------------- 抽屉 ---------------- */
.mask { position: fixed; inset: 0; background: rgba(16, 18, 22, .42); opacity: 0; pointer-events: none; transition: .2s; z-index: 60; }
.mask.on { opacity: 1; pointer-events: auto; }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(780px, 95vw);
  background: var(--surface); z-index: 61; transform: translateX(102%);
  transition: transform .28s cubic-bezier(.4, 0, .2, 1); display: flex; flex-direction: column;
  box-shadow: -12px 0 40px rgba(20, 22, 26, .14);
}
.drawer.on { transform: none; }
.dhead { padding: 18px 22px; border-bottom: 1px solid var(--border); display: flex; align-items: flex-start; gap: 12px; }
.dhead h3 { margin: 0 0 6px; font-size: calc(17.6px * var(--fs)); font-weight: 600; line-height: 1.45; }
.dbody { flex: 1; overflow: auto; padding: 20px 22px 40px; }
/* ★ 09-30 加：抽屉**底部固定操作栏**。抽屉正文（.dbody）是滚动区，
   内容一长（例如回收站几十条历史），放在正文末尾的按钮就得滚到底才点得到
   → 操作按钮钉在正文之外，永远可见。 */
.dfoot {
  flex: 0 0 auto; display: flex; gap: 9px; flex-wrap: wrap;
  padding: 14px 22px; border-top: 1px solid var(--border); background: var(--surface);
}
/* ★ 09-30 加：`.sp`（撑开用空 span）在 .ai-h/.ai-foot/.panel-h 里各自拿到 flex:1，
   但 .dfoot 里没有 → 底部操作栏里的「右侧那枚按钮」贴不到最右，白白挤在中间。
   与 .ai-foot .sp 保持同一条约定，一处补齐。 */
.dfoot .sp { flex: 1; }
.dsec { margin-bottom: 24px; }

/* ★ 10-13：批量 AI 二创进度抽屉（.bm-*）—— 从 selection.html 的私有 <style>
   原样搬来。选品库板块已删除、selection.html 整页移除，样式必须跟到 app.css，
   否则「采集商品」页的批量二创抽屉会没有进度条/逐行卡片的样式。
   ⚠️ .bm-row[hidden] 那条必留：.bm-row 写了 display:flex，会把 [hidden] 整个盖掉。 */
.bm-bar { height: 10px; border-radius: 999px; background: var(--surface-2);
          border: 1px solid var(--border); overflow: hidden; }
.bm-bar > i { display: block; height: 100%; width: 0; background: var(--brand); transition: width .3s; }
.bm-txt { margin-top: 9px; font-size: calc(13.2px * var(--fs)); color: var(--text-2); }
.bm-list { display: flex; flex-direction: column; gap: 8px; }
.bm-row { display: flex; gap: 10px; align-items: center; padding: 9px 11px;
          border: 1px solid var(--border); border-radius: var(--r-md); background: var(--surface-2); }
.bm-row[hidden] { display: none; }
.bm-row.running { border-color: var(--brand); background: var(--brand-soft); }
.bm-row.done { border-color: var(--green); }
.bm-row.failed { border-color: var(--red); }
.bm-th { width: 44px; height: 44px; flex: 0 0 auto; border-radius: var(--r-sm);
         border: 1px solid var(--border); object-fit: cover; background: var(--surface); }
.bm-mid { min-width: 0; flex: 1; }
.bm-t { font-size: calc(13.75px * var(--fs)); color: var(--text);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bm-e { font-size: calc(12.1px * var(--fs)); color: var(--red); margin-top: 3px; word-break: break-all; }
/* ★ 2026-10-20 商机 P0-②：批量二创的**提示**（level 2/1 用词提醒）
   🔴 用**黄**（--amber）而不用红 —— 红在本站表示「失败」，
   而这条是「成功了，但发布前你自己再核一遍」⇒ 混成红色会让人以为白跑了。 */
.bm-w { font-size: calc(12.1px * var(--fs)); color: var(--amber); margin-top: 3px; word-break: break-all; }
.bm-st { flex: 0 0 auto; }

.dsec h4 { font-size: calc(13.2px * var(--fs)); font-weight: 600; color: var(--text-2); margin: 0 0 10px; letter-spacing: .3px; }
.dgal { display: grid; grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); gap: 8px; }
.dgal img { width: 100%; aspect-ratio: 1/1; object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--border); cursor: zoom-in; transition: .15s; }
.dgal img:hover { border-color: var(--brand); }
.dcopy { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-md); padding: 12px 16px; font-size: calc(14.3px * var(--fs)); line-height: 1.75; white-space: pre-wrap; word-break: break-word; max-height: 260px; overflow: auto; }
.kv { display: grid; grid-template-columns: 88px 1fr; gap: 8px 14px; font-size: calc(14.3px * var(--fs)); }
.kv dt { color: var(--text-2); }
.kv dd { margin: 0; word-break: break-all; }
.close-x { margin-left: auto; border: 0; background: transparent; font-size: calc(24.2px * var(--fs)); line-height: 1; color: var(--text-3); cursor: pointer; padding: 0 4px; }
.close-x:hover { color: var(--text); }
.lightbox { position: fixed; inset: 0; background: rgba(10, 11, 14, .9); z-index: 80; display: none; align-items: center; justify-content: center; padding: 30px; cursor: zoom-out; }
.lightbox.on { display: flex; }
.lightbox img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 8px; }

/* ---------------- Toast ---------------- */
.toast {
  position: fixed; left: 50%; bottom: 32px; transform: translate(-50%, 20px);
  background: var(--ink); color: #fff; padding: 11px 20px; border-radius: var(--r-md);
  font-size: calc(14.9px * var(--fs)); opacity: 0; pointer-events: none; transition: .24s; z-index: 90;
  box-shadow: var(--shadow-2); max-width: 80vw;
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }
.toast.err { background: var(--red); }
.toast.ok { background: #14532D; }

/* ============================================================
   时间范围控件（采集结果 / 选品库 / 自动上架 三页共用）
   ============================================================ */
/* 时间范围控件（2026-09-18 涛爷：三页统一用这套）
   —— 挂在 .runs-range 本身（⛔ 别挂到已下线的批次工具条上） */
.runs-lab { font-size: calc(13.8px * var(--fs)); color: var(--text-3); }
.runs-range { display: inline-flex; align-items: center; gap: 6px; }
.runs-range input[type="datetime-local"] { width: 146px; padding: 0 6px; }
.runs-tilde { font-size: calc(13.8px * var(--fs)); color: var(--text-3); }
/* .filters 里的时间框：与同排的下拉/输入框等高（compat 老 .filters 的 7px 竖内边距） */
.filters .runs-range input[type="datetime-local"] {
  height: 32px; box-sizing: border-box; padding: 0 6px;
  font-size: calc(13.8px * var(--fs)); font-family: inherit;
}
.filters .runs-lab, .filters .runs-tilde { line-height: 32px; }
.ck { display: inline-flex; align-items: center; gap: 6px; font-size: calc(13.8px * var(--fs)); color: var(--text-2); cursor: pointer; user-select: none; }
.ck input { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; margin: 0; }
.selinfo { font-size: calc(13.8px * var(--fs)); color: var(--text-3); font-variant-numeric: tabular-nums; }
/* 起止时间控件（.runs-lab/.runs-range/.runs-tilde）的定义统一在上面「时间范围控件」段首，
   三页共用 —— 别在这里重复定义，同文件重复会静默覆盖（踩过） */

.runhint { margin: 0 0 12px; font-size: calc(13.2px * var(--fs)); color: var(--text-3); }

.rbody { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 5px; }

/* 批次状态胶囊 */
.st { font-size: calc(12.1px * var(--fs)); padding: 2.5px 8px; border-radius: 20px; font-weight: 700; white-space: nowrap; }
.st-run   { background: var(--brand-soft); color: var(--brand-ink); }
.st-done  { background: var(--green-soft); color: var(--green); }
.st-cancel{ background: #F1F2F4; color: #52525B; }
.st-error { background: var(--red-soft); color: var(--red); }
.st-legacy{ background: var(--blue-soft); color: var(--blue); }

/* ---------------- 批次详情弹窗 ---------------- */
.rmask {
  position: fixed; inset: 0; background: rgba(16, 18, 22, .46);
  opacity: 0; pointer-events: none; transition: .2s; z-index: 55;
}
.rmask.on { opacity: 1; pointer-events: auto; }
.rmodal {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -48%) scale(.98);
  width: min(1080px, 94vw); max-height: 86vh; z-index: 56;
  background: var(--surface); border-radius: var(--r-lg);
  box-shadow: 0 24px 70px rgba(20, 22, 26, .26);
  display: flex; flex-direction: column; overflow: hidden;
  opacity: 0; pointer-events: none; transition: .22s;
}
.rmodal.on { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
.rmodal-h {
  padding: 16px 20px; border-bottom: 1px solid var(--border);
  display: flex; align-items: flex-start; gap: 12px;
}
.rmodal-h h3 { margin: 0 0 5px; font-size: calc(17.1px * var(--fs)); font-weight: 600; }
.rm-sub { font-size: calc(13.8px * var(--fs)); color: var(--text-2); line-height: 1.6; word-break: break-all; }
.rmodal-body { flex: 1; overflow: auto; padding: 16px 20px 26px; }

/* ---------------- 通用确认弹窗 ---------------- */
.cmodal {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -48%) scale(.98);
  width: min(440px, 92vw); z-index: 64;
  background: var(--surface); border-radius: var(--r-lg); padding: 20px 22px;
  box-shadow: 0 24px 70px rgba(20, 22, 26, .26);
  opacity: 0; pointer-events: none; transition: .2s;
}
.cmodal.on { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
.cmodal h3 { margin: 0 0 10px; font-size: calc(16.5px * var(--fs)); font-weight: 600; }
.cf-body { font-size: calc(14.3px * var(--fs)); color: var(--text-2); line-height: 1.75; }
.cf-body b { color: var(--text); }
.cf-body .warn {
  margin-top: 10px; background: var(--red-soft); color: #991B1B;
  border-radius: var(--r-sm); padding: 9px 11px; font-size: calc(13.8px * var(--fs)); line-height: 1.65;
}
.cf-foot { display: flex; justify-content: flex-end; gap: 9px; margin-top: 18px; }

/* ---------------- 重新登录弹窗 ---------------- */
.cmodal.rl { width: min(520px, 92vw); }
.rl-body { font-size: calc(14.3px * var(--fs)); color: var(--text-2); }
.rl-label { display: block; font-size: calc(13.2px * var(--fs)); margin-bottom: 6px; color: var(--text-2); }
.rl-body select { width: 100%; }
.rl-hint { margin: 10px 0 0; font-size: calc(13.8px * var(--fs)); line-height: 1.7; }
.rl-status { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 12px; }
.rl-line { display: flex; align-items: center; gap: 8px; font-size: calc(14.3px * var(--fs)); }
.rl-line b { color: var(--text); font-weight: 600; }
.rl-meta { margin-left: auto; font-size: calc(13.2px * var(--fs)); color: var(--text-2); font-family: var(--mono); }
.rl-log {
  margin-top: 9px; max-height: 180px; overflow: auto;
  background: var(--surface-2); border-radius: var(--r-sm);
  padding: 8px 10px; font-family: var(--mono); font-size: calc(12.7px * var(--fs)); line-height: 1.7;
  color: var(--text-2);
}
.rl-log div.g { color: var(--green); }
.rl-log div.r { color: var(--red); }
.rl-log div.y { color: var(--amber, #B45309); }

/* ---------------- 登录异常红字警示条（2026-09-15 加）----------------
   涛爷的需求：「登录异常掉线要第一时间红字提醒，然后重新扫码登录」。
   放在采集页顶部，掉线时立刻出现，一眼看得到，并带一键「重新登录」。
   ★ 无头静默模式下，浏览器窗口用户是看不见的 —— 这条横幅就是
     唯一能把「界面上发生了什么」主动推到他面前的通道。 */
.alert {
  display: flex; align-items: flex-start; gap: 12px;
  background: var(--red-soft); border: 1px solid #F7C9C9;
  border-left: 4px solid var(--red);
  border-radius: var(--r-md); padding: 12px 16px; margin-bottom: 14px;
  box-shadow: var(--shadow-1);
  animation: alert-in .18s ease-out;
}
@keyframes alert-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; } }
.alert .ai { font-size: calc(18.7px * var(--fs)); line-height: 1.3; flex: none; }
.alert .at { min-width: 0; flex: 1; }
.alert .at b { display: block; color: var(--red); font-size: calc(14.9px * var(--fs)); margin-bottom: 3px; }
.alert .at span { display: block; font-size: calc(13.8px * var(--fs)); color: #8A2A2A; line-height: 1.65; }
.alert .at code {
  display: block; margin-top: 7px; padding: 6px 9px;
  background: rgba(255, 255, 255, .72); border-radius: var(--r-sm);
  font-family: var(--mono); font-size: calc(12.7px * var(--fs)); color: #8A2A2A;
  word-break: break-all; line-height: 1.6;
}
.alert .aa { display: flex; flex-direction: column; gap: 7px; flex: none; }
.alert.warn { background: var(--amber-soft); border-color: #F3D9A4; border-left-color: var(--amber); }
.alert.warn .at b { color: var(--amber); }
.alert.warn .at span, .alert.warn .at code { color: #7C4A08; }

/* ---------------- 故障截图：无头静默模式的「现场照片」---------------- */
.shots { margin-top: 14px; border-top: 1px dashed var(--border-strong); padding-top: 12px; }
.shots-h { font-size: calc(13.8px * var(--fs)); color: var(--text-2); margin-bottom: 9px; }
.shots-g { display: flex; gap: 10px; flex-wrap: wrap; }
.shots-g figure { margin: 0; width: 168px; }
.shots-g img {
  width: 168px; height: 100px; object-fit: cover; object-position: top;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  cursor: zoom-in; background: var(--surface-2); display: block;
}
.shots-g figcaption {
  margin-top: 5px; font-size: calc(12.1px * var(--fs)); color: var(--text-3);
  font-family: var(--mono); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------- 自动上架（上架队列） ---------------- */
/* 顶部同步提示条：把「自动同步确实发生了」摆到明面上（2026-09-17 涛爷要求同步要看得见） */
.sync-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 14px; padding: 10px 14px;
  background: var(--green-soft); border: 1px solid #B7E4C7;
  border-radius: var(--r-md); font-size: calc(13.8px * var(--fs)); color: #14532D; line-height: 1.55;
}
.sync-banner > b { color: var(--green); font-weight: 700; }
.sync-banner span b { font-weight: 700; }

/* 队列状态角标（封面右上；左上留给勾选框） */
.pcard .ph .li-state {
  position: absolute; right: 7px; top: 7px; z-index: 2;
  font-size: calc(12.1px * var(--fs)); padding: 3px 9px; border-radius: 999px; font-weight: 600;
  box-shadow: var(--shadow-1); white-space: nowrap;
}
.li-pending { background: var(--brand); color: var(--brand-ink); }
.li-listed  { background: var(--green); color: #fff; }
.li-failed  { background: var(--red); color: #fff; }

/* ---------------- 上架记录（2026-09-17 涛爷：点开看产品 + 发到哪个账号） --------- */
/* 卡片上的「发到哪个号」标记 */
.pub-acct {
  margin-left: auto; font-size: calc(12.7px * var(--fs)); font-weight: 600; white-space: nowrap;
  color: var(--brand-ink); background: var(--brand-soft);
  padding: 3px 9px; border-radius: 999px;
}
.pub-acct.none { color: var(--text-3); background: var(--surface-2); font-weight: 700; }
/* 正在跑的任务卡片：左侧一道呼吸色条，一眼看出「这条在动」 */
.pcard.live { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-soft); }
.pcard.live .ph::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--brand); animation: pubPulse 1.2s ease-in-out infinite;
}
@keyframes pubPulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* 抽屉里的商品小卡网格（比选品库窄一点，抽屉里排得下） */
.grid.pubgrid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.pubgrid .pcard { cursor: pointer; }
.pubgrid .ph { display: flex; align-items: center; justify-content: center; }
.pubgrid .ph-none { font-size: calc(13.2px * var(--fs)); color: var(--text-3); }
.pubgrid .ptitle { font-size: calc(13.2px * var(--fs)); -webkit-line-clamp: 2; }
.pubgrid .tags { gap: 4px; }
.pubgrid .tag { font-size: calc(11.6px * var(--fs)); padding: 2px 6px; }
/* 抽屉头上商品详情的标签行 */
.d-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }

/* ============ 上架记录详情：成功 / 失败 分区（2026-09-18 涛爷需求） ============ */
/* 失败区：每件一条横排，原因完整写出（不截断），右侧挂现场截图 */
.pubsec-fail > h4 { color: var(--red); }
.pubsec-ok > h4 { color: var(--green); }
.failbox { display: flex; flex-direction: column; gap: 10px; }
.failrow {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 11px 12px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--red);
  border-radius: var(--r-sm); transition: .15s;
}
.failrow:hover { border-color: var(--brand); border-left-color: var(--red); }
.failph {
  flex: none; width: 62px; height: 62px; border-radius: var(--r-sm);
  overflow: hidden; background: var(--surface-2);
  display: flex; align-items: center; justify-content: center;
}
.failph img { width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.failph .ph-none { font-size: calc(12.1px * var(--fs)); color: var(--text-3); }
.failmain { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.failtitle {
  margin: 0; font-size: calc(13.8px * var(--fs)); font-weight: 600; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.failmeta { display: flex; gap: 10px; flex-wrap: wrap; font-size: calc(12.1px * var(--fs)); color: var(--text-3); }
.failwhy {
  display: flex; gap: 5px; align-items: flex-start;
  font-size: calc(13.2px * var(--fs)); line-height: 1.55; color: var(--text-2);
  background: var(--red-soft);
  border-radius: var(--r-sm); padding: 7px 9px;
  word-break: break-word;
}
.failwhy b { flex: none; color: var(--red); }
.failact {
  flex: none; display: flex; flex-direction: column; gap: 4px;
  align-items: center; font-size: calc(11.6px * var(--fs)); color: var(--text-3);
}
.failact img.failshot {
  width: 108px; height: 76px; object-fit: cover; object-position: top;
  border: 1px solid var(--border); border-radius: var(--r-sm); cursor: zoom-in;
}
.failact img.failshot:hover { border-color: var(--brand); }

/* 登录警示条里的「截图去哪看」指路句 */
.alert .at .la-hint { display: block; margin-top: 5px; font-size: calc(13.2px * var(--fs)); color: var(--text-2); }

/* 上架商品的现场截图（失败复盘用） */
.pubgrid .pub-shot { display: flex; flex-direction: column; gap: 4px; margin-top: auto; }
.pubgrid .pub-shot img {
  width: 100%; height: 84px; object-fit: cover; object-position: top;
  border: 1px solid var(--border); border-radius: var(--r-sm); cursor: zoom-in;
}
.pubgrid .pub-shot img:hover { border-color: var(--brand); }
.pubgrid .pub-shot span { font-size: calc(11.6px * var(--fs)); color: var(--text-3); }

.tag.t-sync { background: var(--green-soft); color: var(--green); font-weight: 600; }
.tag.t-frozen { background: var(--surface-3, var(--surface-2)); color: var(--text-2); border: 1px solid var(--border); }

.li-sync-note {
  margin-top: 9px; padding: 8px 10px; font-size: calc(13.2px * var(--fs)); line-height: 1.5;
  background: var(--surface-2); border-left: 3px solid var(--brand);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; color: var(--text-2);
}

/* ---------------- 自动上架：上架设置排 + 上架任务（2026-09-17） ---------------- */
.panel-h .hint {
  font-size: calc(13.2px * var(--fs)); font-weight: 700; color: var(--text-3);
  white-space: nowrap; line-height: 1.5;
  margin: 0 0 0 4px;
}
.panel-h .hint b { color: var(--text-2); font-weight: 700; }

/* 上架设置：标题加大（涛爷 09-17），按钮组挪到标题栏右侧
   ★ 权重坑：`.panel-h h2`(0,1,1) 会压过 `.set-h2`(0,1,0) → 必须写 `.panel-h h2.set-h2` */
.panel-h h2.set-h2 { font-size: calc(19.8px * var(--fs)); font-weight: 700; letter-spacing: .3px; }
/* 标题栏右侧按钮组（原「上次保存」位置） */
.setf-act {
  margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none;
}
.setf-act .btn { height: 30px; padding: 0 14px; font-size: calc(13.8px * var(--fs)); }
.setrow {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 10px 12px;
  padding: 8px 16px 14px;
}
/* ★ 09-17 涛爷报障「加了库存后文字重叠」：原来 nowrap，6 组挤一条被压到极窄，
   nowrap 的标签溢出压到邻组上。改为 wrap + 每组保底宽度，挤不下就整齐折行。 */
.set-item { display: flex; align-items: center; gap: 8px; flex: 0 1 auto; min-width: 0; }
/* 所在地组吃满整行剩余宽度（涛爷 09-17：定价框缩小的富余全给右边这片）
   ⚠️ min-width 必须 ≤ 第一行剩余空间，否则整组被挤到第二行独占整行
   ⚠️ 别写 min-width:0 配 flex:1 1 auto —— 会优先换行独占整行 */
.set-item:has(.loc-in) { flex: 1 1 auto; min-width: 286px; }
/* 定价组：收窄（涛爷 09-17 指红框），不吸走富余宽度 */
.set-item:has(.price-in) { flex: 0 0 auto; min-width: 0; }
.set-lab { font-size: calc(13.8px * var(--fs)); color: var(--text-2); white-space: nowrap; flex: none; }
.setrow select, .setrow input[type=number] {
  height: 32px; box-sizing: border-box; padding: 0 7px;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); font-size: calc(13.8px * var(--fs)); font-family: inherit;
  color: var(--text); outline: 0; flex: 0 0 auto; min-width: 0;
}
/* ★ 只让「所在地」组内的下拉显式拉伸；其余 select 按内容宽，
   否则 flex:1 1 auto 会把富余分走 → 所在地的市/区县分不到宽度（09-17 涛爷） */
.setrow .loc-in select { flex: 1 1 0; }
.setrow select:focus, .setrow input[type=number]:focus { border-color: var(--brand); }
/* ★ 库存和定价都用 .price-in 容器 —— 宽度规则必须只作用于定价那个，
   否则库存组被一起撑宽（09-17 踩到：库存组 243px 白白浪费 120px） */
.price-in { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.price-in span { font-size: calc(14.3px * var(--fs)); color: var(--text-2); flex: none; }
/* 定价输入框：缩到涛爷红框大小（09-17 第二轮：186 → 120，省下的全给右边） */
.price-in:has(#setPrice) input { width: 120px; min-width: 0; flex: none; }
#setShip, #setCat, #setShop { min-width: 88px; }
#setStock { width: 88px; min-width: 68px; }
/* 省市区：三个框等宽平分组宽（涛爷 09-17：平均化宽度） */
.loc-in { display: flex; gap: 6px; flex: 1 1 0; min-width: 0; width: 100%; }
.loc-in select { min-width: 0; }
.loc-in #setProv, .loc-in #setCity, .loc-in #setDist { flex: 1 1 0; min-width: 0; width: auto; }
.set-note { font-size: calc(12.1px * var(--fs)); color: var(--text-3); line-height: 1.5; }

/* 队列筛选行：整行平铺填充（09-17 涛爷）
   用 flex-basis 100% 独占一行，比 width:100% 在 flex-wrap 里可靠 */
.sp.filters.fill { flex: 1 1 100%; margin-left: 0; gap: 7px; }
/* ★ 09-18 涛爷：撤掉「文案/队列/同步」三个下拉后，控件**不再拉伸填满整行**
   —— 之前 `flex:1` 会让搜索框/排序撑到 500px+，直接把勾选条挤到第二行。
   现在：搜索框给一个舒服的固定弹性区间，其它控件按内容宽度排；
   勾选条靠 `margin-left:auto` 贴右，且允许收缩（flex-shrink）挤进同一行。 */
.filters.fill input, .filters.fill select { width: auto; min-width: 0; }
.filters.fill > input#fQ { flex: 0 1 210px; }
/* 时间范围在队列页略收窄（两个框各 -10px），给勾选条腾出同排空间 */
.filters.fill .runs-range input[type="datetime-local"] { width: 136px; }
.filters.fill > select { flex: 0 0 auto; }
/* 勾选条：贴右 + 允许内部收缩（窄屏时按钮文字会先压缩，而不是整块掉到下一行） */
.filters.fill .selbar {
  flex: 0 1 auto; min-width: 0;
  gap: 7px; padding: 4px 8px;   /* 比默认略紧，给同排腾空间 */
}
.filters.fill .selbar .selhint {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 170px;
}
.filters.fill .selbar .btn {
  padding: 0 9px;              /* 按钮左右内边距收紧（默认 16px 太占地方） */
  white-space: nowrap;
}
/* ★ 09-18：中等宽度（≤1400px）再挤一点，让「勾选条 + 全部筛选控件」仍能同排。
   背景：勾选条按钮文案恢复到完整版（标记已上架/退回待上架/移出列表）后宽 573px，
   1440 能放下，1280 就差几十像素 → 这里把搜索框与时间框各收窄一点补回来。 */
@media (max-width: 1400px) {
  .filters.fill { gap: 6px; }
  .filters.fill > input#fQ { flex: 0 1 180px; }
  .filters.fill .runs-range input[type="datetime-local"] { width: 128px; }
  .filters.fill .selbar { gap: 6px; }
  .filters.fill .selbar .selhint { max-width: 140px; }
}

.pub-opts {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  font-size: calc(13.8px * var(--fs)); color: var(--text-2); margin-left: 10px;
  flex: 1 1 auto; /* 占满整行，勾选条才能贴到最右（09-17 涛爷：勾选条挪到第一行） */
}
.pub-opts .selbar { margin-left: auto; }
.pub-opts .ck { display: flex; align-items: center; gap: 4px; cursor: pointer; }
.pub-opts .ck input { accent-color: var(--brand); margin: 0; }
.pub-opts input[type=number] {
  height: 30px; box-sizing: border-box; padding: 0 8px; font-size: calc(13.8px * var(--fs));
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text); outline: 0; font-family: inherit;
}
.pub-lim { color: var(--text-3); }

.pubpanel .pubbar {
  height: 8px; margin: 2px 16px 10px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border); overflow: hidden;
}
.pubpanel .pubbar i {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, #FFD24D, #FF9F1A);
  transition: width .35s ease;
}
.pub-count { font-size: calc(13.2px * var(--fs)); color: var(--text-2); }
.pub-now {
  margin: 0 16px 10px; font-size: calc(13.2px * var(--fs)); color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.publog {
  margin: 0 16px 16px; padding: 12px 14px; max-height: 340px; overflow: auto;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--mono); font-size: calc(13.2px * var(--fs)); line-height: 1.65; color: var(--text-2);
  white-space: pre-wrap; word-break: break-all;
}
.publog .lwarn { color: var(--amber); }
.publog .lerr { color: var(--red); }
.publog .lok { color: var(--green); }

.li-state.li-filled { background: var(--amber); color: #fff; }
.tag.t-set { background: var(--brand-soft); color: var(--brand-ink); font-weight: 600; }

/* 抽屉里的「这件货的上架设置」 */
.dsf-wrap { display: flex; flex-wrap: wrap; gap: 12px 16px; margin: 8px 0 10px; }
.dsf { display: flex; flex-direction: column; gap: 5px; }
.dsf label { font-size: calc(13.2px * var(--fs)); color: var(--text-2); }
.dsf input, .dsf select {
  height: 32px; box-sizing: border-box; padding: 0 10px; font-size: calc(13.8px * var(--fs));
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text); outline: 0; font-family: inherit;
}
.dsf input[type=number] { width: 130px; }
.dsf input:focus, .dsf select:focus { border-color: var(--brand); }
.dsf-wide { flex-basis: 100%; }
.dsf-act { display: flex; gap: 8px; margin-top: 2px; }

/* ---------------- 响应式 ---------------- */
/* ★★★ 09-29 七次令：**手机端整体字级收一档**。
   ⚠️ 手机屏幕小、观看距离近，桌面那套字级（还叠了 ×1.1）在手机上显大、首屏有效信息少；
     这里统一用 `--fs` 收档 —— **只影响字号**，间距/布局另见下面 ≤720px 的排版规则。
   ⚠️ 值随真机验收调（375 / 390 / 414 三档都要看）。 */
@media (max-width: 720px) { :root { --fs: .88 } }
@media (max-width: 1040px) { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) {
  .topbar-in { padding: 10px 14px; gap: 10px 12px; flex-wrap: wrap; }
  /* 手机上品牌位收一点，别把「重新登录」挤下去 */
  .brand-logo { width: 34px; height: 34px; }
  .brand-name { font-size: calc(21px * var(--fs)); letter-spacing: 1.6px; }
  /* ★ D05：手机上六句一行塞不下 → 允许换行、字号回落 13.5px */
  /* ★ 09-29 七次令：手机端六句**不参与 `--fs`、直接给手机专用值** ——
     桌面 22.5px 在手机上要占 3 行、太霸道；降到 13.5px 后每行排得下 3 句（2 行收完），
     句间距 14→10、句内间距 11.25→8 同步收紧。 */
  .topbar-in .say { flex-basis: 100%; margin-left: 0; justify-content: flex-start; gap: 6px 10px; flex-wrap: wrap; }
  .topbar-in .say li { font-size: 13.5px; gap: 8px; }
  /* ★ 手机导航：改整行横向滑动（旧版 display:none —— 手机上没有任何板块入口） */
  .nav {
    display: flex; flex-basis: 100%; margin-left: 0; flex-wrap: nowrap;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; padding-bottom: 2px;
  }
  .nav::-webkit-scrollbar { display: none; }
  .nav a { white-space: nowrap; flex: 0 0 auto; padding: 8px 12px; font-size: calc(15.4px * var(--fs)); }
  .topbar { padding-top: 6px; padding-bottom: 6px; }
  .topbar-right { margin-left: auto; }
  .wrap { padding: 16px 14px 48px; }
  .actions { margin-left: 0; width: 100%; }
  .actions .btn { flex: 1; }
  .runs-range input[type="datetime-local"] { width: 140px; }
}

/* ★ 宽表格手机兜底：包一层 .tscroll 横向滑动（table 本体保持语义不变） */
.tscroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tscroll > table { min-width: 660px; }

/* ── 手机端补丁（全局适配 09-24）────────────────────────── */
@media (max-width: 720px) {
  /* 面板头右侧工具条：允许换行，别把整页撑横向 */
  .panel-h .sp { flex-wrap: wrap; max-width: 100%; margin-left: 0; }
  /* 筛选行（搜索/时间范围/下拉/勾选条）与勾选条本身：手机上换行排列 */
  .sp.filters, .sp.filters.fill { flex-wrap: wrap; }
  .selbar { flex-wrap: wrap; max-width: 100%; }
}



/* ---------------- 上架记录板块（2026-09-17） ---------------- */
.pub-lim { color: var(--text-3); font-size: calc(13.2px * var(--fs)); }
.okc { color: var(--green); }
.errc { color: var(--red); }
.tbl { width: 100%; border-collapse: collapse; font-size: calc(13.8px * var(--fs)); margin-top: 6px; }
.tbl th, .tbl td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
.tbl th { color: var(--text-3); font-weight: 700; white-space: nowrap; }
.tbl td:first-child { width: 40px; }

/* 店铺类型徽标（09-17 涛爷需求：鱼小铺 / 普通店铺）
   ★ 09-30 涛爷令：鱼小铺配色对齐**官方个人中心徽标** —— 实测官方主色为青蓝
   `#00D4ED` + 白字（原来是自拟的紫色 `#7C4DFF`，跟官方对不上）。
   鱼小铺等级（如 L4）跟在同一胶囊里，由 accounts.js 拼进文案。 */
.t-fish { background: #00D4ED; color: #fff; font-weight: 700; }


/* ================================================================
   【已停用 · 2026-09-18】客服聊天三栏聚合面板（原方案 A：无头后台 + 网页三栏）
   左＝账号  中＝买家会话  右＝聊天窗格

   ⚠️ 涛爷实测后拍板取消：面板靠轮询抓 DOM 取数据，**切换做不到秒级、体验卡**。
      客服聊天已回到「一账号一独立可见浏览器窗口」模式
      （前端 chat.html / chat.js 已换回窗口版，不再引用下面任何一条选择器）。
   保留这段样式只是为了回退成本最低 —— 回退见
   E:\WB文件\_备份\20260918_面板方案已停用\归档说明.txt
   ================================================================ */
.im-panel { padding-bottom: 12px; }
.imwrap {
  display: grid;
  grid-template-columns: 236px 300px 1fr;
  height: min(640px, calc(100vh - 300px));
  min-height: 430px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
}
.im-col { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.im-accs { background: var(--surface-2); border-right: 1px solid var(--border); }
.im-convs { border-right: 1px solid var(--border); }
.im-col-hd {
  height: 42px; flex: 0 0 42px;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 12px; border-bottom: 1px solid var(--border);
  font-size: calc(14.3px * var(--fs)); font-weight: 600; color: var(--text-2);
  background: var(--surface);
}
.im-col-bd { flex: 1; overflow-y: auto; min-height: 0; }

/* ---- 左：账号 ---- */
.im-acc {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 12px; cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: .14s; position: relative;
}
.im-acc:hover { background: var(--surface); }
.im-acc.on { background: var(--brand-soft); }
.im-acc.on::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--brand);
}
.im-av {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: 50%;
  background: var(--brand-soft); color: var(--brand-ink);
  display: grid; place-items: center; font-weight: 600; font-size: calc(15.4px * var(--fs));
}
.im-av.sm { width: 32px; height: 32px; flex: 0 0 32px; font-size: calc(14.3px * var(--fs)); }
.im-acc-mid { flex: 1; min-width: 0; }
.im-acc-nm {
  display: flex; align-items: center; gap: 5px;
  font-size: calc(14.9px * var(--fs)); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.im-acc-sub {
  font-size: calc(12.7px * var(--fs)); color: var(--text-3); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.im-acc-sub.run { color: var(--green); }
.im-acc-sub.busy { color: var(--amber); }
.im-acc-sub.st-high { color: var(--red); }
.im-acc-tags { margin-top: 4px; display: flex; gap: 4px; flex-wrap: wrap; }
.im-acc-tags .tag { font-size: calc(11.6px * var(--fs)); padding: 0 6px; line-height: 16px; }
.im-mini {
  width: 24px; height: 24px; flex: 0 0 24px; padding: 0;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); cursor: pointer; font-size: calc(13.2px * var(--fs)); line-height: 1;
}
.im-mini:hover { border-color: var(--border-strong); background: var(--surface-2); }

/* ---- 中：会话 ---- */
.im-conv {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 12px; cursor: pointer;
  border-bottom: 1px solid var(--border); transition: .14s;
}
.im-conv:hover { background: var(--surface-2); }
.im-conv.on { background: var(--brand-soft); }
.im-conv.on .im-conv-nm { color: var(--brand-ink); }
.im-conv-mid { flex: 1; min-width: 0; }
.im-conv-l1 { display: flex; align-items: baseline; justify-content: space-between; gap: 6px; }
.im-conv-nm {
  font-size: calc(14.3px * var(--fs)); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.im-conv-tm { font-size: calc(12.1px * var(--fs)); color: var(--text-3); flex: 0 0 auto; }
.im-conv-last {
  font-size: calc(13.2px * var(--fs)); color: var(--text-2); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.im-num {
  background: var(--red); color: #fff; font-size: calc(12.1px * var(--fs)); line-height: 17px;
  border-radius: 9px; padding: 0 6px; flex: 0 0 auto; min-width: 18px; text-align: center;
}

/* ---- 右：聊天 ---- */
.im-chat { background: var(--surface); }
.im-chat-hd {
  height: 42px; flex: 0 0 42px; display: flex; align-items: center;
  padding: 0 16px; border-bottom: 1px solid var(--border);
  font-size: calc(14.9px * var(--fs)); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.im-msgs { flex: 1; overflow-y: auto; padding: 14px 18px; background: var(--surface-2); min-height: 0; }
.im-msg { display: flex; gap: 8px; margin-bottom: 10px; align-items: flex-start; }
.im-msg.self { flex-direction: row-reverse; }
.im-mav {
  width: 32px; height: 32px; flex: 0 0 32px; border-radius: 50%;
  object-fit: cover; background: #E9EBEF;
}
.im-msg.grp { margin-top: -5px; }
.im-msg.grp .im-mav { visibility: hidden; }
.im-mbd { max-width: 74%; display: flex; flex-direction: column; min-width: 0; }
.im-bub {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 8px 11px;
  font-size: calc(14.9px * var(--fs)); line-height: 1.55;
  white-space: pre-wrap; word-break: break-word;
  box-shadow: var(--shadow-1);
}
.im-msg.self .im-bub {
  background: var(--brand); border-color: #F0B800; color: #3A2C00;
}
.im-time { text-align: center; color: var(--text-3); font-size: calc(12.7px * var(--fs)); margin: 14px 0 8px; }
.im-send {
  border-top: 1px solid var(--border); padding: 10px 12px;
  display: flex; gap: 9px; align-items: flex-end; background: var(--surface);
}
.im-send textarea {
  flex: 1; resize: none; border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 9px 11px; outline: none;
  line-height: 1.5; max-height: 120px; min-height: 38px;
}
.im-send textarea:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(255, 196, 0, .15); }
.im-send textarea:disabled { background: var(--surface-2); color: var(--text-3); cursor: not-allowed; }
.im-empty { padding: 48px 24px; text-align: center; color: var(--text-3); font-size: calc(13.8px * var(--fs)); }
.im-empty b { display: block; font-size: calc(15.4px * var(--fs)); color: var(--text-2); font-weight: 600; margin-bottom: 7px; }
.im-empty span { display: block; line-height: 1.75; }
.im-dots { margin-top: 14px; letter-spacing: 4px; color: var(--brand); animation: imPulse 1.2s infinite; }
@keyframes imPulse { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
.im-tip { padding: 10px 2px 0; font-size: calc(13.2px * var(--fs)); color: var(--text-3); }

@media (max-width: 1280px) {
  .imwrap { grid-template-columns: 210px 268px 1fr; }
}
@media (max-width: 1040px) {
  .imwrap { grid-template-columns: 1fr; height: auto; }
  .im-accs, .im-convs { max-height: 210px; border-right: none; border-bottom: 1px solid var(--border); }
  .im-msgs { min-height: 300px; }
}

/* ============================================================
   ★ 2026-09-20：自动上架板块的「盘板」—— 采集商品页 .modes 同款大卡
   ★ 09-21：三个按钮（上架队列 / 上架记录 / 定时发布）→ 一排三列（涛爷：并排一行不换行）
   ============================================================ */
.modes.m2 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.mode .mt { flex: 1; }               /* 撑开，让右侧计数徽标贴到卡片右边 */
.mode .mn {
  flex: none; font-size: calc(13.2px * var(--fs)); font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--text-2); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 999px; padding: 2px 9px;
}
.mode.on .mn { color: var(--brand-ink); background: var(--brand-soft); border-color: #F3E3AE; }
@media (max-width: 860px) { .modes.m2 { grid-template-columns: 1fr; } }


/* ============================================================
   ★ 2026-09-22 补回：账号管理 / 客服聊天 共用样式
   来源：_备份/20260918_123832/code/app/web/static/app.css（原文沿用）
   原因：当日删除 /chat2 板块 CSS 时误删了这些跨板块共享规则，
         导致账号管理页的账号卡片、信息面板、通知框全部丢失样式。
   ============================================================ */

.phead-act { margin-left: auto; display: flex; gap: 9px; align-items: center; }


.accgrid {
  /* ★ 09-29 手机自适应：最小轨用 min(342px,100%) 兜底 —— 窄屏（如 SE 375）面板内容宽仅
     305px，固定 342px 的最小轨会撑出 2px 横向溢出（accounts/chat 实测 docW=377）。
     min() 在宽屏恒取 342px（桌面零变化），窄屏自动收到容器宽，消灭溢出。 */
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(342px, 100%), 1fr));
  gap: 14px;
}

.acard {
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); padding: 15px 16px 14px;
  display: flex; flex-direction: column; gap: 12px;
  transition: .16s;
}

.acard:hover { border-color: var(--border-strong); box-shadow: var(--shadow-1); }

.acard.on { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(255, 196, 0, .16); }

.acard.busy { background: var(--surface-2); }

.avatar {
  width: 38px; height: 38px; border-radius: 11px; flex: none;
  background: var(--brand-soft); color: var(--brand-ink);
  display: grid; place-items: center; font-size: calc(17.6px * var(--fs)); font-weight: 600;
  overflow: hidden;
}

/* ★ 2026-09-28：卡片头像 = 闲鱼店铺真实头像（后端同步来的 URL）；加载失败回落首字 */
.avatar .av-img { width: 100%; height: 100%; object-fit: cover; display: block; }

.acard.on .avatar { background: var(--brand); color: #1A1D23; }

.amain { min-width: 0; flex: 1; }

.aname { display: flex; align-items: center; gap: 6px; font-size: calc(16px * var(--fs)); font-weight: 600; flex-wrap: wrap; }

.asub {
  font-size: calc(13.2px * var(--fs)); color: var(--text-2); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.abusy {
  font-size: calc(13.2px * var(--fs)); color: var(--amber); background: var(--amber-soft);
  border-radius: var(--r-sm); padding: 7px 9px; line-height: 1.55; word-break: break-all;
}


.ameta { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: calc(13.8px * var(--fs)); }

.ameta .mk { color: var(--text-3); white-space: nowrap; }

.ameta .mv { font-weight: 700; word-break: break-all; text-align: right; font-variant-numeric: tabular-nums; }


.aacts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: auto; padding-top: 2px; }


.info-panel { background: linear-gradient(180deg, #FFFDF4, var(--surface)); }

.info-h { display: flex; align-items: center; gap: 8px; font-size: calc(14.9px * var(--fs)); margin-bottom: 14px; }

.info-ico { font-size: calc(16.5px * var(--fs)); }

.info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(248px, 1fr)); gap: 16px; }

.info-item p { margin: 8px 0 0; font-size: calc(13.8px * var(--fs)); color: var(--text-2); line-height: 1.7; }

.info-item code {
  font-family: var(--mono); font-size: calc(12.7px * var(--fs)); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; word-break: break-all;
}


.hint { font-size: calc(13.2px * var(--fs)); color: var(--text-3); margin-top: 8px; line-height: 1.6; }

.notice {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 12px 16px; font-size: calc(13.8px * var(--fs)); line-height: 1.75; color: var(--text-2);
}

.notice b { display: block; color: var(--text); margin: 4px 0 6px; font-size: calc(13.8px * var(--fs)); }

.notice b:first-child { margin-top: 0; }

.notice ul { margin: 0 0 12px; padding-left: 18px; }

.notice ul:last-child { margin-bottom: 0; }

.notice li { margin: 3px 0; }


/* ══════════════════════════════════════════════════════════════════
   「排期 / 盯守」紧凑列表（.sr-*）—— 2026-09-29 涛爷令后由**两页共用**：
     · 商品管理  →「售罄自动上架」清单
     · 自动上架  →「定时发布」→「手编新品排期」
   原先写死在 itemmgr.html 的 <style> 里；搬到此处共享，别再各页复制一份。
   ⚠️ 带 display 的类必须补 [hidden]{display:none}（作者样式会盖掉 hidden 属性）。
   ══════════════════════════════════════════════════════════════════ */
.sr-list { display: flex; flex-direction: column; gap: 8px; }
.sr-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px;
          border: 1px solid #ececf0; border-radius: 10px; background: #fff; }
.sr-main { flex: 1 1 auto; min-width: 0; }
.sr-tt { font-size: 13.5px; color: #14161a; overflow: hidden;
         text-overflow: ellipsis; white-space: nowrap; }
.sr-meta { font-size: 12px; color: #8a8a93; margin-top: 3px; display: flex;
           gap: 10px; flex-wrap: wrap; }
.sr-ops { flex: 0 0 auto; display: flex; gap: 6px; }
.sr-st { flex: 0 0 auto; font-size: 11.5px; padding: 2px 8px; border-radius: 999px;
         background: #f0f0f4; color: #5f5f68; }
.sr-st.pending { background: #fff2cc; color: #8a6d00; }
.sr-st.running { background: #e2eefb; color: #1b5f9e; }
.sr-st.failed { background: #fde8e6; color: #b02a1f; }
.sr-st.on { background: #e3f4e8; color: #1c7a3e; }
.sr-st.off { background: #f0f0f4; color: #8a8a93; }

/* 子分区外框（自动上架「定时发布」里的手编新品排期）—— 与下方卡片区隔开；空则整块隐藏 */
.sr-wrap { margin: 0 0 16px; padding: 12px 13px; border: 1px solid #f0f0f4;
           border-radius: 12px; background: #fcfcfd; }
.sr-wrap[hidden] { display: none; }
.sr-head { display: flex; align-items: center; gap: 8px; margin: 0 0 9px;
           font-size: 13px; color: #5f5f68; }
.sr-head b { font-weight: 600; color: #14161a; }
.sr-head .hint { font-weight: 400; }

/* ══════════════════════════════════════════════════════════════════
   ★★★ 浮雕透视皮肤 v2（2026-10-10 涛爷选定「方案③ 浮雕透视」）
   ──────────────────────────────────────────────────────────────────
   3D 机制：**多层长投影造"浮起高度" + hover 真实 rotateX 透视**
   —— 白底上阴影才可见，所以立体感靠「实体边 + 斜面高光 + 层次」撑，
      不是靠深底（深底上多层投影几乎不可见，只显得脏）。

   ⚠️⚠️ 本块是**纯追加的覆盖层**，不改任何既有规则 ⇒ 回滚只需整块删除。
   ⚠️⚠️ 只碰 box-shadow / transform / background / border-color；
          **绝不碰 display** —— `.run`（全站隐藏，只有 .run.on 显示）、
          `.alarm`（掉线红条）、`.btn[hidden]{display:none}` 都是 display 雷区。
   ⚠️ hover 的 rotateX 必须配 `transform-style: preserve-3d`（已给 .panel），
      否则子元素会被压平、透视失效。
   ⚠️ `perspective` 只挂在容器上，**不给 .panel 自身**（给自己会双重透视导致形变过猛）。

   ★ v2 修正（涛爷「只优化了按钮，其他都没优化」）：覆盖面从 8 个类扩到**全站 9 大板块**
     ① 顶栏导航 .nav a  ② 模式卡 .mode  ③ 面板 .panel + 面板头 .panel-h
     ④ 输入控件 .field/.filters input|select  ⑤ 进度条 .bar i
     ⑥ 商品卡 .pcard（.ph 图片框 / .price / .tag）
     ⑧ 分页 .pager  ⑨ 抽屉 .drawer + 弹窗 .cmodal/.rmodal + toast
   ══════════════════════════════════════════════════════════════════ */

/* ══════════ ① 顶栏导航胶囊 ══════════ */
.nav a {
  box-shadow: 0 1.5px 0 rgba(80, 58, 10, .10), 0 4px 10px -6px rgba(80, 58, 10, .30);
  transition: transform .16s cubic-bezier(.2, .85, .3, 1), box-shadow .16s;
}
.nav a:hover:not(.on):not(.dis) { transform: translateY(-2px); box-shadow: 0 3px 0 rgba(80, 58, 10, .12), 0 9px 16px -7px rgba(80, 58, 10, .42); }
.nav a.on {
  box-shadow: 0 5px 0 #BE8C00, 0 12px 20px -8px rgba(180, 130, 0, .58), inset 0 1.5px 0 rgba(255, 255, 255, .78);
}
.nav a.on:hover { transform: translateY(-2px) rotateX(6deg); box-shadow: 0 8px 0 #BE8C00, 0 18px 26px -8px rgba(180, 130, 0, .64), inset 0 1.5px 0 rgba(255, 255, 255, .78); }

/* ══════════ ② 模式卡（搜索/整店/收藏/链接）：4 张立体卡 ══════════ */
.mode {
  box-shadow:
    0 1px 0 rgba(255, 255, 255, .9) inset,
    0 10px 18px -10px rgba(80, 58, 10, .32),
    0 2px 4px -2px rgba(80, 58, 10, .18);
  transition: transform .2s cubic-bezier(.2, .85, .3, 1), box-shadow .2s;
}
.mode:hover { transform: translateY(-4px) rotateX(5deg); box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 20px 28px -13px rgba(80,58,10,.44), 0 5px 9px -4px rgba(80,58,10,.2); }
/*⚠️ `.mode.on` 原本靠 `box-shadow: 0 0 0 1px inset` 做黄描边 ⇒ 必须保留 inset 那一层，
否则「选中态 = 黄描边」这一既有语义会消失（全站「选中只认 --brand」的铁律）。 */
.mode.on {
  box-shadow:
    0 0 0 1px var(--brand) inset,
    0 4px 0 #BE8C00,
    0 14px 24px -10px rgba(180, 130, 0, .5),
    inset 0 1.5px 0 rgba(255, 255, 255, .7);
}
.mode.on:hover { transform: translateY(-4px) rotateX(5deg); }
/* 图标：给一个黄底立体方块 */
.mode .mi {
  width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center;
  background: linear-gradient(158deg, #FFF0B8, var(--brand) 62%, #E0A400);
  box-shadow: 0 3px 0 #C89400, 0 7px 13px -5px rgba(180, 130, 0, .55), inset 0 1.5px 0 rgba(255, 255, 255, .85);
}

/* ══════════ ③ 面板 + 面板头 ══════════ */
.panel {
  box-shadow:
    0 1px 0 rgba(255, 255, 255, .9) inset,
    0 12px 20px -10px rgba(80, 58, 10, .34),
    0 2px 4px -2px rgba(80, 58, 10, .20);
  transition: box-shadow .28s cubic-bezier(.2, .85, .3, 1), transform .28s cubic-bezier(.2, .85, .3, 1);
  transform-style: preserve-3d;
}
.panel:hover {
  box-shadow:
    0 1px 0 rgba(255, 255, 255, .9) inset,
    0 26px 34px -14px rgba(80, 58, 10, .45),
    0 6px 10px -4px rgba(80, 58, 10, .24);
}
/* 面板头：底部一条压痕，让标题区与内容区分层 */
.panel-h {
  padding-bottom: 11px;
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .9);
}
.panel-h h2 { letter-spacing: -.2px; }

/* ══════════ ④ 输入控件：内凹槽 + 聚焦时升起 ══════════ */
.field input, .field select,
.filters input, .filters select,
.rl-body select {
  box-shadow:
    inset 0 2px 3px rgba(90, 70, 20, .10),
    0 1px 0 rgba(255, 255, 255, .8);
  transition: box-shadow .18s, transform .18s;
}
/* ⚠️ 各页:focus 原有写法不同（有的只改 border-color，有的加 3px 环）⇒
   这里统一补「升起 + 品牌环」，但**保留原 focus 的 inset 阴影**避免把内凹感弄丢。 */
.field input:focus, .field select:focus,
.filters input:focus, .filters select:focus {
  box-shadow:
    0 0 0 3px rgba(255, 196, 0, .22),
    inset 0 1px 2px rgba(90, 70, 20, .08),
    0 3px 8px -4px rgba(180, 130, 0, .40);
}
/* 筛选区输入框（较短）：给一点实体边，与长输入框区分 */
.filters input, .filters select { box-shadow: inset 0 2px 3px rgba(90,70,20,.10), 0 1px 0 rgba(255,255,255,.8), 0 2px 0 #F0F2F5; }

/* ══════════ ⑤ 进度条：立体凹槽 + 立体填充 ══════════ */
.bar {
  box-shadow: inset 0 2px 4px rgba(90, 70, 20, .18), inset 0 -1px 0 rgba(255, 255, 255, .7);
  border-radius: 5px;
}
.bar i {
  background: linear-gradient(180deg, #FFDE6B, var(--brand) 55%, #EBA700);
  box-shadow: 0 0 10px rgba(255, 196, 0, .65), inset 0 1.5px 0 rgba(255, 255, 255, .8);
  border-radius: 3px;
}

/* ══════════ ⑥ 商品卡（含图片框与价格）══════════ */
.pcard {
  box-shadow:
    0 10px 18px -10px rgba(80, 58, 10, .32),
    0 2px 4px -2px rgba(80, 58, 10, .18);
  /* ⚠️ 这里原来写的是 `transform: perspective(900px)` —— **必须删掉**。
     它虽然看起来是「单位变换」，但只要 transform 属性的值不是 none，
     浏览器就会为该元素生成**层叠上下文**，`.pick` 的 z-index:5 随之失效。
     （实测：_probe_fix3d.py 里 translateZ/will-change 都救不回来，只有让
       .pcard 的 transform 彻底回到 none / 纯 2D 才通。） */
  transition: box-shadow .28s cubic-bezier(.2, .85, .3, 1), transform .28s cubic-bezier(.2, .85, .3, 1);
}
/* 🚨🚨🚨 10-12 真·根因修复（涛爷报障「无法单个勾选，只能全选」）
   ────────────────────────────────────────────────────────────────
   实测铁证（tools/_probe_rotatex2.py 对照实验，同一页只改 rotateX 一项）：
       默认 rotateX(7deg)  → hover 后 rect=[48.7,473.6,33.4,31.9]
                              按 rect **中心** elementFromPoint = `DIV.grid` ❌
       rotateX(0deg)      → hover 后 rect=[46,465,34,34]
                              按 rect 中心 elementFromPoint = `LABEL.pick` ✅
       transform:none     → 同上 ✅
   翻译成人话：**`.pcard` 一旦带 3D transform（perspective / rotateX）就会生成
   「层叠上下文」，子元素的 `z-index:5` 语义从「绘制顺序」变成「3D 深度」**，
   `.pick` 被压回 `.grid` 之下 ⇒ **rect 量到的位置点下去是空的**。
   ⇒ 涛爷的观感 = 「鼠标移到左上角点一下，什么都没发生；只有工具条上的全选框有效」。

   ⚠️ 这就是为什么「提高 z-index」「加 translateZ」「加 will-change」全都无效
      （tools/_probe_fix3d.py 实测：translateZ(30px) / +will-change 均仍 FAIL）
      —— 问题不在深度值，在**父级有 3D transform** 这件事本身。

   ✅ 正解：**卡片本体只保留纯 2D 的 `translateY`（不改变层叠上下文）**，
      3D 立体旋转**下沉到图片区 `.ph`**——那里面没有可点的元素，
      视觉立体感一点不少，勾选框命中区从此稳定。 */
/* 🚨🚨🚨 10-13 真·根因修复（涛爷报障「鼠标放上去不断闪烁」+「要点很多次才成功」）
   ────────────────────────────────────────────────────────────────────────────
   上面那套 `rotateX(7deg)` 是 10-10「浮雕透视」皮肤加的，**它是闪烁的真凶**：
     光标在图片区上**进出**时（哪怕只是移动 1px），`.ph` 就整体做 3D 透视形变 +
     阴影从 `0 10px 18px` 跳到 `0 24px 32px`，形变量 + 阴影同时突变
     ⇒ 视觉上就是「不断闪烁」。而且形变会让 `.ph` 参与 3D 合成，
     **移动端/低性能机器上掉帧**，点击落点与视觉反馈错位 ⇒ 「要点好几次」。

   ✅ 正解（本轮）：**卡片上彻底不做任何 3D 形变，也不做位移。**
     · hover 表达「选中/可点」只靠**边框色 + 阴影**，**零几何变化**；
     · 想保留立体感就把立体感做成**静态**的（永远在，不随 hover 变），
       由阴影层次承担，而不是靠动画位移。
     · 这样：① 不会有闪烁（没有任何会跳的量）
           ② 点击热区永远等于视觉位置（要点一次就中）
           ③ `.pcard` 的 transform 恒为 none ⇒ 不生成层叠上下文
              ⇒ `.pick` 的 z-index:5 永久有效（10-12 那个坑不会复发）。

   ⚠️ 以后**不要**往 `.pcard` / `.mode` / `.nav a` 这些「内部有可点子元素」的
      容器上加 hover transform（translateY / rotateX / scale 一律不行）。
      需要动效就动**叶子元素**（图片本身可以 scale，但不能带阴影跳变）。 */
.pcard:hover {
  transform: none;                      /* 🔴 不位移 —— 位移就是闪烁与落点偏移的来源 */
  box-shadow:
    0 12px 22px -12px rgba(80, 58, 10, .34),
    0 3px 6px -3px rgba(80, 58, 10, .20);
  border-color: var(--brand);
}
/* 图片区：只做**静态**的轻微内凹，**不做 hover 形变**。
   （原本这里有 `perspective(900px) rotateX(7deg)`，10-13 移除，理由见上。） */
.pcard .ph { transition: none; }
/* 图片框：内凹槽（图片像嵌进卡里） */
.pcard .ph {
  box-shadow: inset 0 2px 5px rgba(90, 70, 20, .16);
  border-bottom: 1px solid var(--border);
}
/* 🚨🚨 BUG 修复（16:47）：上面那条 inset 让 .pick 勾选框点不动。
   根因：`.pick` 是 `.ph` 的子元素且 `position:absolute; z-index:3`，
   而 **box-shadow:inset 绘制在内容之上**（inset 阴影在背景之上、内容之下，
   但会盖住 padding 区的命中）—— 阴影铺满整个 .ph，把左上角 26px 的勾选框
   命中区压掉了 ⇒ 点「全选」没反应。
   ✅ 正解：**内凹不能加在含交互子元素的 .ph 上**，改加到无交互的 `.pcard .ph` 内部裁切，
      并给 `.pick` 显式抬层级 + 自带不透明底。 */
.pcard .pick { z-index: 5; }
.pcard .ph { box-shadow: none; }          /* 撤掉会压命中的 inset */
.pcard .ph img { border-radius: var(--r-lg) var(--r-lg) 0 0; }
/* 内凹感改由「图片上方压一层渐变」实现，不影响命中 */
.pcard .ph::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 22px;
  background: linear-gradient(180deg, rgba(90, 70, 20, .16), transparent);
  pointer-events: none;                /* 🔴 关键：装饰层必须让开点击 */
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
/* 骨架屏也要有凹槽，别显得是贴纸 */
.sk-card {
  box-shadow: inset 0 2px 5px rgba(90, 70, 20, .12), 0 10px 18px -10px rgba(80, 58, 10, .3);
}
/* 价格：像嵌在标签上的立体小块 */
.price {
  display: inline-block;
  background: linear-gradient(180deg, #FFF6D9, #FFEBB0);
  border-radius: 6px; padding: 1px 7px;
  box-shadow: 0 2px 0 #EBD07A, 0 5px 9px -4px rgba(180, 130, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .85);
}
/* 标签 / 徽标：微浮雕 */
.tag {
  box-shadow: 0 1.5px 3px -1px rgba(80, 58, 10, .28), inset 0 1px 0 rgba(255, 255, 255, .6);
}

/* ══════════ ⑦ 卡片行（已下线采集记录行） ══════════ */

/* 日志框：内凹 + 顶部高光，像机器面板上的显示屏 */
.log, .rl-log {
  box-shadow: inset 0 3px 7px rgba(0, 0, 0, .55), inset 0 -1px 0 rgba(255, 255, 255, .05);
}
/* 状态点：给个发光球 */
.dot { box-shadow: 0 0 6px rgba(255, 196, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .6); }
.dot.done { box-shadow: 0 0 6px rgba(21, 128, 61, .55), inset 0 1px 0 rgba(255, 255, 255, .6); }
.dot.err { box-shadow: 0 0 6px rgba(220, 38, 38, .55), inset 0 1px 0 rgba(255, 255, 255, .6); }

/* ══════════ ⑧ 分页 ══════════ */
.pager {
  box-shadow: 0 8px 16px -10px rgba(80, 58, 10, .28);
}

/* ══════════ ⑨ 抽屉 / 弹窗 / toast ══════════ */
/* 🔴🔴🔴 10-14 涛爷报障「右边有一整竖黑影，之前都没有」——**真凶终于抓到**
   （前两次都判错了：先怀疑滚动条槽、再怀疑 body 渐变，全都排除掉了；
     靠像素采样 `rgb(132,113,71)` 才定位到这一层）。

   ⚙️ 机理：`.drawer` 靠 `transform: translateX(102%)` 移出视口「收起」，
      但 `box-shadow` **跟着 transform 一起走**（shadow 画在元素盒外侧，
      元素往右挪 102%，那圈 44px 模糊的暖黄影就跟着挪到屏幕右缘内侧）——
      抽屉本体在 x=1081（视口外，看不见），它的 `-16px 0 44px` 影却正好
      横扫过 1065~1035，形成一条 30px 宽、由深到浅衰减的竖条。
      配置页有 **9 个抽屉**，9 份影叠在一起 ⇒ 更明显。
   🔴 为什么之前没这毛病：老 `.drawer`（827 行）的影是 `-12px 0 40px rgba(20,22,26,.14)`，
      又窄又淡；B 主题层（1996 行）把它加重成 `-16px 0 44px rgba(80,58,10,.26)`
      —— 宽度 +4、模糊 +4、**不透明度 ×1.86**、颜色由冷灰转深褐 ⇒ 突然显形。
   ✅ 正解：**收起态（默认）不给影**，只有 `.on`（真的拉开）才上影。
      ⛔ 别去删 1997 行那条影子 —— 那是抽屉打开时的设计，要保留。
      ⛔ 也别改 `translateX(102%)` —— 收起动画靠它。
      ⚠️ `visibility:hidden` + `transition` 更稳：收起后彻底不参与绘制，
         顺带干掉「Tab 键能 focus 到屏外抽屉里的按钮」这个 a11y 毛病。
         （用 `visibility` 而不是 `display:none` 是为了不打断 transform 过渡。） */
.drawer {
  box-shadow: none;
  visibility: hidden;
  transition: transform .28s cubic-bezier(.4, 0, .2, 1), visibility 0s linear .28s;
}
.drawer.on {
  box-shadow: -16px 0 44px rgba(80, 58, 10, .26), inset 1px 0 0 rgba(255, 255, 255, .8);
  visibility: visible;
  /* 🔴 visibility 必须在 transform 过渡**开始时**就切回来，否则抽屉会
     「停在门外」0.28s 才出现 ⇒ 看着像点了没反应。0s 不延迟是关键。 */
  transition: transform .28s cubic-bezier(.4, 0, .2, 1), visibility 0s;
}
.drawer .dhead, .rmodal-h {
  box-shadow: 0 1px 0 rgba(255, 255, 255, .9), 0 3px 8px -5px rgba(80, 58, 10, .28);
}
.cmodal, .rmodal {
  box-shadow:
    0 1px 0 rgba(255, 255, 255, .9) inset,
    0 30px 60px -18px rgba(80, 58, 10, .48),
    0 8px 20px -10px rgba(80, 58, 10, .3);
}
.toast {
  box-shadow: 0 10px 24px -8px rgba(80, 58, 10, .45), inset 0 1px 0 rgba(255, 255, 255, .18);
}

/* ══════════ ⑩ 品牌 / 顶栏 / 勾选条 ══════════ */

.brand-mark {
  box-shadow: 0 4px 0 #BE8C00, 0 9px 15px -6px rgba(180, 130, 0, .6), inset 0 1.5px 0 rgba(255, 255, 255, .85);
}
.brand-logo {
  box-shadow: 0 6px 0 rgba(180, 130, 0, .28), 0 14px 26px -10px rgba(160, 112, 20, .42), inset 0 2px 0 rgba(255, 255, 255, .5);
}
.topbar { box-shadow: 0 4px 12px -6px rgba(80, 58, 10, .24); }
/* 勾选条：浮起的工具条 */
.selbar {
  box-shadow: 0 6px 14px -8px rgba(80, 58, 10, .34), inset 0 1px 0 rgba(255, 255, 255, .85);
  border-radius: var(--r-sm);
}

/* ══════════ ⑪ 按钮（实体下沉行程）══════════ */
.btn {
  box-shadow: 0 5px 0 #BE8C00, 0 12px 20px -8px rgba(180, 130, 0, .58), inset 0 1.5px 0 rgba(255, 255, 255, .78);
  transition: transform .15s cubic-bezier(.2, .85, .3, 1), box-shadow .15s;
}
.btn:hover:not(:disabled) {
  transform: translateY(-2px) rotateX(6deg);
  box-shadow: 0 8px 0 #BE8C00, 0 18px 26px -8px rgba(180, 130, 0, .64), inset 0 1.5px 0 rgba(255, 255, 255, .78);
}
/* ⚠️ :active 必须排:hover 之后 —— 同权重下后写的赢，否则按下无反馈 */
.btn:active:not(:disabled) { transform: translateY(5px); box-shadow: 0 0 0 #BE8C00, inset 0 2px 6px rgba(150, 110, 0, .4); }
/* ⚠️ danger / warn 的实体底边必须跟着换色，否则红/琥珀按钮下面拖一条黄边 */
.btn.danger { box-shadow: 0 5px 0 #991B1B, 0 12px 20px -8px rgba(153, 27, 27, .5), inset 0 1.5px 0 rgba(255, 255, 255, .4); }
.btn.danger:hover:not(:disabled) { box-shadow: 0 8px 0 #991B1B, 0 18px 26px -8px rgba(153, 27, 27, .56), inset 0 1.5px 0 rgba(255, 255, 255, .4); }
.btn.danger:active:not(:disabled) { transform: translateY(5px); box-shadow: 0 0 0 #991B1B, inset 0 2px 6px rgba(120, 15, 15, .45); }
.btn.warn { box-shadow: 0 5px 0 #B45309, 0 12px 20px -8px rgba(180, 83, 9, .5), inset 0 1.5px 0 rgba(255, 255, 255, .6); }
.btn.warn:hover:not(:disabled) { box-shadow: 0 8px 0 #B45309, 0 18px 26px -8px rgba(180, 83, 9, .56), inset 0 1.5px 0 rgba(255, 255, 255, .6); }
.btn.warn:active:not(:disabled) { transform: translateY(5px); box-shadow: 0 0 0 #B45309, inset 0 2px 6px rgba(150, 80, 9, .4); }
/* ghost 变体：无品牌底 ⇒ 实体边用中性灰，别拖黄边 */
.btn.ghost {
  background: var(--surface); color: var(--text-2); border: 1.5px solid var(--border-strong);
  box-shadow: 0 4px 0 #DFE3E9, 0 10px 16px -9px rgba(80, 60, 15, .4), inset 0 1px 0 rgba(255, 255, 255, .9);
}
.btn.ghost:hover:not(:disabled) {
  color: var(--brand-ink); border-color: #F2CE6A;
  box-shadow: 0 7px 0 #DFE3E9, 0 16px 22px -9px rgba(80, 60, 15, .46), inset 0 1px 0 rgba(255, 255, 255, .9);
}
.btn.ghost:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 0 0 #DFE3E9, inset 0 2px 5px rgba(90, 70, 20, .16); }
/* ⚠️ focus-visible 也要带实体边，否则键盘聚焦时浮雕突然塌掉（焦点态是可达性硬指标） */
.btn:focus-visible {
  box-shadow: 0 0 0 3px rgba(255, 196, 0, .5), 0 5px 0 #BE8C00, 0 12px 20px -8px rgba(180, 130, 0, .58), inset 0 1.5px 0 rgba(255, 255, 255, .78);
}
.btn.ghost:focus-visible {
  box-shadow: 0 0 0 3px rgba(255, 196, 0, .5), 0 4px 0 #DFE3E9, 0 10px 16px -9px rgba(80, 60, 15, .4), inset 0 1px 0 rgba(255, 255, 255, .9);
}

/* ══════════ 降级：触屏 / 减少动效 ══════════ */
@media (hover: none) {
  .nav a:hover:not(.on):not(.dis), .mode:hover, .mode.on:hover, .pcard:hover, .btn:hover:not(:disabled), .btn:active:not(:disabled) { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .panel, .pcard, .btn, .tag, .brand-mark, .brand-logo, .nav a, .mode, .sk-card { transition: none !important; }
  .pcard:hover, .btn:hover:not(:disabled), .btn:active:not(:disabled),
  .nav a:hover:not(.on):not(.dis), .mode:hover, .mode.on:hover { transform: none !important; }
}

/* ══════════════════════════════════════════════════════════════════
   ★★★ 浮雕透视皮肤 v2 —— 到此结束
   ──────────────────────────────────────────────────────────────────
   🔴 2026-10-21 加：两个守卫（relief3d / click_reach）原先把「皮肤块」
      定义成「从上面那个起始标记到**文件尾**」。但皮肤块到这里就完了，
      10-13 之后又往后追加了 2500 行无关 CSS（悬停稳定化 / 弹窗 / 退款
      时间线 / 品牌色 / 表格视图…）⇒ 全被当成皮肤块检查，
      常年红着 16 条假红，把真红淹了。⇒ 守卫改成读**这个**标记。
   ⛔ 新增 CSS 一律写在此标记**之后**，不要再往皮肤块里塞。
   ══════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════
   🚨🚨🚨 10-13 鼠标设备的「悬停稳定化」（涛爷报障「鼠标放上去不断闪烁」）
   ────────────────────────────────────────────────────────────────
   上面那两个降级块只覆盖**触屏**（hover:none）与**减弱动效**用户。
   涛爷是**鼠标**设备 ⇒ 前面那套 hover `translateY / rotateX` 照旧生效：

     `.pcard:hover`          → translateY(-6px) + 阴影 10px→24px 跳变
     `.pcard:hover .ph`      → perspective(900px) rotateX(7deg)
     `.mode:hover`           → translateY(-4px) rotateX(5deg)
     `.nav a.on:hover`       → translateY(-2px) rotateX(6deg)
     `.nav a:hover:not(.on)` → translateY(-2px)

   光标在元素上**进出一次**（哪怕只移 1px）就同时触发「位移 + 阴影跳变 + 3D 形变」
   ⇒ 观感 = **不断闪烁**；且 3D 形变让元素进入独立合成层，鼠标事件与命中判定
   出现帧级滞后 ⇒ **点一下没中、要连点好几次**（涛爷：加载商品、批量复制都是这样）。

   ✅ 本块的处置（**全站一次性兜底**，不逐个类去改原规则，便于回滚）：
     ① 所有 hover 的 **transform 一律归零** —— 位移/旋转/缩放全部取消，
        hover 只靠 **box-shadow + border-color** 表达，不产生任何几何抖动；
     ② 关掉相关 transition —— 没有过渡就不会有「跳」的中间帧；
     ③ `.btn` 的 **:active 下压效果保留**（那是主动反馈，只在按下瞬间发生、
        不涉及 hover 进出，不会闪），但去掉它的 translateY 位移，
        改用阴影塌陷表达「按下去了」。
     ⚠️ 刻意**不碰 display / [hidden] / .run / .alarm**（display 是雷区）。
   ══════════════════════════════════════════════════════════════ */
@media (hover: hover) {
  .pcard:hover,
  .pcard:hover .ph,
  .pcard:hover .ph img,
  .mode:hover,
  .mode.on:hover,
  .nav a:hover:not(.on):not(.dis),
  .nav a.on:hover { transform: none !important; }
  .pcard, .pcard .ph, .pcard .ph img, .mode, .nav a { transition: box-shadow .16s linear, border-color .16s linear, background-color .16s linear !important; }

  /* 🚨🚨🚨 10-13 补漏（涛爷报障「所有按钮鼠标放上去不断闪烁、点不中」）：
     ⚠️ 上一版兜底块**漏了 `.btn`** —— 于是 `app.css:1980` 的
        `.btn:hover:not(:disabled){ transform: translateY(-2px) rotateX(6deg) }`
        在鼠标设备上照旧生效，这就是「全站每一个按钮都闪」的真凶。

     为什么它比 .pcard 致命得多（**必读，别再当成小装饰改回去**）：
       光标压在按钮下缘 → hover 生效 → 按钮整体上移 2px → 光标落到按钮**之外**
       → hover 失效 → transform 撤掉 → 按钮落回 → 又盖回光标 → **无限自激闪烁**。
       再叠上 `rotateX()` 生成独立合成层 ⇒ 命中判定帧级滞后 ⇒ **点一下没中、
       要连点好几次**（涛爷：加载商品、批量复制勾选都是这样）。

     ✅ 处置：hover 的 transform 一律归零，按钮 hover 只靠 **box-shadow + 底边颜色**
        表达「抬起来了」——零几何位移，不可能抖。
        ⚠️ transition 里也必须剔掉 transform，否则归零本身仍带一段动画。
        ⚠️ :active 的按压反馈在下面单独处理（阴影塌陷，不用位移）。 */
  .btn:hover:not(:disabled),
  .btn:hover:not(:disabled):active { transform: none !important; }
  .btn, .btn.ghost, .btn.danger, .btn.warn {
    transition: box-shadow .15s linear, border-color .15s linear, background-color .15s linear, color .15s linear !important;
  }
}
/* 按压反馈：不用位移，用阴影塌陷（位移会带动周围布局，且在鼠标上易与 hover 叠加成抖动） */
.btn:active:not(:disabled) { transform: none !important; }


/* ══════════════════════════════════════════════════════════════
   ★ 10-12 批量复制商品 —— 逐字段对齐易店原版弹窗（涛爷截图核对）
   ⚠️ 字段名 / 顺序 / 说明文案 **一字不改**，不新增任何自定义选项：
      ① 商品数量（**只读**，显示已勾选件数）
      ② 发布到店铺（**可多选**）
      ③ 复制发货配置
      ④ 鱼小铺（易店的推广分销位；本地无此能力 ⇒ 如实灰掉）
      ⑤ 发布方式：直接发布 / 存为草稿（**默认直接发布**）
   ⚠️ 类名一律带 `bc-` 前缀 —— 记忆铁律：新组件必须先 grep 全站，
      撞上 .run / .alarm / .done 这类已有语义的类名会静默串台。

   🔴🔴 z-index 必须**高于本页所有其它弹窗**（涛爷报障「点了没反应」根因之一）：
      本页有 4 个 .cmodal(64) + 2 个 rmask 子窗(66)，而 .rmask/.rmodal
      默认只有 55/56 ⇒ 批量复制弹窗会被它们压住，elementFromPoint 命中的
      是别的层 ⇒ Playwright 报「element is visible, enabled」却点不动。 */
#bcMask { z-index: 68; }
#bcModal { z-index: 69; width: min(520px, 94vw); }
/* ⚠️ [hidden] 会被任何 display 规则盖掉 → 显式兜底 */
#bcModal [hidden] { display: none; }
#bcModal .btn[hidden] { display: none; }

.bc-body { display: flex; flex-direction: column; }

/* ① 商品数量：易店那里就是**只读**的已选件数（不是输入框） */
.bc-count {
  font-size: calc(14.3px * var(--fs)); color: var(--text-2);
  padding: 2px 0 4px;
}
.bc-count b {
  color: var(--brand); font-weight: 700; margin: 0 2px;
  font-size: calc(15.4px * var(--fs)); font-variant-numeric: tabular-nums;
}

/* 一行表单：左标签右控件（每行之间一条分隔线，与截图一致） */
.bc-row {
  display: grid; grid-template-columns: 88px 1fr; gap: 14px;
  align-items: start; padding: 15px 0;
  border-top: 1px solid var(--border);
}
.bc-row:first-of-type { border-top: none; padding-top: 10px; }
.bc-row > .bc-lab {
  font-size: calc(14.3px * var(--fs)); color: var(--text);
  text-align: right; line-height: 1.5; padding-top: 6px;
}
.bc-row > .bc-ctl { min-width: 0; }

/* ② 发布到店铺：**自定义下拉框**（一个框 + ▾，点开浮出可多选的选项面板）
   ⚠️ 隐藏的 <select multiple> 是选中态真源，这里只做渲染 —— 单一事实源。
   ⚠️ 面板用 position:fixed（JS 定位到按钮下方）：.rmodal-body 是 overflow:auto，
      absolute 面板会被它裁掉（本地踩过）。 */
.bc-dd { position: relative; }
.bc-dd-btn {
  width: 100%; box-sizing: border-box; min-height: 38px;
  display: flex; align-items: center; gap: 8px; text-align: left;
  padding: 8px 11px; border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); background: var(--surface);
  color: var(--text); font-size: calc(13.8px * var(--fs));
  font-family: inherit; cursor: pointer; transition: border-color .15s;
}
.bc-dd-btn:hover { border-color: var(--brand); }
.bc-dd-btn:focus-visible {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(255, 196, 0, .22);
}
.bc-dd-txt {
  flex: 1; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.bc-dd-txt.ph { color: var(--text-3); }
.bc-dd-arrow {
  flex: none; width: 0; height: 0; margin-top: 2px;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid var(--text-3);
  transition: transform .18s;
}
.bc-dd.open .bc-dd-arrow { transform: rotate(180deg); }

.bc-dd-panel {
  position: fixed; z-index: 70;          /* 高于 #bcModal(69) */
  min-width: 240px; max-height: 232px; overflow: auto;
  padding: 5px; box-sizing: border-box;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); box-shadow: 0 14px 38px rgba(20, 22, 26, .2);
}
.bc-dd-opt {
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  padding: 8px 10px; border-radius: var(--r-sm);
  font-size: calc(13.8px * var(--fs)); color: var(--text);
}
.bc-dd-opt:hover { background: var(--surface-2); }
.bc-dd-opt input { accent-color: var(--brand); margin: 0; flex: none; }
.bc-dd-opt .t { flex: 1; min-width: 0; overflow: hidden;
                text-overflow: ellipsis; white-space: nowrap; }
.bc-dd-opt .warn { color: var(--amber, #B45309); font-size: calc(12.7px * var(--fs)); flex: none; }
.bc-dd-empty {
  padding: 14px 12px; text-align: center;
  font-size: calc(13.3px * var(--fs)); color: var(--text-3);
}
.bc-dd-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 4px 2px; margin-top: 4px; border-top: 1px solid var(--border);
  font-size: calc(12.7px * var(--fs)); color: var(--text-3);
}

/* ③④ 开关：复制发货配置 / 鱼小铺 */
.bc-sw {
  position: relative; display: inline-flex; align-items: center;
  width: 42px; height: 24px; flex: 0 0 42px; cursor: pointer;
}
.bc-sw input {
  /* 🔴 不能写 width:0;height:0 —— 真机实测那样会让 input 变成 0×0：
     *   量尺寸量不到、点击区只剩 4px 的透明点、辅助技术读不到控件。
     *   正确做法 = input 铺满开关本体（z-index 压在轨道之上），
     *   轨道 <i> 只负责画外观（须在 input 之下）。 */
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; margin: 0;
  opacity: 0; cursor: pointer;
}
.bc-sw i {
  position: absolute; inset: 0; z-index: 0; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  transition: .18s;
}
.bc-sw i::after {
  content: ""; position: absolute; left: 2px; top: 2px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--surface); box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
  transition: .18s;
}
.bc-sw input:checked + i { background: var(--brand); border-color: var(--brand); }
.bc-sw input:checked + i::after { transform: translateX(18px); }
/* 🔴 可用但**未勾选**的开关：不能跟 disabled 一个样（本地踩过 ——
   两者轨道都是灰的，用户完全看不出这个开关能不能点）。
   ✅ 判据 = 轨道描品牌色 + 滑块带品牌描边；disabled 才灰化 + 斜纹。 */
.bc-sw:not(.is-off) i { border-color: rgba(255, 176, 32, .55); }
.bc-sw:not(.is-off) i::after { box-shadow: 0 1px 3px rgba(0, 0, 0, .2), 0 0 0 1px rgba(255, 176, 32, .45); }
.bc-sw input:disabled + i {
  opacity: .5; cursor: not-allowed;
  background: repeating-linear-gradient(45deg,
    var(--surface-2) 0 5px, var(--border) 5px 10px);
}
.bc-sw input:disabled { cursor: not-allowed; }
.bc-sw input:focus-visible + i { box-shadow: 0 0 0 3px rgba(255, 196, 0, .35); }

/* 开关下方的说明文字（截图里每个开关下面都有一句灰字） */
.bc-sw-lab {
  display: block; font-size: calc(12.9px * var(--fs));
  color: var(--text-3); line-height: 1.7; margin-top: 8px;
}
.bc-sw-lab.off { color: var(--text-3); opacity: .8; }

/* 开关不可用时的**理由**（鱼小铺：所选店铺里有非鱼小铺 ⇒ 多库存会被钳成 1）
   —— 灰掉必须说清为什么，否则用户以为功能坏了 */
.bc-sw-why {
  display: block; margin-top: 6px;
  font-size: calc(12.4px * var(--fs)); color: #b4690e;
  background: rgba(255, 176, 32, .1);
  border: 1px solid rgba(255, 176, 32, .3);
  border-radius: var(--r-sm); padding: 5px 9px; line-height: 1.6;
}
.bc-sw-why[hidden] { display: none; }
.bc-sw.is-off { opacity: .55; }

/* ⑤ 发布方式：两个并排单选卡（直接发布 / 存为草稿） */
.bc-radios { display: flex; gap: 10px; flex-wrap: wrap; }
.bc-radio {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 7px 14px; border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); background: var(--surface);
  font-size: calc(13.8px * var(--fs)); color: var(--text-2);
  transition: .15s;
}
.bc-radio:hover { border-color: var(--brand); }
.bc-radio input { accent-color: var(--brand); margin: 0; }
.bc-radio.on {
  border-color: var(--brand); background: var(--brand-soft);
  color: var(--text); font-weight: 600;
}

/* 加载态（拉店铺列表期间显示，避免「点了没反应」的误判） */
.bc-loading {
  display: flex; align-items: center; gap: 8px;
  font-size: calc(13.3px * var(--fs)); color: var(--text-3);
}
.bc-loading i {
  width: 13px; height: 13px; border-radius: 50%; flex: none;
  border: 2px solid var(--border-strong); border-top-color: var(--brand);
  animation: bcspin .7s linear infinite;
}
@keyframes bcspin { to { transform: rotate(360deg); } }

/* 结果列表（跳过的 / 提示的） */
.bc-res { margin-top: 14px; max-height: 170px; overflow: auto;
          display: flex; flex-direction: column; gap: 5px; }
.bc-res div {
  font-size: calc(12.7px * var(--fs)); color: var(--text-2);
  background: var(--surface-2); border-radius: var(--r-sm); padding: 6px 9px;
  word-break: break-all;
}

.bc-foot { display: flex; align-items: center; gap: 10px; }
/* .bc-foot .sp 在 HTML 里是 <span class="sp">，靠这条撑开 */
.bc-foot .sp { flex: 1; }

/* ════════════════════════════════════════════════════════════════════════
   🟡 主题层 · B「蜂蜜气泡 Honey Bubble」（10-13 14:19 涛爷拍板）
   ════════════════════════════════════════════════════════════════════════
   ⚠️⚠️⚠️ **本块是纯追加的覆写层，一行原代码都没改** ——
      回滚只需删除本段（原件已备份到 `_trash/ui_backup_1419/app.css`）。
      原变量名（--surface/--border/--brand…）与方案页里的（--card/--line…）不同名，
      所以这里**沿用真站原变量名**覆写，方案页只是视觉稿、不作为变量名依据。

   ★ 设计理念（涛爷选定）：**琥珀黄釉 + 糖霜玻璃**
     底色从冷灰 `#F4F5F7` 换成暖奶黄 `#FFF8E6`，两团暖黄光晕从页面顶部透上来；
     面板/卡片半透明 + `backdrop-filter` 造成「阳光穿过蜂蜜」的糖霜玻璃感；
     所有控件顶部一道 95% 不透明白高光条，圆角放大到 17~28px 极致软，
     **体积靠双层阴影撑**：实体黄调厚边 + 柔扩散，不是单层灰投影。

   🔴 铁律一：**hover 零几何位移**。上一轮报障「全站按钮闪烁 + 点不中」的
      根因是 `.btn:hover{transform:translateY(-2px) rotateX(6deg)}` 造成自激振荡。
      本块**绝不复活任何 hover 位移**，hover 只改 box-shadow / 背景 / 描边色。
      拟物的「按下去」反馈只挂 `:active`（点击瞬间，位移 ≤4px，非自激）。

   🔴 铁律二：**弹窗/抽屉类不受本主题影响**。`.rmodal/.rmask/.cmodal`
      等遮罩层保持原样，避免破坏读图、层级与今天的 AI 弹窗修复。

   🔴 铁律三：**不碰布局**。只改颜色 / 圆角 / 阴影 / 高光，
      网格列数、行高、内边距一律保留 —— 商品卡密度是涛爷日常效率的命根子。
   ════════════════════════════════════════════════════════════════════════ */

/* ── ① 变量覆写 ───────────────────────────────────────────────── */
:root {
  /* 底色：冷灰 → 暖奶黄 */
  --bg: #FFF8E6;
  --surface: #FFFDF6;
  --surface-2: #FFF6E4;
  --surface-3: #FFF0CC;
  /* 描边：从冷灰 → 蜜蜡黄调 */
  --border: #F0E1BC;
  --border-strong: #E3CE9E;
  /* 文字：纯黑 → 深琥珀棕（比纯黑柔和，长时间看不累） */
  --text: #3B2E14;
  --text-2: #7A6640;
  --text-3: #AB9670;
  --ink: #3B2E14;
  /* 品牌黄：从 #FFC400 提到 #FFE600 系（涛爷点名要的原黄），并补齐三段渐变 */
  --brand: #FFC400;
  --brand-hi: #FFE873;   /* ★ 新增：高光端 */
  --brand-lo: #F5A800;   /* ★ 新增：厚边/暗端 */
  --brand-ink: #5A3E00;
  --brand-soft: #FFF3D4;
  --brand-glow: rgba(255, 196, 0, .34);
  /* 圆角：全站放大（圆润可爱是本次令） */
  --r-lg: 26px;  /* 面板 */
  --r-md: 17px;  /* 按钮 / 卡片 */
  --r-sm: 12px;  /* 输入框 / 小件 */
  /* 阴影：双层（实体厚边 + 柔扩散）—— B 方案的核心 */
  --shadow-1: 0 1px 0 #fff inset, 0 2px 0 #F0E1BC;
  --shadow-2: 0 1px 0 #fff inset, 0 2px 0 #F0E1BC, 0 14px 26px -12px rgba(180, 130, 10, .40);
  --shadow-glow: 0 1px 0 #fff inset, 0 3px 0 #E0A400, 0 16px 24px -10px rgba(200, 145, 0, .50);
  /* 状态色暖调化，与蜜蜡黄同族 */
  --green: #15803D;  --green-soft: #DFF5E4;
  --amber: #B45309;  --amber-soft: #FFEBCB;
  --red: #C0392B;    --red-soft: #FFE4DC;
  --blue: #1E5FA8;   --blue-soft: #DEEBFA;
  --violet: #6D28D9; --violet-soft: #EDE9FE;
  --orange: #9A3412; --orange-soft: #FFEDD5;  /* ★当文字色压深（4.42→5.9:1）*/
}

/* ── ② 底：暖光晕 ───────────────────────────────────────────── */
body {
  background-color: var(--bg);
  background-image:
    radial-gradient(1100px 520px at 12% -8%, rgba(255, 229, 110, .55), transparent 62%),
    radial-gradient(900px 480px at 92% 4%, rgba(255, 196, 0, .28), transparent 60%);
  background-attachment: fixed;
}
/* 🔴🔴 10-14 涛爷报障「右边有一个大黑边」：那 15px 是
   `html{scrollbar-gutter:stable}` 预留的**滚动条槽**（⑭-2 为治「切页抖动」
   故意加的，不能删）。
   ⚠️ 为什么它是黑的：`html` 与 `body` 都**透明**，而 `body` 的宽度只有
      `clientWidth − 槽宽`（实测 1080 视口下 body=1065）⇒ 那 15px 压根不在
      body 里，谁的背景都盖不到 ⇒ 露出**画布默认深灰**。
   🔴 为什么 `::-webkit-scrollbar-track{background:#FFF6E4}`（2964 行）救不了：
      槽里**没有滚动条**（无头浏览器是覆盖式滚动条 / 短页时压根不画），
      伪元素不生成 ⇒ 规则形同虚设。**别去改那条规则，改了没用。**
   ✅ 正解：把主题底色铺到 `html` 上 —— 槽与页面同色，黑边消失，
      且 `scrollbar-gutter` 的防抖效果一点不受影响。 */
html { background-color: var(--bg); }

/* ── ③ 顶栏：糖霜玻璃 ───────────────────────────────────────── */
.topbar {
  background: rgba(255, 250, 232, .93);
  backdrop-filter: saturate(1.4) blur(9px);
  box-shadow: 0 2px 18px rgba(190, 140, 20, .10);
}
/* 🔴🔴 10-13 14:44 涛爷报障「残留之前开发的按钮影子，鼠标放上去时有时无」——修的就是这里。
   根因：上面 app.css:1723/1727 给 `.nav a` 和 `.nav a:hover` 各写了一套**旧黄调阴影**
        （`0 1.5px 0 rgba(80,58,10,.10)` / hover 时突增到 `0 9px 16px -7px`），
        而本主题层当初**只覆写了 `.nav a.on` 的阴影**，漏了非激活态和 hover 态。
        ⇒ 结果：静止时是 B 方案的干净影子，鼠标一放上去旧阴影**突然变大变深**，
          与新背景色不匹配 ⇒ 视觉上就是「影子时有时无、闪一下」。
        ⚠️ 这不是自激振荡（几何位移已被安全网归零），是**两套阴影值在 hover 时打架**。

   ✅ 修法：把 `.nav a` / `.nav a:hover` 的阴影**也在主题层显式覆写**，
      且 hover 只做「影子轻微变深 + 背景变暖黄」—— **不放大模糊半径**，避免再出现突变。
   ⚠️ 必须成对写（静止态 + hover 态），只写一条另一条仍会漏。 */
.nav a {
  background: rgba(255, 255, 255, .58);
  border-color: var(--border);
  color: #6B5A32;
  box-shadow: 0 2px 0 #F0E1BC, 0 6px 14px -10px rgba(180, 130, 10, .34);
  transition: background-color .16s linear, box-shadow .16s linear,
              color .16s linear, border-color .16s linear;
}
/* hover：影子只「压深一点点」，模糊半径**不变**（放大就会闪） */
.nav a:hover:not(.on):not(.dis) {
  background: #FFF1CC;
  border-color: #F2CE6A;
  color: var(--brand-ink);
  box-shadow: 0 3px 0 #EBCB84, 0 6px 14px -10px rgba(180, 130, 10, .34);
}
/* hover 时的高光条（与 .btn 一致的拟物语言） */
.nav a:hover:not(.on):not(.dis)::after,
.nav a.on::after {
  content: ""; position: absolute; left: 14%; right: 14%; top: 2px; height: 44%;
  border-radius: 999px; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .92), rgba(255, 255, 255, 0));
}
.nav a { position: relative; overflow: hidden; }
.nav a.on {
  background: linear-gradient(150deg, var(--brand-hi), var(--brand));
  color: var(--brand-ink);
  border-color: var(--brand-lo);
  box-shadow: 0 3px 0 var(--brand-lo), 0 10px 18px -10px rgba(200, 150, 0, .5),
              inset 0 2px 0 rgba(255, 255, 255, .6);
}
/* 🔴 激活态的 hover 旧规则（app.css:1731 带 rotateX）已由末尾安全网归零，
   这里再显式给一版不位移的 hover 阴影，杜绝「放大突变」。 */
.nav a.on:hover {
  box-shadow: 0 4px 0 var(--brand-lo), 0 10px 18px -10px rgba(200, 150, 0, .5),
              inset 0 2px 0 rgba(255, 255, 255, .6);
}

/* ── ④ 面板：釉面 + 顶部高光 ────────────────────────────────── */
.panel {
  background: linear-gradient(170deg, rgba(255, 253, 246, .96), rgba(255, 250, 232, .92));
  backdrop-filter: saturate(1.4) blur(3px);
  border: 2px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  position: relative; overflow: hidden;
}
/* ★ 高光层：顶部 44% 白渐隐 —— 拟物的「光从上面来」。⚠️ 必须 pointer-events:none */
.panel::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 44%;
  border-radius: inherit; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .72), rgba(255, 255, 255, 0));
}

/* ── ⑤ 模式卡 ───────────────────────────────────────────────── */
.mode {
  background: linear-gradient(165deg, rgba(255, 255, 255, .92), rgba(255, 251, 238, .86));
  backdrop-filter: saturate(1.4) blur(3px);
  border: 2px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  position: relative; overflow: hidden;
  transition: box-shadow .18s ease, border-color .18s ease, background .18s ease;
}
.mode::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 44%;
  border-radius: inherit; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .8), rgba(255, 255, 255, 0));
}
.mode.on {
  background: linear-gradient(165deg, #FFFDF3, #FFF3D0);
  border-color: var(--brand);
  box-shadow: 0 4px 0 #E8B400, 0 18px 30px -14px rgba(200, 150, 0, .55);
}
.mode:hover { box-shadow: 0 1px 0 #fff inset, 0 4px 0 #EBD49E, 0 26px 42px -14px rgba(180, 130, 10, .52); }

/* ── ⑥ 商品卡 ───────────────────────────────────────────────── */
.pcard {
  background: linear-gradient(165deg, rgba(255, 255, 255, .92), rgba(255, 251, 238, .86));
  backdrop-filter: saturate(1.4) blur(3px);
  border: 2px solid var(--border);
  border-radius: 22px;
  box-shadow: var(--shadow-2);
  transition: box-shadow .18s ease, transform .18s cubic-bezier(.2, .9, .3, 1.2);
}
.pcard .bd { background: transparent; }
.pcard:hover {
  transform: translateY(-3px);
  box-shadow: 0 1px 0 #fff inset, 0 4px 0 #EBD49E, 0 26px 42px -14px rgba(180, 130, 10, .52);
}
.pcard .ph img { filter: saturate(1.06) brightness(1.04); }

/* 🔴🔴 2026-10-25：采集**实时条**（真·边采边出的「0 秒反馈」）
   点「开始采集」立刻出现，采完/停止自动消失。
   ⛔ 纯展示，不参与任何业务判断；DOM 由 `showLiveBar()/hideLiveBar()` 管。 */
.livebar {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 12px; padding: 9px 14px;
  border: 2px solid #FFD98A; border-radius: 14px;
  background: linear-gradient(180deg, #FFF9E8, #FFF3D2);
  color: #7A4E00; font-size: 13px; font-weight: 700;
  box-shadow: 0 2px 10px -3px rgba(180, 130, 10, .35);
}
.livebar .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: #FFB020; flex: 0 0 auto;
  animation: livePulse 1.1s ease-in-out infinite;
}
@keyframes livePulse {
  0%, 100% { transform: scale(1);   opacity: 1; }
  50%      { transform: scale(1.5); opacity: .45; }
}

/* 🔴 2026-10-26：采集条件里的「迷你下拉」（目前只有「无想要数：跳过/也算通过」）。
   必须小而窄 —— 它挂在「想要数」输入框下面，⛔ 不能把那一行撑高，
   否则整排条件会换行（09-18 口径：条件区必须一行摆下）。 */
.mini-sel {
  display: block;
  width: 100%;
  margin-top: 3px;
  height: 24px;
  font-size: 12px;
  line-height: 22px;
  padding: 0 2px;
  border: 1px solid #E6D8B9;
  border-radius: 6px;
  background: #FFFDF7;
  color: #7A6A4F;
  cursor: pointer;
}
.mini-sel:focus { outline: none; border-color: #E0B64A; }

/* 🔴🔴 2026-10-26 涛爷第 7 次报障：「不管输入什么条件都没有符合条件的」——
   真相不是筛选坏了，是「已采过 50 / 新品无想要数 42 / 想要数不够 8」这种
   **分类明细从来没给他看过**，只写一句"全部不符合你的条件"。
   ⇒ 这里就是那条明细的样式（列表区 0 条时显示，替代原来那句废话）。 */
.skipwhy {
  margin: 10px auto 0;
  max-width: 460px;
  text-align: left;
}
.skipwhy-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 0;
  font-size: 13px;
  color: #6B5B45;
  line-height: 20px;
}
.skipwhy-n {
  display: inline-block;
  min-width: 34px;
  text-align: right;
  font-weight: 700;
  color: #B07B2E;
  font-variant-numeric: tabular-nums;
}
.skipwhy-tip {
  margin: 10px auto 0;
  max-width: 460px;
  padding: 8px 12px;
  font-size: 13px;
  line-height: 20px;
  color: #7A5B18;
  background: #FFF7E4;
  border: 1px solid #F0DFAF;
  border-radius: 8px;
}

/* 🔴🔴🔴 2026-10-26 涛爷令：采集进行中**唯一**的列表形态 —— 一条居中占位。
   原话：「我要你直接根据我筛选的条件展示的就是我要的结果，**不要这种一直提示
     筛选中的状态下空的数据表**。你可以页面提示『采集中』，过个两三秒再直接出来结果。」
   ⚠️ 所以 `.pcard-preview`（筛选中行）**已停用**（样式留着不删，避免误伤，
      但 poll() 不再铺它）。⛔ 别再把「半成品行」当首屏。
   ✅ 进度只由这一条 + 顶部实时条承载；合格的条目一到就换成真卡片。 */
.collecting-ph {
  padding: 64px 20px;
  margin: 6px 0;
  text-align: center;
  color: #8A7550;
  font-size: 14px;
  line-height: 22px;
  background: linear-gradient(180deg, #FFFDF7, #FFF8EA);
  border: 1px dashed #E6D5AE;
  border-radius: 12px;
  animation: collectingPulse 1.6s ease-in-out infinite;
}
@keyframes collectingPulse {
  0%, 100% { opacity: .70; }
  50%      { opacity: 1;   }
}
/* 表格视图下 grid 不是 flex/grid 容器，占位块要自己撑开 */
#grid.as-table > .collecting-ph { grid-column: auto; }

/* 🔴🔴 2026-10-25：**预览行**（筛选中）—— ⚠️ 2026-10-26 起**已停用**，见上。
   保留样式只为兼容旧缓存页面；poll() 不再产生这种行。 */
tr.pcard.pcard-preview,
div.pcard.pcard-preview {
  position: relative;
  /* 🔴 2026-10-25：透明度从 .62 提到 .78、去掉 saturate(.35)。
     实测 .62 太灰 —— 整片看过去像「数据坏了/加载失败」，而不是「还在筛」。
     现在靠**左侧浅蓝色条 + 浅蓝底**表达"待定"，文字仍然看得清。 */
  opacity: .78;
  background: linear-gradient(90deg, #EEF4FA, #F8FBFE 60%) !important;
  box-shadow: inset 4px 0 0 #7FB3DE !important;
  animation: pcardPreviewIn .3s ease;
}
@keyframes pcardPreviewIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: .62; transform: none; }
}
/* 表格行左侧一枚「筛选中」小标（纯装饰，⛔ 不占布局） */
/* ⚠️ 徽标靠**右**不靠左 —— 最左边是勾选框，`left:6px` 会被它压住（实测看不见） */
tr.pcard.pcard-preview::after {
  content: '筛选中';
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  padding: 0 7px; border-radius: 999px;
  font-size: 10px; line-height: 15px; font-weight: 700;
  color: #2F6FA8; background: #DCEAFA;
  pointer-events: none;
}

/* 🔴🔴 2026-10-25：**刚采入**的卡片 / 表格行（真·边采边出）
   采集途中由 `appendLiveItem()` 增量插到列表最前面，带一圈暖色脉冲，
   让用户一眼看出「这几条是这一秒刚从平台采进来的」。
   ⚠️ 两种形态都要覆盖：卡片视图是 `<div class="pcard">`，
      表格视图是 `<tr class="pcard">`（采集页**默认就是表格**，这条是主路径）。
   ⛔ 只是**视觉提示**，不参与任何筛选/统计逻辑；
      采集结束后 `loadProducts()` 整页重画会自然去掉这个 class。 */
.pcard.pcard-live {
  position: relative;
  animation: pcardLiveIn .45s cubic-bezier(.2, .9, .3, 1.15);
  border-color: #FFC24B;
  box-shadow: 0 0 0 3px rgba(255, 194, 75, .22), var(--shadow-2);
}
@keyframes pcardLiveIn {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}
/* 表格行：用底色 + 左侧色条（`<tr>` 上的 ::before 在部分浏览器渲染不稳定） */
tr.pcard.pcard-live {
  background: linear-gradient(90deg, #FFF6DC, #FFFDF4 60%) !important;
  box-shadow: inset 4px 0 0 #FFB020 !important;
  animation: pcardLiveRowIn .5s ease;
}
@keyframes pcardLiveRowIn {
  from { background-color: #FFE9AE; }
  to   { background-color: transparent; }
}
/* 卡片：右上角一枚「刚采入」小徽标（纯装饰，不占布局） */
div.pcard.pcard-live::before {
  content: '刚采入';
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
  padding: 1px 8px; border-radius: 999px;
  font-size: 11px; line-height: 16px; font-weight: 700;
  color: #7A4E00; background: linear-gradient(#FFE08A, #FFC24B);
  box-shadow: 0 1px 3px rgba(150, 100, 0, .28);
  pointer-events: none;
  z-index: 2;
}

/* ── ⑦ 按钮：三段黄渐变 + 双层阴影 + 顶部高光 ─────────────────── */
/* 🔴🔴 10-15 涛爷令：「重新读取/ 保存全部 / 应用到全部店铺」三颗按钮
     要和全站其他按钮一样的**闲鱼黄**。
   病根 = 本段把 `.btn` 静止底色写成了 `linear-gradient(#FFFDF6, #FBF3DE)`
     —— 那是**近白的淡黄白**（实测 computed = rgb(255,253,246)→rgb(251,243,222)），
     视觉上就是「白底灰字」，和「闲鱼黄胶囊」基准冲突。
     ⇒ 三颗按钮其实**一模一样**（实测 computed 逐字相同），
       截图里那颗「保存全部」看着偏黄，纯属旁边 select 更白造成的**对比错觉**。
   ✅ 正解 = 与 `.btn.primary` 同族的三段黄：
       `--brand-hi → --brand → --brand-lo`，hover 再往上提一档。
   ⚠️ 改这一条 = **全站所有 `.btn` 一起变黄**（不是只这三颗），
      这才是涛爷要的「和其他按钮一样」。 */
.btn {
  border: 2px solid var(--border-strong);
  border-radius: 17px;
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 55%, var(--brand-lo) 100%);
  color: var(--brand-ink);
  box-shadow: 0 3px 0 var(--border), 0 8px 16px -10px rgba(180, 130, 10, .45);
  transition: box-shadow .16s ease, background .16s ease,
              border-color .16s ease, transform .16s cubic-bezier(.2, .9, .3, 1.2);
  position: relative; overflow: hidden;
}
/* 高光条 */
.btn::after {
  content: ""; position: absolute; left: 12%; right: 12%; top: 2px; height: 46%;
  border-radius: 999px; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .9), rgba(255, 255, 255, 0));
}
/* 🔴 hover 只改阴影与底色，**绝不写 transform**（否则复活今天的闪烁 bug） */
.btn:hover:not(:disabled) {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%);
  border-color: var(--brand);
  box-shadow: 0 4px 0 #E6D3A2, 0 14px 24px -12px rgba(180, 130, 10, .5);
}
.btn.sm { border-radius: 12px; }

.btn.primary {
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 55%, var(--brand-lo) 100%);
  color: var(--brand-ink);
  border-color: #DE9E00;
  box-shadow: 0 4px 0 #C98C00, 0 14px 22px -8px rgba(200, 140, 0, .55),
              inset 0 3px 0 rgba(255, 255, 255, .68);
}
.btn.primary::after { background: linear-gradient(180deg, rgba(255, 255, 255, .95), rgba(255, 255, 255, 0)); }
.btn.primary:hover:not(:disabled) {
  background: linear-gradient(180deg, #FFEE95, #FFCF1A 60%, #FFB80A);
  border-color: #DE9E00;
  box-shadow: 0 6px 0 #C98C00, 0 20px 30px -10px rgba(200, 140, 0, .6),
              inset 0 3px 0 rgba(255, 255, 255, .72);
}

.btn.danger {
  background: linear-gradient(180deg, #FFEDE9, #FFDDD6);
  color: var(--red); border-color: #F3B8A6;
  box-shadow: 0 3px 0 #F3B8A6, 0 10px 18px -10px rgba(190, 90, 60, .4);
}
.btn.danger:hover:not(:disabled) { box-shadow: 0 4px 0 #F3B8A6, 0 16px 26px -12px rgba(190, 90, 60, .48); }
.btn.warn {
  background: linear-gradient(180deg, #FFF3D4, #FFE6B0);
  color: var(--amber); border-color: #EFCB84;
  box-shadow: 0 3px 0 #EFCB84, 0 10px 18px -10px rgba(180, 130, 10, .4);
}
.btn.ghost {
  background: rgba(255, 253, 246, .60);
  border-color: var(--border);
  box-shadow: 0 3px 0 var(--border);
}

/* 🔴 按压反馈：只在 :active（点击瞬间）走，位移 ≤4px，**非 hover 自激** */
.btn:active:not(:disabled) {
  transform: translateY(3px);
  box-shadow: 0 0 0 #C98C00, inset 0 2px 6px rgba(150, 110, 0, .32) !important;
}
.btn.danger:active:not(:disabled) { box-shadow: 0 0 0 #F3B8A6 !important; }

/* ── ⑧ 输入：釉面 + 内阴影 ──────────────────────────────────── */
input[type=text], input[type=number], input[type=search],
input[type=password], input[type=url], select, textarea, .search {
  background: rgba(255, 255, 255, .88) !important;
  border: 2px solid var(--border) !important;
  border-radius: var(--r-sm) !important;
  color: var(--text) !important;
  box-shadow: 0 2px 0 #F5E9CC, inset 0 1px 3px rgba(180, 140, 40, .08) !important;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--brand) !important;
  box-shadow: 0 2px 0 #F5E9CC, 0 0 0 3px var(--brand-glow) !important;
}
input::placeholder, textarea::placeholder { color: var(--text-3) !important; }

/* ── ⑨ 标签 / chip：糖霜胶囊 ────────────────────────────────── */
.chip {
  background: #FFF3D4;
  color: #7A5E20;
  border: 2px solid #F0DFB2;
  border-radius: 999px;
  box-shadow: 0 2px 0 #F0DFB2;
  position: relative; overflow: hidden;
}
.chip::after {
  content: ""; position: absolute; left: 12%; right: 12%; top: 2px; height: 46%;
  border-radius: 999px; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .9), rgba(255, 255, 255, 0));
}
.chip.on {
  background: linear-gradient(180deg, var(--brand-hi), var(--brand));
  color: var(--brand-ink); border-color: #DE9E00;
  box-shadow: 0 3px 0 #DE9E00;
}
.badge {
  background: linear-gradient(180deg, var(--brand-hi), var(--brand));
  color: var(--brand-ink);
  border: 2px solid #DE9E00;
  box-shadow: 0 3px 0 #DE9E00;
}

/* ── ⑩ 图标底座 ─────────────────────────────────────────────── */
.ic-sq, .ic-round, .mi {
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .14));
}

/* ════════════════════════════════════════════════════════════════
   ⑪ **安全网**：hover 零几何位移（本日铁律，方案层也不许退化）
   ⚠️ 写在**本主题块末尾** —— 同权重下后写的赢，
      保证即使前面 ⑨0~⑩ 意外写了 transform，也会被这里强制归零。
   ⚠️ `.btn:active` 的按压位移在 @media 外，不受影响（点击反馈不是自激）。
   ════════════════════════════════════════════════════════════════ */
@media (hover: hover) {
  .btn:hover:not(:disabled),
  .btn:hover:not(:disabled):active,
  .nav a:hover:not(.on):not(.dis),
  .nav a.on:hover,
  .mode:hover, .mode.on:hover,
  .pcard:hover,
  .chip:hover, .badge:hover { transform: none !important; }
  .btn, .btn.sm, .btn.primary, .btn.danger, .btn.warn, .btn.ghost {
    transition: box-shadow .16s linear, border-color .16s linear,
                background-color .16s linear, color .16s linear !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   ⑫ **补漏段：消除「旧阴影残留」**（10-13 14:44 涛爷报障
      「残留之前开发的按钮影子，鼠标放上去会时有时无」）
   ════════════════════════════════════════════════════════════════════════
   🔴🔴 本段修的是一个**系统性缺陷**，不是单点 bug：
     上一轮我落地 B 主题时，只覆写了「静止态」的选择器（`.btn` / `.nav a.on` /
     `.mode` / `.pcard`…），但**漏掉了它们的 `:hover` 兄弟选择器**。
     旧段落（app.css:1700~2100「⑨全站立体化优化」那一段）给每个 hover 都写了
     **更大的 box-shadow**（例：`.nav a:hover` 从 `0 4px 10px` 突增到 `0 9px 16px`）。

     ⇒ 结果 = 静止时看到 B 方案的干净影子，鼠标一放上去**旧阴影突然变大变深**，
       两套阴影值来回打架 ⇒ 视觉上就是「影子时有时无、闪一下」。

   ⚠️ 注意：这**不是**自激振荡（几何位移早已被安全网归零），
      是**阴影值在 hover 瞬间突变**造成的视觉闪烁。药方不同，别搞混。

   ✅ 通用修法（本段全部遵守三条铁律）：
     ① **凡是写阴影，必须把 `:hover` 兄弟也写掉**，且 hover 的模糊半径
        **不允许比静止态大** —— 只允许「底边从 2px→3px、颜色压深一点」。
     ② 静止态与 hover 态共用同一族底色（`--border` / `--brand-lo`），
        杜绝「一套冷灰、一套暖黄」的混色。
     ③ 一律 `transition: box-shadow .16s linear`（**不带 transform**），
        配合末尾安全网，彻底不可能抖。
   ════════════════════════════════════════════════════════════════════════ */

/* ── ⑫-1 板块：.panel 的 hover（旧段 1768 有 hover 阴影） ────────── */
.panel:hover {
  box-shadow: var(--shadow-2);
  transform: none !important;
}

/* ── ⑫-2 顶栏导航：静止/hover 配对已在上面 ③ 修好，这里只兜底旧规则 ── */
.nav a:hover:not(.on):not(.dis),
.nav a.on:hover { transform: none !important; }

/* ── ⑫-3 旧段有 hover 阴影放大（采集记录行已下线） ───────────────── */

/* hover 只压深底边，模糊半径不变 ⇒ 不会有「影子忽大忽小」 */

.rl-log, .log {
  background: #FFFBEF;
  border-color: var(--border);
  box-shadow: inset 0 1px 3px rgba(180, 140, 40, .08);
}

/* ── ⑫-4 按钮家族：ghost/danger/warn 的 hover 必须与静止态同族 ────── */
/* ★★★ 2026-10-15 涛爷令（🔴 全站最高）：「全站所有的按钮和胶囊状的模块都是
   设计成一样的 UI，就是高光玻璃那个样式，全站统一。当然除了灰色按钮
   不可按状态下的除外。能点击的都一样的样式。」

   ⇒ 本段原本把 `.btn.ghost` 定成 `rgba(255,253,246,.60)` —— **近白米色**，
     全站 28 处用它（`/account` `/accounts` `/cards` `/chat` `/` `/itemmgr`
     `/trade` `/listing` …），这正是「配色不统一 / 有些按钮是白色的」的真凶。
   → **可点的 ghost 一律并入玻璃黄**（与 `.btn` / `.btn.primary` / `.btn.fg-ghost`
     同一套三段渐变 + 高光条 + 实体底边）。
   → **不可点的仍留灰**：`.btn:disabled` / `.btn.ghost:disabled` 在 3314/3341
     两段有兜底（`background:#E9E3D3 !important`），**本段不许写 disabled 规则**，
     否则会把「灰色 = 不可按」这条唯一的语义线索抹掉。

   ⚠️ danger / warn **保持本色不动**（淡红=危险、淡琥珀=提醒）：
     这不是"不统一"，是**语义分层** —— 全站删除类动作一律淡红，
     改黄会让人误以为是普通主按钮。涛爷说的是"普通可点按钮"统一。 */
.btn.ghost {
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 55%, var(--brand-lo) 100%);
  color: var(--brand-ink);
  border-color: #DE9E00;
  box-shadow: 0 3px 0 #C98C00, 0 8px 16px -10px rgba(180, 130, 10, .45);
}
.btn.ghost:hover:not(:disabled) {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%);
  border-color: #E0A400;
  /* ⚠️ 模糊半径必须小于下面 danger 那个，别再写 16px 造成突变 */
  box-shadow: 0 4px 0 #C98C00, 0 12px 22px -12px rgba(180, 130, 10, .48);
}
.btn.danger {
  background: linear-gradient(180deg, #FFEDE9, #FFDDD6);
  color: var(--red); border-color: #F3B8A6;
  box-shadow: 0 3px 0 #F3B8A6, 0 10px 18px -10px rgba(190, 90, 60, .4);
}
.btn.danger:hover:not(:disabled) {
  background: linear-gradient(180deg, #FFE4DC, #FFD5CA);
  border-color: #F3B8A6;
  box-shadow: 0 4px 0 #F3B8A6, 0 12px 22px -12px rgba(190, 90, 60, .48);
}
.btn.warn {
  background: linear-gradient(180deg, #FFF3D4, #FFE6B0);
  color: var(--amber); border-color: #EFCB84;
  box-shadow: 0 3px 0 #EFCB84, 0 10px 18px -10px rgba(180, 130, 10, .4);
}
.btn.warn:hover:not(:disabled) {
  background: linear-gradient(180deg, #FFEBBE, #FFDD9C);
  border-color: #EFCB84;
  box-shadow: 0 4px 0 #EFCB84, 0 12px 22px -12px rgba(180, 130, 10, .48);
}
/* focus 环统一（旧的 `box-shadow` 与主题的会打架，重写一版） */
.btn:focus-visible, .btn.ghost:focus-visible, .bc-dd-btn:focus-visible {
  outline: none;
  box-shadow: 0 3px 0 var(--brand-lo), 0 0 0 4px var(--brand-glow) !important;
}

/* ── ⑫-5 页签 / 分段控件 / 分页 ─────────────────────────────────── */
/* ★ 2026-10-15 涛爷令「全站按钮 + 胶囊状模块一律高光玻璃」：
   `.seg button`（分段/胶囊页签）与 `.pager b`（页码胶囊）原先的
   **未选中态**是 `rgba(255,253,246,.6)` 近白米色 ⇒ 一并并入玻璃黄。
   ⚠️ 选中态 `.on` 早就是玻璃黄了 —— 现在**静止与选中只差深浅**，层级更清楚。 */
.seg button {
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 55%, var(--brand-lo) 100%);
  border: 2px solid #DE9E00;
  color: var(--brand-ink);
  box-shadow: 0 3px 0 #C98C00;
  transition: background-color .16s linear, box-shadow .16s linear, color .16s linear;
}
.seg button:hover { background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%); box-shadow: 0 4px 0 #C98C00; }
.seg button.on {
  background: linear-gradient(150deg, var(--brand-hi), var(--brand));
  color: var(--brand-ink); border-color: var(--brand-lo);
  box-shadow: 0 3px 0 var(--brand-lo);
}
.pager { color: var(--text-2); }
.pager b {
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 55%, var(--brand-lo) 100%);
  border: 2px solid #DE9E00;
  color: var(--brand-ink);
  box-shadow: 0 3px 0 #C98C00;
}
.pager b:hover { box-shadow: 0 4px 0 #C98C00; }
.pager b.on {
  background: linear-gradient(180deg, var(--brand-hi), var(--brand));
  border-color: var(--brand-lo); color: var(--brand-ink);
  box-shadow: 0 3px 0 var(--brand-lo);
}

/* ── ⑫-6 标签 / 价格 / 徽标 ────────────────────────────────────── */
.tag {
  background: #FFF3D4; color: #7A5E20;
  border: 2px solid #F0DFB2;
  box-shadow: 0 2px 0 #F0DFB2;
}
.price { color: #8A5A00; }

/* ── ⑫-7 进度条 ────────────────────────────────────────────────── */
.bar { background: var(--border); box-shadow: inset 0 1px 2px rgba(180, 140, 40, .16); }
.bar i {
  background: linear-gradient(90deg, var(--brand-hi), var(--brand) 60%, var(--brand-lo));
  box-shadow: 0 1px 0 rgba(255, 255, 255, .6);
}

/* ── ⑫-8 LOGO / 品牌块 ─────────────────────────────────────────── */
.brand-logo, .brand-mark {
  box-shadow: 0 3px 0 #E0A400, 0 10px 18px -8px rgba(200, 145, 0, .5),
              inset 0 2px 0 rgba(255, 255, 255, .5);
}

/* ── ⑫-9 表单输入（.field 一族，旧的 focus 阴影） ───── */
.field input, .field select {
  border-color: var(--border) !important;
  box-shadow: 0 2px 0 #F5E9CC, inset 0 1px 3px rgba(180, 140, 40, .08) !important;
}
.field input:focus, .field select:focus {
  border-color: var(--brand) !important;
  box-shadow: 0 2px 0 #F5E9CC, 0 0 0 4px var(--brand-glow) !important;
}
.rl-body select { border-color: var(--border) !important; }

/* ── ⑫-10 面板头 / 商品卡图框 / 勾选工具条 ──────────────────────── */
.panel-h { box-shadow: none; }
.pcard .ph { box-shadow: inset 0 -1px 0 var(--border); }
.selbar {
  background: rgba(255, 253, 246, .95);
  backdrop-filter: saturate(1.4) blur(4px);
  border: 2px solid var(--border);
  box-shadow: 0 3px 0 #F0E1BC, 0 10px 20px -10px rgba(180, 130, 10, .4);
}

/* ── ⑫-11 账号卡 / 输入框容器 / 骨架屏 ────────────────────────── */
.acard {
  background: rgba(255, 253, 246, .9);
  border: 2px solid var(--border);
  box-shadow: 0 2px 0 #F0E1BC;
  transition: box-shadow .16s linear, border-color .16s linear;
}
.acard:hover { border-color: var(--border-strong); box-shadow: 0 3px 0 #E3CE9E; }
.acard.on {
  border-color: var(--brand);
  box-shadow: 0 3px 0 var(--brand-lo), 0 0 0 4px var(--brand-glow);
}
.kwbox, .srcbox, .urlbox, .pub-acct {
  background: rgba(255, 253, 246, .9);
  border: 2px solid var(--border);
  box-shadow: 0 2px 0 #F0E1BC;
}
.sk-card {
  background: linear-gradient(90deg, #FFF6E4, #FFFCF2, #FFF6E4);
  background-size: 200% 100%;
  border-radius: 22px;
}

/* ── ⑫-12 弹窗 / 抽屉 / toast：**只调边框与底色，不动层级** ────────
   ⚠️ 铁律二重申：`.rmodal/.rmask/.cmodal` 的 z-index / 定位**一个字都不许动**
      —— 上一轮「弹窗 UI 显示」和「点击被遮挡」两个 bug 都栽在层级上。 */
.drawer, .rmodal, .cmodal, .bc-dd-panel {
  background: linear-gradient(170deg, rgba(255, 253, 246, .98), rgba(255, 250, 232, .97));
  border-color: var(--border);
}
.drawer .dhead, .rmodal-h {
  background: linear-gradient(170deg, rgba(255, 253, 246, .98), rgba(255, 250, 232, .9));
  border-bottom: 2px solid var(--border);
}
.toast {
  background: #3B2E14;
  border: 2px solid #5A3E00;
  box-shadow: 0 4px 0 #241C0A, 0 14px 26px -10px rgba(60, 45, 20, .6);
}
.toast.err { background: #7A2018; border-color: #5A3E00; }

/* ── ⑫-13 模式卡图标 / 下拉框 / 开关拨钮 ───────────────────────── */
.mode .mi { filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .14)); }
.bc-sw i { background: rgba(255, 253, 246, .9); border: 2px solid var(--border); box-shadow: 0 2px 0 #F0E1BC; }
.bc-sw input:checked + i { background: linear-gradient(180deg, var(--brand-hi), var(--brand)); border-color: var(--brand-lo); box-shadow: 0 3px 0 var(--brand-lo); }
.bc-sw input:disabled + i { opacity: .5; box-shadow: none; }
.bc-sw i::after { background: #fff; box-shadow: 0 2px 4px rgba(120, 90, 50, .3); }
.im-send textarea { border-color: var(--border) !important; box-shadow: 0 2px 0 #F5E9CC !important; }
.mode .mi, .tag, .badge { position: relative; }

/* ════════════════════════════════════════════════════════════════
   ⑬ **终局安全网**（最后一段，权重最高）
   ⚠️ 凡「旧段落定义了 box-shadow」的 hover 规则，一律强制：
        · transform 归零（不抖）
        · 过渡里**不含 transform**（归零本身也不能带动画）
        · 阴影只在同族色内压深（不放大模糊半径 ⇒ 不会忽大忽小）
   ════════════════════════════════════════════════════════════════ */
@media (hover: hover) {
  .nav a:hover, .nav a.on:hover,
  .nav a:hover:not(.on):not(.dis),
  .btn:hover:not(:disabled), .btn:hover:not(:disabled):active,
  .btn.ghost:hover:not(:disabled), .btn.danger:hover:not(:disabled),
  .btn.warn:hover:not(:disabled),
  .panel:hover, .mode:hover, .mode.on:hover, .pcard:hover,
  .acard:hover, .acard.on, .seg button:hover, .pager b:hover, .chip:hover, .badge:hover,
  .selbar:hover {
    /* 🔴⑳-6（16:31 修）浮层类 `.drawer` / `.rmodal` / `.cmodal` 已**从本名单剔除**。
       它们靠 `transform: translate(-50%,-50%)` 做居中定位，被`transform:none`
       抹掉就会「鼠标进弹窗 → 弹窗跳到左上角」＝ 涛爷报的「弹窗跟着鼠标移动」。
       ⚠️ 以后**禁止**把浮层（drawer/rmodal/cmodal/lightbox）加回任何
          「hover 禁 transform」名单。 */
    transform: none !important;
  }
  .nav a, .btn, .btn.sm, .btn.primary, .btn.ghost, .btn.danger, .btn.warn,
  .panel, .mode, .pcard, .acard, .seg button, .pager b, .chip, .badge, .tag, .selbar {
    transition: box-shadow .16s linear, border-color .16s linear,
                background-color .16s linear, color .16s linear !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   ⑭ **修涛爷两条报障**（15:01）
     ① 「中间有一段颜色很淡，内容看不清」
     ② 「板块之间切换页面不对齐、抖一下」
   ════════════════════════════════════════════════════════════════════════

/* ── ⑭-1 🔴 对比度：暖灰在暖黄底上不够看 ─────────────────────────────
   涛爷报障原话：「中间都有一部分是颜色很淡的，导致有些内容都看不清楚」。

   实测根因（`/trade` 页逐元素量WCAG 对比度）：
     · `--text-3` 我调成 `rgb(171,150,112)`（暖灰）⇒ 在 `rgba(255,250,232)`
       的暖黄面板上只有 **3.66:1**，正文级文字**不达标**（WCAG AA 要 4.5:1）。
     · `trade.html:130` 那个**页面内联 `<style>`** 硬编码了 `.oqlab{color:#666}`
       —— **冷灰**落在暖黄底上，既不达标又和整站暖调冲突（这才是图里
       「订单管理」那排标签发灰发淡的直接原因）。⚠️ 内联样式优先级高于
       外部CSS，**只在app.css 里改它是改不动的** ⇒ 必须改 trade.html本身。

   ✅ 修法：把三个层级全部压到**可读区间**，同时保持暖调（不要退回纯灰）。
     · `--text-3` : `rgb(171,150,112)` 3.66:1 ⇒ **`#6F5C38`** ≈ 6.3:1
     · `--text-2` : `rgb(122,102,64)` 保持（实测已够）
     · `--text`   : `rgb(59,46,20)`  保持（高对比，不动）
   ⚠️ 注意：**不能只把 --text-3 加深就完事**，`.oqlab` 这类**硬编码 #666**
      必须逐页扫出来改，否则那几处永远是灰的。 */
:root {
  --text-3: #6F5C38;   /* 3.66:1 → 约 6.3:1（暖调保持不变，只是压深） */
}
/* 兜底：把散落在内联 style / 旧段落里的冷灰标签一并暖化+压深 */
.oqlab, .hint, .panel-h .hint, .sub, .lb, .muted,
.td-sub, .rl-sub, .pc-meta, .price-in span {
  color: var(--text-3);
}
/* 极淡占位（“—” “暂无”）也别淡到看不见 */
.empty, .td.empty { color: #6F5C38; }  /* 4.22→约6.3:1 */

/* ── ⑭-2 🔴 页面切换抖动：滚动条宽度差10px ──────────────────────────
   涛爷报障原话：「商品管理切换到订单管理的时候，很明显的抖动一下，页面不对齐」。

   实测根因：各页**内容高度差很大**（商品管理页高 1189px、订单管理 800px），
   于是长页出滚动条、短页不出。滚动条**实占 10px 宽**
   （`app.css` 里 `::-webkit-scrollbar { width: 10px }`），
   ⇒ 切换瞬间 `clientWidth` 从 1520 → 1510，**整页内容横向位移 10px**，
      这就是肉眼看到的「抖一下 + 右侧对不齐」。
   ⚠️ Playwright 默认是**覆盖式滚动条**（`clientWidth` 不变），所以在无头
      浏览器里**量不出来** —— 必须靠这个推理定位，别被"实测全绿"骗了。

   ✅ 正解：`html { scrollbar-gutter: stable }` —— **永远预留滚动条槽位**，
      有无滚动条都占同一宽度 ⇒ 切换时零位移。
   ⚠️ `stable`（而非 `stable both-edges`）只在右边留槽，不影响居中。
   ⚠️ 这是**布局层**修复，不涉及任何主题色，涛爷的视觉风格不受影响。 */
html {
  scrollbar-gutter: stable;
  /* 滚动条也暖化，跟B 方案同一族（原来是很冷的灰） */
  scrollbar-color: #E0C88A #FFF6E4;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: #FFF6E4; }
::-webkit-scrollbar-thumb {
  background: #E0C88A; border-radius: 6px;
  border: 2px solid #FFF6E4;
}
::-webkit-scrollbar-thumb:hover { background: #D0B264; }

/* ════════════════════════════════════════════════════════════════════════
   ⑮ **全站对比度扫尾**（15:01 涛爷「中间颜色很淡，看不清」的第二轮）
   ════════════════════════════════════════════════════════════════════════
   上一轮我只改了 `--text-3` 变量，结果用WCAG 脚本逐元素量才发现
   **还有 95 处不达标**，根因全部是「**硬编码在HTML 内联<style> 里的冷灰**」
   ——它们既不受变量控制，也不随主题变化，这就是涛爷看到「一段很淡」的真凶。

   ⚠️ 内联 `<style>` 优先级高于外部 CSS，**光在 app.css 里覆写是不生效的**
      （`:root` 变量能改，裸十六进制改不了）⇒ 必须**改 HTML 文件本身**。
      `grep -rn '#666\|#666d78\|#a0a0a8\|#9aa0aa\|#8a8a93\|#f2f2f5' *.html`
      —— 这是以后换主题后的**必查命令**。 */
:root {
  /* ★ 二次校准：先把变量压到稳妥区间（旧值 3.66:1 不达标） */
  --text-3: #6F5C38;   /* ≈6.3:1 达标 */
}
/* ── ⑮-1 卡片角标 `.n`：白字压**深色半透明底**，不是浅底 ─────────────
   旧值 `rgba(16,18,22,.72)` 在暖黄面板上会被背景透成米色 ⇒ 白字 1.07:1。
   ⇒ 底色改为**不透明**深棕，白字立刻回到 12:1 以上。 */
.pcard .ph .n {
  background: rgba(45, 34, 14, .92);
  color: #FFF6E4;
  border: 1px solid rgba(255, 230, 170, .5);
}

/* ── ⑮-2 表单提示 / 空状态（原来是 #a0a0a8，2.4:1）───────────────── */
.nm-hint, .nm-ai-empty, .nm-sec h4 .nm-hint {
  color: #6F5C38;
}

/* ── ⑮-3 订单字数计数（原来是 #9aa0aa，2.6:1）───────────────────── */
.fbrow2 .fblen2 { color: #7A6642; }

/* ── ⑮-4 订单号 / 次要标识（原来是 #8a8a93，3.2:1）────────────────── */
.oid, .rtnow, .odcard h4, .xbox .bd { color: #6F5C38; }

/* ── ⑮-5 右下角调试条（原138,117,70 = 4.34:1，差一点点）────────── */
.wb-idb-host { color: #5F4E2E; }

/* ── ⑮-5b 状态色在浅底上压深一档（绿 4.38 / 琥珀 4.42 → 达标）────────
   WCAG AA 要 4.5:1，`#15803D` 在`#DFF5E4` 上只有 4.38 —— 差一点。
   ⚠️ 只压深**文字色**，底色不动（--green-soft 保持浅色）。 */
.st-done, .st-ok { color: #0F6B33; }
.st-warn, .st-run { color: #8A4A00; }
/* 黄底上的黄字（config/trade 都有）—— 通用兜底 */
.cfgb-hint b, .fblen b, .cfgbox b, .hint b { color: var(--brand-ink); }
/* 只读输入框的灰字 */
.em-in[readonly], .input[readonly] { color: #6B5836; }

/* ── ⑮-6 🔴 黄底上的黄字必须压深（config.html 的「随机取一条」1.51:1）──
   `#FFC400` 当**文字色**落在 `#FFF8E6` 上几乎看不见 —— 这是最典型的
   「设计稿好看、真站看不清」：黄色**只能当底色，文字必须用深棕**。 */
b, strong, .warn-txt, .hint b, .panel-h .hint b {
  /* 只压深「继承 var(--brand) 当文字色」的那批，不动 --brand 本身 */
}
.ttxb, .pill.on b, .tag b { color: var(--brand-ink); }


/* ════════════════════════════════════════════════════════════════════════
   ⑰ **只治「文字显示的那一段」，玻璃 + 高光 + 厚边全部保留**
      （15:22 涛爷第三次修正：「那些按钮感觉不同了，没改之前好看，
        有高光什么的。我只需要你改掉文字显示那一段的渐变就好，其他恢复」）

   🔴🔴 **⑯ 段我做过头了**：把面板/卡片/弹窗/按钮高光全删成实色 ⇒ 立体感没了，
      按钮"看起来不同了"。**涛爷要的是容器继续漂亮（玻璃+高光），
      只把「字坐着的那块底」从半透明渐变换成实色（看得清）。**

   ✅ 实测定位（`tools/_probe_text_bg.py`，7 页逐元素量 alpha/渐变）：
     · **按钮类全是 GRAD**（`.btn` / `.btn.primary` / `.pillbtn` / `.fg-ghost` / `.warn`）
       —— 这些是**要保留高光**的，一概不碰。
     · **`--surface` 已被主题覆写成实色 `#FFFDF6`** ⇒ `.kpi` / `.fg-card` /
       `.mode` / `.pcard` 底色本来就实，**不需要再改**（⑯ 段白改了）。
     · 真正「字坐在半透明玻璃上」的只有两类：
        ① **表格**：`table.ordt`（订单/评价）、`.rr-table`（配置读回）
           —— 表头与数据行都是纯文字区，玻璃底 ⇒ 字发飘。
        ② **空状态 / 说明块**：`.empty` `.nm-ai-empty` `.trash-empty` `.nc-empty`
           —— 大字 + 浅底，最刺眼。
   ⚠️ 本段**只动上面这两类的 background**，**不动** background-image 以外的任何属性：
      不碰 box-shadow / border / radius / ::before / ::after / backdrop-filter / transform。
      ⇒ 容器的釉面高光与厚边**原样保留**，视觉风格回到⑯ 段之前。
   ════════════════════════════════════════════════════════════════════════ */

/* ── ⑰-1 表格：纯文字承载区，给实色底衬（玻璃高光留在面板上）────── */
table.ordt,
.rr-table {
  background: #FFFDF6 !important;
}
/* 表头：实色蜜黄 + 深棕字（黄底不能配黄字） */
table.ordt th,
.rr-table th {
  background: #FFF0CC !important;
  color: #5A3E00 !important;
}
/* 数据行：实色 + 斑马纹（行距不清就靠它） */
table.ordt td,
.rr-table td { background: #FFFDF6 !important; }
table.ordt tbody tr:nth-child(even) td,
.rr-table tbody tr:nth-child(even) td { background: #FFF9EC !important; }
/* 行分隔线压深，别让行与行糊在一起 */
table.ordt td, .rr-table td { border-bottom-color: #EFE1BE !important; }

/* ── ⑰-2 空状态 / 大字说明块：实色 + 足够对比 ────────────────────
   ⚠️ `.empty` 有 5 个页面各自的内联定义，颜色已各自压深；
      这里只给底色，**不改它们的 color**（避免又踩"变量改不动内联"的坑）。 */
.empty, .nm-ai-empty, .trash-empty, .nc-empty, .img-empty {
  background: #FFF6E2 !important;
}

/* ── ⑰-3 小标签：字小底浅，给实色（这些也是纯文字承载块）───────── */
.chip, .tag, .badge {
  background: #FFF3D4 !important;
}

/* ── ⑰-4 表单控件：实色白底（这条是真问题，保留）───────────────────
   ⚠️ 输入框半透明叠在半透明面板上 = 白上加白，输入框边界会消失。
      这是⑯ 段唯一**确实该留**的改动，不属于"恢复"范围。 */
input[type=text], input[type=number], input[type=search],
input[type=password], input[type=url], select, textarea, .search {
  background: #FFFFFF !important;
}
input[readonly], textarea[readonly], .em-in[readonly], .input[readonly] {
  background: #F4EEDA !important;
  color: #6B5836 !important;
}

/* ════════════════════════════════════════════════════════════════════════
   ⑱ **只治「页面正文区」的面板底：改成实色，按钮 / 卡片 / 导航原样不动**
      （15:44 涛爷终于说清范围：
       「按钮啊其他那些的设计保留现在的样式不变。但是我截图中这些区域的展示，
         就不要用渐变色变成跟半透明一样。意思就是说页面正文开始的这些
         文字内容展示的区域不要半透明。」）

   ✅ 精准定义（实测 `tools/_probe_panel.py`，7 页逐个 .panel 量出来的）：
      涛爷截图里「上架队列」「采集结果」「在售商品」「筛选查询」「店铺」
      ⇒ **就是 `.panel` 本体**，全站正文区都是它。

   🔴 只动 `.panel` 的两件事，**其余一个属性都不碰**：
      ① `background-image` → 去掉半透明渐变，底色换成**不透明实色 `#FFFDF6`**
      ② `::before` 顶部白高光 → 干掉（`rgba(255,255,255,.72)` 盖住 44% 高度，
         等于给标题和第一行文字蒙了张白纸 ⇒ 这才是"看着淡"的真凶）

   ⛔ **绝对不碰**（涛爷原话「按钮啊其他那些的设计保留现在的样式不变」）：
      `.btn` / `.btn.primary` / `.pillbtn` / `.nav a` / `.seg button` / `.mode` /
      `.pcard` / `.acard` / `.chip` / `.tag` / `.drawer` / `.rmodal` / `.cmodal`
      —— 它们的渐变、高光、厚边、hover 全部原样保留。

   ⚠️ **删掉 ::before 后层次不能塌**：靠 ①的实色 + 原有 `box-shadow: var(--shadow-2)`
      双层阴影 + 2px 暖边框撑住面板的立体感，视觉重量不减。
   ════════════════════════════════════════════════════════════════════════ */

/* ── ⑱-1 面板本体：半透明渐变 → 不透明实色 ────────────────────────
   ⚠️ 用 `background` 简写覆盖，才能连带干掉 background-image；
      若只写 background-color，linear-gradient 会**留在上层继续生效**。 */
.panel {
  background: #FFFDF6 !important;
  backdrop-filter: none !important;   /* 半透时把后面的光晕又透进来一层 = 又淡一层 */
}

/* ── ⑱-2 干掉面板顶部白高光（「蒙了张白纸」的元凶）──────────────────
   ⚠️ 用 display:none 不删选择器 —— 万一别处有依赖 ::before 的兄弟规则，
      删整条会让那些规则一起失效（⑯ 段踩过同样的坑，这里只删背景不删选择器）。 */
.panel::before {
  display: none !important;
  content: none !important;
}

/* ── ⑱-3 兜底断言：⚠️ 这条不是样式，是给**以后写新主题的人**看的
   ⚠️ 若哪天 `.btn` / `.mode` / `.pcard` 突然没有渐变和高光了，
        就是有人复制粘贴了本段的选择器 —— 回查，**不是本段该管的**。 */

/* ════════════════════════════════════════════════════════════════════════
   ⑳ **弹窗 / 弹层不许「跟着鼠标跑」**（16:16 涛爷报障：
      「弹窗模式的功能，点击弹窗后会随着鼠标移动」）

   🔴🔴 实测结论（`tools/_probe_dlg.py`，真点击 + 鼠标横扫弹窗内部）：
     · 弹窗 `.cmodal` 的 `getBoundingClientRect()` **一个像素都没变**（恒 537,415）
     · 全站 `*.js` / `*.html` **零** `mousemove` / `mouseover` / `clientX` 监听
     ⇒ 弹窗本身**没有跟随逻辑**，那"跟着鼠标跑"是**视觉错觉**：
       **光标移到哪个按钮上，哪个按钮就"长高"。**

   ⚠️ 元凶 = hover 时把 `box-shadow` 的**底边厚度加粗**：
        静止 `0 4px 0 #DFE3E9`  →  hover `0 7px 0 #DFE3E9`   （厚了 3px）
        底边是 `0 Npx 0 color`（零模糊实心偏移阴影）＝ 按钮的"厚度"。
        N 变大 ⇒ 按钮整体往下长一截 ⇒ 光标扫过一排按钮时，
        **它们轮流长高/缩回** ⇒ 观感 = "整排东西跟着鼠标蠕动"。

   ✅ 正解：**hover 绝不许改底边厚度 `0 Npx 0`**。
     · 底边 N 恒定（立体厚度是**静态**的）
     · hover 只允许：① 第二层柔扩散阴影变浓（blur 可变，它不占布局）
                  ② `inset` 高光略增强 ③ 边框色变黄 ④ `background` 微调
     · `transform` 一律 `none`（位移 = 闪烁 + 落点偏移）
   ⚠️ 这与已有铁律一致：「hover 不得放大模糊半径」—— 本条是它的孪生条款：
      **模糊半径可变，底边厚度不可变。**
   ⑳-5 🔴 按**实测静止值**逐族对齐（`tools/_probe_edge_rest.py` 量出来的）
   ────────────────────────────────────────────────────────────
   实测静止底边厚度：
     `.nav a`          = **2px**   ·  `.nav a.on`  = **3px**
     `.mode`           = **2px**   ·  `.mode.on`   = **4px**
     `.btn` 普通        = **3px**   ·  `.btn.primary` = **4px**
   ⇒ 上一段我一律写 4px，等于给「静止 3px 的族」又加了 1px ⇒ 守卫仍红。
   ✅ 这里**逐族写回自己的静止值**，hover 只加浓扩散层，底边一个像素都不动。 */

/* 普通按钮：锁 3px */
.btn:hover:not(:disabled), .btn:focus-visible, .btn.sm:hover:not(:disabled),
.btn.lg:hover:not(:disabled), .btn.pillbtn:hover:not(:disabled),
.btn.oqbtn:hover:not(:disabled), .btn.ghost:hover:not(:disabled),
.btn.danger:hover:not(:disabled), .btn.warn:hover:not(:disabled),
.btn.fg-ghost:hover:not(:disabled) {
  transform: none !important;
  box-shadow:
    0 3px 0 var(--pick-edge, #E0A400),
    0 14px 24px -10px rgba(120, 85, 15, .55),
    inset 0 1.5px 0 rgba(255, 255, 255, .82) !important;
  border-color: var(--brand);
}
/* 主按钮：静止就是 4px，hover 还是 4px */
.btn.primary:hover:not(:disabled), .btn.primary:focus-visible {
  box-shadow:
    0 4px 0 #C98C00,
    0 16px 26px -10px rgba(200, 140, 0, .60),
    inset 0 3px 0 rgba(255, 255, 255, .72) !important;
}
/* 导航：静止 2px，激活项静止 3px */
.nav a:hover:not(.on):not(.dis) {
  box-shadow:
    0 2px 0 rgba(80, 58, 10, .12),
    0 10px 18px -8px rgba(80, 58, 10, .45),
    inset 0 1.5px 0 rgba(255, 255, 255, .78) !important;
}
.nav a.on:hover, .nav a.on:focus-visible {
  box-shadow:
    0 3px 0 var(--brand-lo),
    0 12px 20px -8px rgba(200, 150, 0, .55),
    inset 0 2px 0 rgba(255, 255, 255, .6) !important;
}
/* 模式卡：静止 2px，激活态静止 4px */
.mode:hover, button.mode:hover {
  box-shadow:
    0 2px 0 #EBD49E,
    0 20px 34px -14px rgba(180, 130, 10, .52),
    inset 0 2px 0 rgba(255, 255, 255, .7) !important;
}
.mode.on:hover, button.mode.on:hover {
  box-shadow:
    0 4px 0 #E8B400,
    0 20px 34px -14px rgba(200, 150, 0, .55),
    inset 0 2px 0 rgba(255, 255, 255, .7) !important;
}

/* ⑳-6b（补回，⑳ 段去重时一并删掉了这两条）
   ⚠️ `:active` 用 `transition:none`：鼠标在按下瞬间移出时，避免"弹回去"形成二次闪动。 */
.btn:active:not(:disabled) { transition: none !important; }
/* 导航 / 卡片族：禁 transform 位移（浮层类除外，它们靠 transform 定位） */
.nav a:hover:not(.on):not(.dis),
.nav a.on:hover,
.seg button:hover:not(.on), .pager b:hover, .chip:hover, .tg:hover, .linkbtn:hover,
.tgmain:hover, .tgstall:hover, .selbar:hover,
.pcard:hover, .panel:hover, .acard:hover, .badge:hover {
  transform: none !important;
}

/* ⑳-7 禁用按钮不许用 opacity 表达（16:31 contrast 守卫实测 3 处不达标）
   ────────────────────────────────────────────────────────────
   实测（`_verify_contrast.py` → `/accounts` 3 处，最低 **1.57:1**）：
     fg=[199,203,209]  bg=[255,251,239]
   根因 `.btn:disabled { opacity:.45 }` —— **整体半透明**会把前景与背景
   一起拉向中间色 ⇒ 文字既发灰又和底色混在一起（与⑱ 段同源的坑）。
   ✅ 正解：**不用 opacity**，改用「实色去饱和底 + 明确的可读前景色」。
     · `opacity` 会连带把 box-shadow 一起变淡（立体感全丢）⇒ 一并解决。
     · 仍保留「明显不可用」的观感：底色变灰、边框变浅、cursor 提示改掉。 */
.btn:disabled {
  opacity: 1 !important;
  background: #EFE8D8 !important;
  background-image: none !important;
  color: #6E6250 !important;
  border-color: #D8CCB2 !important;
  box-shadow: 0 3px 0 #CFC2A6, 0 8px 14px -10px rgba(120, 100, 60, .35) !important;
  cursor: not-allowed;
}
/* ⚠️ 主按钮禁用：同样不用 opacity，但保住"这是个主按钮"的可辨识度 */
.btn.primary:disabled {
  background: #E6DCBE !important;
  color: #7A6B4E !important;
  border-color: #D3C6A6 !important;
}
/* ⚠️ ghost / warn / danger 禁用：不要用主色描边，免得看起来还能点 */
.btn.ghost:disabled, .btn.warn:disabled, .btn.danger:disabled {
  background: #F2EDE0 !important;
  color: #7A6E5C !important;
  border-color: #DCD2BC !important;
}
/* ⑳-7b busy 提示块（.abusy）：文字 4.30:1 也压深一点 */
.abusy { color: #8A4E08 !important; }

/* ⑳-8 禁用按钮配色压深（⑳-7 第一版太浅，实测只有 3.80:1 / 4.26:1）
   ────────────────────────────────────────────────────────────
   ⚠️「禁用」不等于「看不清」。做法：底更灰、前景更深，二者都往中间收但**留住 4.5:1**。
     · 前景压到 `#5A4E3C`（深琥珀灰）—— 在灰底上仍有 ≈7:1
     · 底色保持去饱和的 `#E4DCC6`，靠 `cursor:not-allowed` + 无主色描边表达"不可点" */
.btn:disabled {
  opacity: 1 !important;
  background: #E4DCC6 !important;
  background-image: none !important;
  color: #5A4E3C !important;
  border-color: #CDC0A2 !important;
  box-shadow: 0 3px 0 #C3B597, 0 8px 14px -10px rgba(120, 100, 60, .35) !important;
  cursor: not-allowed;
}
.btn.primary:disabled {
  background: #DCCFAE !important;
  color: #584C39 !important;
  border-color: #C6B896 !important;
}
.btn.ghost:disabled, .btn.warn:disabled, .btn.danger:disabled {
  background: #E9E3D3 !important;
  color: #5C5243 !important;
  border-color: #CFC5AC !important;
}

/* ════════════════════════════════════════════════════════════════════════
   ㉑ 表单控件统一高度（10-13 涛爷报「文字和排版没对齐，一高一低」）
   ────────────────────────────────────────────────────────────────────────
   🔴 根因：`select` 的 line-height 是 UA 默认的 `normal`（内容高 16px），
      而 `input` 继承了全局 1.6 行高（内容高 22.88px）。两者 padding/border
      完全相同（8px 上下 + 2px 上下边框），于是：
        select = 8+16   +8+2+2 = 36px
        input  = 8+22.88+8+2+2 = 42.88px     ← 差 6.88px
      而 `.params` 是 `align-items:flex-end`（底对齐）⇒ 矮的 select 整组被
      顶上去 6.88px，label 与控件**同时**错开半行 = 截图里的「一高一低」。

   ✅ 对策：不再依赖 line-height 巧合，**给控件一个显式统一高度**，让
      select / input / textarea 三者盒模型完全一致。
   ⚠️ 用 `min-height` 而非 `height`：`.rng` 里塞多控件时仍可自然撑高。
   ⚠️ 必须写 `box-sizing: border-box`，否则 2px×2 边框会把实际高度顶大 4px。
   ════════════════════════════════════════════════════════════════════════ */
:root {
  --ctl-h: 42px;          /* 统一控件高（.rng 多控件行会自动更高，不受此限） */
  --ctl-line: 24px;       /* 统一单行内容高，text 垂直居中不跳 */
}

/* ① 单行控件族：显式同高 + 同 line-height
   ⚠️ 必须用 `height` 不是 `min-height`：UA 给 `select` 的 line-height 是
      `normal`（`line-height:24px` 对它不生效），min-height 下 select 会被
      内容撑到 42、input 撑到 44，仍差 2px。写死 height + border-box 才 100% 等高。
   ⚠️ `.rng` 里每个控件各自是单行控件、各自吃满 42px；`.rng` 容器本身
      `align-items:center`，多控件时容器自然横向排列，不会被此规则压扁。 */
input[type=text], input[type=number], input[type=search],
input[type=password], input[type=url], input[type=tel],
select, .search {
  height: var(--ctl-h);
  min-height: var(--ctl-h);
  line-height: var(--ctl-line);
  box-sizing: border-box;
}
/* ⚠️ textarea 多行，**只给 box-sizing 不给高度**（否则固定死没法多行） */
textarea {
  box-sizing: border-box;
  line-height: 1.6;
}

/* ② `.field` 族：label 行高也钉死，杜绝不同浏览器 label 高度飘 */
.field label,
.setrow label,
.filters label {
  line-height: 20px;
  min-height: 20px;
}

/* ③ 字段组：改**顶对齐**为「等高顶对齐」——
   底对齐本身没错，但一旦控件高度不齐就会错位。改成 stretch 让每列
   自己顶部对齐，控件顶边整齐；控件同高时两者等价，兼容性最好。 */
.params,
.filters {
  align-items: flex-start;
}

/* ④ 扫尾：把「专属规则写死高度」的漏网控件也拉齐（10-13 实测揪出 6 个）
   ⚠️ **豁免两类**，它们天生不是标准输入框，拉齐反而会变形：
     - `.kwbox input` / `.srcbox input` / `.urlbox input`：透明底无边框的**内嵌搜索框**，
       高度由外层容器（min-height:44px）决定，拉到 42 会顶破容器；
     - `input[type=checkbox|radio]`：本来就是 15px 小方块。
   ⚠️ `textarea` 也不在 ① 的选择器里（多行框各有各的高度，正常）。 */
input[type=text], input[type=search],
.sp input, .filters input, .dsec input,
.cf-body input, .cmodal input, .rl-body input, .cd-m-b input,
.fg-card-input input, .fgd-body input {
  height: var(--ctl-h);
  min-height: var(--ctl-h);
  box-sizing: border-box;
}
/* 内嵌搜索框只统一**行高**不碰高度**（高度交给容器 min-height 管） */
.kwbox input, .srcbox input, .urlbox input {
  height: auto;
  min-height: 0;
  line-height: var(--ctl-line);
}
/* `datetime-local` / `time` 在 Windows 上有 UA 私有内边距 + 日历图标，
   不显式压 height 会被撑到 52~56px，必须一起钉死 */
input[type=datetime-local], input[type=date], input[type=time] {
  height: var(--ctl-h);
  min-height: var(--ctl-h);
  box-sizing: border-box;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}
/* 卡片头部的紧凑工具条（`.sp`）用小一号高度，
   否则 42px 会把面板头撑得过高 —— 但**同一条工具条内必须彼此等高** */
.sp input, .sp select {
  --ctl-h: 36px;
}

/* ════════════════════════════════════════════════════════════════════════════
   ★★ 2026-10-14 涛爷令（二次）：「**整个胶囊**都改成这种样式」
   本段负责 = **只有胶囊本体**，胶囊里的数字一概不碰（见下方「数字徽标」）。

   📌 历史（别重犯）：曾经还先做过一段「数字徽标玻璃化」（给 `<b>` / `.cnt`
   加玻璃底 + `min-width:26px` + `min-height:20px` + `padding` + 圆角 14px），
     2026-10-14 涛爷令**整段撤销**——原话：
       「你胶囊已经做除样式了，那你之前弄错的数字胶囊就没必要了啊，恢复它。
         要不然它把整个胶囊整得很大，看起来不好看。」
     ⇒ 根因：**给数字加 `min-width`/`min-height`/`padding` 会把整个胶囊撑大**
       （胶囊高度 = max(自身内容, 数字行高+内边距)），单行数字看着鼓、整体看着肿。
     🚨 因此本段铁律追加一条：
       **不许给胶囊内的数字/徽标加 `min-width`、`min-height`、`padding`、
         `border-radius`、`background`**。数字只允许改 `color` / `font-weight`
         / `text-shadow`。要强调就靠胶囊本体，不要靠给数字穿衣服。
       原始定义回落处：itemmgr.html:19 `.mtabs button b` / itemmgr.html:29
       `.ktabs button b`（本来就只有 font-weight，无背景无边框）；
       admin.html:54-57 `.adm-tabs button .cnt`（唯一真·有底色的徽标）。

   改造范围（全站同款胶囊，共 4 类，都在「带边框 + 白底 + 999px圆角」这个家族里）：
     ① .mtabs button:not(.btn)  —— 商品管理：商品管理 / 批量小刀（itemmgr）
     ② .ktabs button           —— 全部在售 / 滞销品 / 零增长 / 审核中 / 已小刀 / 待小刀
     ③ .ttabs button           —— 订单管理（trade）
     ④ .cfg-tabs button        —— 功能配置（config）
        ⚠️ **这项目前是死样式**：`config.html:286-292` 有 `.cfg-tabs` 的 CSS，
           但 HTML 里 `class="cfg-tabs"` 出现 **0 次**（实测 grep -c = 0），
           页面现在根本没有页签（只有 `.cfgb` 那些卡片）。
           规则留着无害（将来启用即生效），**但别以为「功能配置页签改了」**——
           实测抓到 0 个元素是正常的，不是失效。
     ⑤ .adm-tabs button        —— 管理端页签（**紫色系**，玻璃化时保留紫调，见下）

   ⚠️🔴 **刻意不动**的两种页签（它们不是胶囊，是**下划线式**，套玻璃会四不像）：
     · 下划线式页签 —— `border-bottom:2px solid transparent` + `background:none`
     · `.ostabs`（订单页：订单状态筛选）—— `border:none; background:none` + `::after` 画下划线
     判据：容器/按钮样式里出现 `border-bottom: 2px solid transparent` 或 `border:none; background:none`
     ⇒ 是下划线式，**不要**往这段里加。

   🔴🔴 为什么每条都带 `body ` 前缀（别删）：
     `.mtabs button:not(.btn)` / `.ktabs button` / `.ttabs button` / `.cfg-tabs button`
     都定义在**各自页面的内联<style> 里**（itemmgr.html / trade.html / config.html），
     而内联 `<style>` 在 `<link href="/static/app.css">` **之后**
     ⇒ 同特异度时**它赢**，光「写在文件末尾」压不住，必须靠 `body ` 提权。
     `.adm-tabs` 同理（admin.html 内联）。

   🔴 铁律（同第一段，再强调一次）：
     · **hover 绝不写 transform**，只改阴影/底色/描边。
       `.btn:hover{translateY+rotateX}` 会造成光标出界→hover 失效→落回→又盖回
       的**无限振荡**，加上 rotateX 生成合成层 ⇒ 命中判定滞后 ⇒「闪 + 点不中」。
     · **transition 里绝不能出现 transform**。
     · `position:relative` + `overflow:hidden` + `::after pointer-events:none`
       三件套必须成套，否则高光条溢出胶囊 / 挡住文字点击。
   ════════════════════════════════════════════════════════════════════════════ */

/* ── ① 胶囊本体 · 静止态：半透明玻璃（非激活） ──────────────────── */
body .mtabs button:not(.btn),
body .ktabs button,
body .ttabs button,
body .cfg-tabs button {
  position: relative;
  overflow: hidden;
  color: #5C4A28;
  /* 半透明玻璃底：上白下奶黄，能透出页面后面的光（这是「玻璃感」的来源） */
  background: linear-gradient(180deg, rgba(255, 255, 255, .72), rgba(255, 249, 232, .52));
  -webkit-backdrop-filter: saturate(1.6) blur(7px);
  backdrop-filter: saturate(1.6) blur(7px);
  border: 2px solid #EBD9AE;
  /* 双层阴影：下沿实体撑厚度 + 上沿柔散撑空气感（与 .btn 同一套语言）
     🔴 下沿厚度是**调过的**：第一版给到 `0 3px 0` + 柔散半径 18px，
        截图实锤 = 胶囊「浮起来」，看着像颗鸡蛋。
        现在压到 `0 2px 0` + 半径 14px，影子贴地，才像按钮。 */
  box-shadow: 0 2px 0 #F0E1BC,
              0 7px 14px -10px rgba(180, 130, 10, .38),
              inset 0 1px 0 rgba(255, 255, 255, .9);
  text-shadow: 0 1px 0 rgba(255, 255, 255, .6);
  transition: box-shadow .16s ease, background-color .16s ease,
              border-color .16s ease, color .16s linear;
}

/* ── ② 顶部高光条：拟物的「光从上面来」（.btn::after 同款） ─────── */
body .mtabs button:not(.btn)::after,
body .ktabs button::after,
body .ttabs button::after,
body .cfg-tabs button::after {
  content: ""; position: absolute; left: 10%; right: 10%; top: 2px; height: 44%;
  border-radius: 999px; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .92), rgba(255, 255, 255, 0));
}

/* ── ③ hover：底色转亮 + 影子压深，**绝不写 transform**（铁律） ──── */
body .mtabs button:not(.btn):hover,
body .ktabs button:not(.btn):hover,
body .ttabs button:not(.btn):hover,
body .cfg-tabs button:not(.btn):hover {
  background: linear-gradient(180deg, rgba(255, 255, 255, .92), rgba(255, 246, 224, .68));
  border-color: #EFCF7E;
  color: var(--brand-ink);
  box-shadow: 0 3px 0 #E6D3A2,
              0 11px 20px -12px rgba(180, 130, 10, .46),
              inset 0 1px 0 rgba(255, 255, 255, .98);
}

/* ── ④ 激活态：琥珀玻璃胶囊（跟 `.btn.primary` 同族） ───────────── */
body .mtabs button:not(.btn).on,
body .ktabs button.on,
body .ttabs button.on,
body .cfg-tabs button.on {
  color: var(--brand-ink);
  font-weight: 700;
  /* 三段黄渐变（顶亮→ 中纯 → 底深），比原来的 135deg 两段更有体积 */
  background: linear-gradient(180deg,
              rgba(255, 245, 198, .96) 0%,
              rgba(255, 209, 51, .92) 55%,
              rgba(240, 173, 0, .88) 100%);
  border-color: #DE9E00;
  box-shadow: 0 3px 0 #C98C00,
              0 13px 22px -11px rgba(200, 140, 0, .5),
              inset 0 3px 0 rgba(255, 255, 255, .72);
  text-shadow: none;
}
/* 激活态的高光条要更强（亮面在上） */
body .mtabs button:not(.btn).on::after,
body .ktabs button.on::after,
body .ttabs button.on::after,
body .cfg-tabs button.on::after {
  background: linear-gradient(180deg, rgba(255, 255, 255, .96), rgba(255, 255, 255, 0));
}
/* 激活态 hover：只压深一档（不动几何） */
body .mtabs button:not(.btn).on:hover,
body .ktabs button.on:hover,
body .ttabs button.on:hover,
body .cfg-tabs button.on:hover {
  background: linear-gradient(180deg,
              rgba(255, 250, 220, .98) 0%,
              rgba(255, 220, 80, .95) 55%,
              rgba(248, 186, 10, .92) 100%);
  box-shadow: 0 6px 0 #C98C00,
              0 22px 34px -12px rgba(200, 140, 0, .6),
              inset 0 3px 0 rgba(255, 255, 255, .8);
}

/* ── ⑤ 管理端 `.adm-tabs`：紫色系，只做「同款玻璃化」不换色 ──────── */
body .adm-tabs button {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, rgba(255, 255, 255, .74), rgba(248, 246, 255, .54));
  -webkit-backdrop-filter: saturate(1.5) blur(7px);
  backdrop-filter: saturate(1.5) blur(7px);
  border: 2px solid #DCD4F2;
  box-shadow: 0 3px 0 #E4DEF5,
              0 9px 18px -10px rgba(90, 60, 170, .34),
              inset 0 1px 0 rgba(255, 255, 255, .9);
  transition: box-shadow .16s ease, background-color .16s ease,
              border-color .16s ease, color .16s linear;
}
body .adm-tabs button::after {
  content: ""; position: absolute; left: 10%; right: 10%; top: 2px; height: 44%;
  border-radius: 999px; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .92), rgba(255, 255, 255, 0));
}
body .adm-tabs button:hover {
  background: linear-gradient(180deg, rgba(255, 255, 255, .94), rgba(246, 243, 255, .72));
  border-color: #C4B4EE;
  box-shadow: 0 4px 0 #D2C6F2,
              0 14px 26px -12px rgba(90, 60, 170, .4),
              inset 0 1px 0 rgba(255, 255, 255, .98);
}
/* 激活态保留紫色渐变，但加内高光 + 玻璃感（与 .adm-tabs button.on 同色系） */
body .adm-tabs button.on {
  background: linear-gradient(180deg,
              rgba(178, 158, 246, .96) 0%,
              rgba(124, 92, 214, .94) 55%,
              rgba(98, 70, 186, .92) 100%);
  border-color: #5B40B0;
  color: #fff;
  box-shadow: 0 4px 0 #4E3596,
              0 16px 26px -10px rgba(90, 60, 170, .5),
              inset 0 3px 0 rgba(255, 255, 255, .45);
}
body .adm-tabs button.on::after {
  background: linear-gradient(180deg, rgba(255, 255, 255, .7), rgba(255, 255, 255, 0));
}
body .adm-tabs button.on:hover {
  box-shadow: 0 6px 0 #4E3596,
              0 22px 34px -12px rgba(90, 60, 170, .55),
              inset 0 3px 0 rgba(255, 255, 255, .55);
}


/* ================================================================
   退款详情弹窗（2026-10-15 涛爷令：详情里要看退款理由/买家图片/买家自填内容）
   ================================================================ */

.rf-modal { width: min(880px, 94vw); }

.rf-loading { padding: 40px 0; text-align: center; color: var(--text-2); }

/* 🔴【已废弃 · 10-15】顶部四个 KPI 方块 —— 已被下面 `.rf-sum` 一行摘要替代。
   涛爷原话：「排版太繁琐太占用空间了，不要每个内容都占据一行。」
   保留仅为不打断旧 DOM 探测。 */
.rf-kpis { display: none; }
.rf-kpi { display: none; }

/* ── 一行紧凑摘要（10-15 第三条令的正解）── */

/* ── 内容区块 ── */
.rf-sec { border: 1px solid var(--border); border-radius: var(--r-md);
          padding: 14px 16px; margin-bottom: 14px; }
.rf-sec > h4 { margin: 0 0 10px; font-size: calc(14.5px * var(--fs)); font-weight: 600;
               display: flex; align-items: center; gap: 8px; }
.rf-sec > h4 .rf-tag { font-size: calc(11.5px * var(--fs)); font-weight: 500;
                       color: var(--text-2); background: var(--surface-2);
                       border: 1px solid var(--border); border-radius: 999px;
                       padding: 1px 8px; }

/* 买家自填内容：引号气泡，让人一眼看出「这是买家自己写的」 */
.rf-note { background: var(--brand-soft); border-left: 3px solid var(--brand);
           border-radius: 0 var(--r-sm) var(--r-sm) 0; padding: 9px 13px;
           margin-bottom: 8px; font-size: calc(14px * var(--fs)); line-height: 1.65;
           color: #4A3A00; white-space: pre-wrap; word-break: break-word; }
.rf-note:last-child { margin-bottom: 0; }

/* 买家上传的图片缩略图 */
.rf-imgs { display: flex; flex-wrap: wrap; gap: 10px; }
.rf-img { width: 92px; height: 92px; border-radius: var(--r-sm); overflow: hidden;
          border: 1px solid var(--border-strong); cursor: zoom-in; background: var(--surface-2);
          transition: box-shadow .16s ease, border-color .16s ease; }
.rf-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rf-img:hover { border-color: var(--brand);
                box-shadow: 0 6px 16px -8px rgba(180, 130, 10, .5); }
.rf-empty { color: var(--text-2); font-size: calc(13.5px * var(--fs)); }

/* 处理时间线 */
.rf-tl { position: relative; padding-left: 18px; }
.rf-tl::before { content: ''; position: absolute; left: 5px; top: 6px; bottom: 6px;
                 width: 2px; background: var(--border); border-radius: 2px; }
.rf-tl-item { position: relative; margin-bottom: 14px; }
.rf-tl-item:last-child { margin-bottom: 0; }
.rf-tl-item::before { content: ''; position: absolute; left: -16px; top: 5px;
                      width: 9px; height: 9px; border-radius: 50%;
                      background: var(--surface); border: 2px solid var(--border-strong); }
.rf-tl-item.is-buyer::before { border-color: var(--brand); background: var(--brand); }
.rf-tl-top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.rf-tl-txt { font-weight: 600; font-size: calc(13.8px * var(--fs)); }
.rf-tl-time { color: var(--text-2); font-size: calc(12.7px * var(--fs)); }
.rf-tl-tips { margin-top: 4px; font-size: calc(13px * var(--fs)); color: var(--text-2);
              line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
.rf-tl-pics { margin-top: 6px; display: flex; gap: 8px; }
.rf-tl-pics img { width: 54px; height: 54px; border-radius: var(--r-sm);
                  border: 1px solid var(--border); cursor: zoom-in; object-fit: cover; }

/* ── 底部操作条 ── */
.rf-acts { display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
           padding-top: 14px; margin-top: 4px; border-top: 1px solid var(--border); }
.rf-acts .rf-sp { flex: 1; }
.rf-warn { color: #B45309; font-size: calc(13px * var(--fs)); line-height: 1.6;
           background: #FFF7E6; border: 1px solid #FBE3A8; border-radius: var(--r-sm);
           padding: 8px 11px; margin-top: 10px; }

/* ── 同意/拒绝 的内联操作区（不叠第二个弹窗，见 trade.html 注释）── */
.rf-form { margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--border-strong); }
.rf-form h5 { margin: 0 0 10px; font-size: calc(14px * var(--fs)); font-weight: 600; }
.rf-field { margin-bottom: 12px; }
.rf-field > label { display: block; font-size: calc(13px * var(--fs)); color: var(--text-2);
                     margin-bottom: 5px; }
.rf-field select, .rf-field textarea, .rf-field input[type="text"] {
    width: 100%; padding: 9px 11px; border: 1px solid var(--border-strong);
    border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
    font-size: calc(14px * var(--fs)); font-family: inherit; }
.rf-field select:focus, .rf-field textarea:focus, .rf-field input:focus {
    outline: none; border-color: var(--brand);
    box-shadow: 0 0 0 3px rgba(255, 196, 0, .22); }
.rf-field textarea { min-height: 76px; resize: vertical; line-height: 1.6; }
.rf-field .rf-tip { font-size: calc(12.3px * var(--fs)); color: var(--text-2); margin-top: 4px; }

/* 🔴🔴【已废弃 · 10-15】支付宝授权 iframe 的样式全部作废。
   原因：`TBWVPayPasswrdValidateHandler.checkPayPasswordAction` 只在
   KUN / FISH_WEB / OUTER_WINDVANE / SSR 四个**App 容器**里注册，
   浏览器根本拿不到 iframe ⇒ 前端已删掉整条 iframe 链路。
   ⛔ 别再拿这几个 class 去「把密码框显示出来」—— 协议层不存在，
   自己画的框收了密码也传不出去。保留仅为不打断旧 DOM 探测。 */
.rf-ali-box { display: none; }
.rf-ali-frame { display: none; }

/* ── 紧凑摘要行（10-15 第三条令：「不要每个内容都占据一行」）──
   把原来 4 个 KPI 方块压成**一行**，flex + 自动换行。 */
.rf-sum { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px;
          padding: 9px 12px; margin-bottom: 14px;
          background: var(--surface-2); border: 1px solid var(--border);
          border-radius: var(--r-md); font-size: calc(13px * var(--fs)); }
.rf-sum-i { color: var(--text-2); white-space: nowrap; }
.rf-sum-i > span { color: var(--ink); margin-left: 4px; }
.rf-sum-i.b > span { font-weight: 700; color: var(--brand-ink);
                    font-size: calc(15.5px * var(--fs));
                    font-variant-numeric: tabular-nums; }
.rf-sum-i.t > span { color: #B45309; font-weight: 600; }

/* 小节内的小标题（「买家补充说明」这种二级标题，不占一整块） */
.rf-sub2 { font-size: calc(12.5px * var(--fs)); color: var(--text-2);
           margin: 8px 0 6px; font-weight: 500; }
/* 灰色小字补充说明 */
.rf-tip2 { font-size: calc(12.3px * var(--fs)); color: var(--text-2); line-height: 1.6;
           margin-top: 6px; }
/* 买家自填内容 = 黄色气泡（.rf-note.is-buyer 是它的变体，
   与平台系统文案区分开） */
.rf-note.is-buyer { background: var(--brand-soft); border-left: 3px solid var(--brand);
                    color: #4A3A00; }

/* 「展开全部 N 条 ▾」——时间线默认只出最近 3 条 */
.rf-more { margin-top: 4px; padding: 4px 12px; font-size: calc(12.5px * var(--fs));
           color: var(--brand-ink); background: var(--brand-soft);
           border: 1px solid #F2CE6A; border-radius: 999px; cursor: pointer;
           font-family: inherit; transition: box-shadow .16s ease, border-color .16s ease; }
.rf-more:hover { border-color: var(--brand);
                 box-shadow: 0 4px 12px -8px rgba(180, 130, 10, .55); }

/* ── 同意退款的提示弹窗（10-15 第三条令：只讲一句话，引导去手机 App）── */
.rf-confirm { text-align: center; }
.rf-confirm-t { font-size: calc(16px * var(--fs)); font-weight: 700;
                color: var(--ink); margin-bottom: 10px; }
.rf-confirm-b { font-size: calc(13.5px * var(--fs)); color: var(--text-1, var(--text-2));
                line-height: 1.85; margin-bottom: 4px; }
.rf-confirm-b strong { color: var(--brand-ink); }

/* ── 订单抽屉里的退款块（10-15 第二条令：详情进「原来的详情按钮」抽屉）── */
.rf-drawer-block { border: 1px solid #F2CE6A; border-radius: var(--r-md);
                   background: linear-gradient(180deg, var(--brand-soft) 0%, transparent 90px),
                               var(--surface);
                   padding: 12px 14px; margin-bottom: 14px; }
.rf-drawer-block .rf-loading { padding: 22px 0; }

/* ── 大图查看（独立一层，必须高于 .rmodal 的 56）── */
.rf-mask2 { z-index: 57; }
.rf-imgbox { z-index: 58; width: min(1000px, 94vw); background: var(--surface);
             padding: 16px; }
.rf-img-x { position: absolute; top: 8px; right: 8px; z-index: 2; }
.rf-img-full { display: block; width: 100%; max-height: 82vh; object-fit: contain;
               border-radius: var(--r-sm); background: #fff; }

/* ════════════════════════════════════════════════════════════════════════
   ㉒ 全站「高光玻璃」统一（10-15 涛爷令：本段是唯一事实源，放最后 = 压住上面所有历史段）
   ────────────────────────────────────────────────────────────────────────
   令：全站所有**能点**的按钮和胶囊状模块 = 同一个高光玻璃 UI。
   ⚠️ 两条已拍板的例外（别再翻案）：
     ① `.mode` 大模式卡 = 卡片不是按钮 ⇒ 保持白橙底，**只有选中态 `.on` 变玻璃黄**。
     ② `.danger` 淡红 / `.warn` 淡琥珀 = 语义色，**保留**（删除类一眼可辨，别误点）。
   ⚠️ disabled 一律走 ⑳-8 段的暖米灰，**不许**只靠 opacity 变淡（那还是黄的）。
   ⚠️ 本段只写 `background` / `border-color` / `box-shadow` / `color`
      ——⛔ 绝不动 `min-width`/`min-height`/`padding`（MEMORY「给数字穿衣服」铁律）。
   ════════════════════════════════════════════════════════════════════════ */

/* ── ① 玻璃黄的变量化写法（与 .btn 基准同一套语言） ─────────────── */
:root {
  --glass-bg:   linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 55%, var(--brand-lo) 100%);
  --glass-bd:   #DE9E00;                /* 实体边 */
  --glass-bd-on: var(--brand-lo);       /* 选中态边（更深的黄，压得住） */
  --glass-sh:   0 3px 0 #C98C00, 0 8px 16px -10px rgba(180, 130, 10, .45);
}

/* ── ② 胶囊族页签：`.mtabs` / `.ktabs` / `.ttabs` / `.cfg-tabs`
      3518 段把它们做成了「半透明白玻璃」（rgba(255,255,255,.72)）= 近白米色
      ⇒ 与「能点的都是玻璃黄」冲突。本段把**静止态**也刷成玻璃黄，
      只保留 `::after` 顶部高光条与「不加 transform」的铁律。 ─────── */
body .mtabs button:not(.btn),
body .ktabs button,
body .ttabs button,
body .cfg-tabs button {
  color: var(--brand-ink);
  background: var(--glass-bg);
  -webkit-backdrop-filter: none;       /* 底已不透明，留着只会糊文字 */
  backdrop-filter: none;
  border: 2px solid var(--glass-bd);
  box-shadow: var(--glass-sh);
  text-shadow: none;
}
body .mtabs button:not(.btn):hover,
body .ktabs button:hover,
body .ttabs button:hover,
body .cfg-tabs button:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%);
  border-color: #E0A400;
  color: var(--brand-ink);
}
body .mtabs button:not(.btn).on,
body .ktabs button.on,
body .ttabs button.on,
body .cfg-tabs button.on {
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 45%, var(--brand-lo) 100%);
  border-color: var(--glass-bd-on);
  color: var(--brand-ink);
  box-shadow: 0 3px 0 var(--brand-lo), 0 10px 18px -10px rgba(180, 130, 10, .55);
}

/* ── ③ `.mode` 大模式卡：涛爷拍板 = 卡片保持白橙底，**只有选中态变玻璃黄**
      ⚠️ 选中也**不刷满**：四个大卡并排全黄会压得慌，所以选中态用
      「玻璃黄 + 更深实体边 + 更厚下沿」，跟静止态的白橙拉开层级。 ─── */
.mode.on {
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 55%, var(--brand-lo) 100%);
  border-color: var(--brand-lo);
  box-shadow: 0 4px 0 var(--brand-lo), 0 18px 30px -14px rgba(200, 150, 0, .55);
}
.mode.on .mt i { color: var(--brand-ink); opacity: .82; }

/* ── ④ 图标类按钮：`×` 关闭、抽屉关闭、大图关闭
      这些原先是 `background:none` / 近白透明 ⇒ 可点却不是玻璃黄。
      做成**小圆玻璃钮**（尺寸不动，只换皮）。 ─────────────────── */
.close-x, .rmodal-x, .cd-m-x, .rc-d-x, .rf-img-x {
  border-radius: 50%;
  border: 2px solid var(--glass-bd);
  background: var(--glass-bg);
  color: var(--brand-ink);
  box-shadow: var(--glass-sh);
  transition: box-shadow .16s ease, background-color .16s linear, color .16s linear;
}
.close-x:hover, .rmodal-x:hover, .cd-m-x:hover, .rc-d-x:hover, .rf-img-x:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%);
  border-color: #E0A400;
  color: var(--brand-ink);
}

/* ── ⑤ 下拉/触发类胶囊：`.bc-dd-btn`（批次下拉）、`.oqbtn`（订单查询）
      `.oqbtn` 本是 `.btn` 子类已被兜住，这里只补非 `.btn` 的胶囊。 ── */
.bc-dd-btn {
  background: var(--glass-bg);
  border: 2px solid var(--glass-bd);
  color: var(--brand-ink);
  box-shadow: var(--glass-sh);
}
.bc-dd-btn:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%);
  border-color: #E0A400;
}

/* ── ⑥ 案例卡内的两颗小胶囊：`.case-shot`（看处罚截图）/`.case-more`（展开全文）
      —— 资源页「违规与起诉案例」里逐条可点的胶囊。 ────────────────── */
.case-shot, .case-more {
  background: var(--glass-bg);
  border: 2px solid var(--glass-bd);
  color: var(--brand-ink);
  box-shadow: var(--glass-sh);
  border-radius: 999px;
}
.case-shot:hover, .case-more:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%);
  border-color: #E0A400;
}

/* ── ⑦ disabled 统一兜底（**最后一道**，覆盖所有子类的黄底）
      ⚠️ 为什么必须 `!important` + `opacity:1`：`.btn:disabled` 早期只写
      `opacity:.45` ⇒ 黄底只是「变淡」，computed 出来还是黄的 ⇒ 守卫判
      「disabled 不是灰的」（首跑 11 条）。这里显式换成暖米灰实色。
      ⚠️ `background-image:none` 是必须的：光改 `background` 不够，
      父级渐变会从 `background-image` 漏上来。 ────────────────────── */
.btn:disabled,
.btn.primary:disabled,
.btn.ghost:disabled,
.btn.capsule:disabled,
.btn.oqbtn:disabled,
body .mtabs button:disabled,
body .ktabs button:disabled,
body .ttabs button:disabled,
body .cfg-tabs button:disabled,
.seg button:disabled,
.pager b:disabled {
  opacity: 1 !important;
  background: #E4DCC6 !important;
  background-image: none !important;
  color: #5A4E3C !important;
  border-color: #CDC0A2 !important;
  box-shadow: 0 3px 0 #C3B597, 0 8px 14px -10px rgba(120, 100, 60, .35) !important;
  cursor: not-allowed;
}

/* ── ⑧ 跨页兜底：各页面 HTML **内联 <style>** 里的按钮/页签
      ⚠️ 为什么这批必须 `!important`：`trade.html:141 .oqbtn`、
      `resource.html:172 .case-more`、`cards.html:264 .cd-m-x`、
      `resource.html:238 .rc-d-x` 全都写在**各自 HTML 的内联样式**里。
      内联 <style> 与外链 app.css **同属作者样式**（优先级 0,1,0 打平），
      谁写在后面谁赢 —— 外链的通常在后，但不可靠 ⇒ 这里一律 `!important`。
      ⛔ 仍然只换 `background`/`border-color`/`box-shadow`/`color`，不动尺寸。
      ⚠️ `.ostabs` **故意不在这里** —— 它是订单页的下划线式页签
         （`trade.html:150 border:none; background:none` + `::after` 画下划线），
         守卫 `_verify_glass_capsule.py` B1/B3 明令不许玻璃化。
      ⚠️ `.rctabs` / `.rc-tabs` 是**臆造的选择器**，仓里查无此名 ⇒ 已删。
         资源页真实容器是 `#csTabs`（见⑫ 段）。别再凭感觉编类名。 */
.oqbtn, .case-more, .case-shot, .cd-m-x, .rc-d-x {
  background: var(--glass-bg) !important;
  background-image: var(--glass-bg) !important;
  border: 2px solid var(--glass-bd) !important;
  color: var(--brand-ink) !important;
  box-shadow: var(--glass-sh) !important;
  border-radius: 999px;
  text-decoration: none;
}
.oqbtn:hover, .case-more:hover, .case-shot:hover, .cd-m-x:hover, .rc-d-x:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  background-image: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  border-color: #E0A400 !important;
  color: var(--brand-ink) !important;
  text-decoration: none;
}
/* 关闭类按钮保持圆形（内联样式把 width/height 写死了，这里只改形状不改尺寸） */
.cd-m-x, .rc-d-x { border-radius: 50% !important; }

/* ── ⑨ 跨页 disabled 兜底（含内联样式页） ─────────────────────── */
.oqbtn:disabled, .case-more:disabled, .case-shot:disabled,
.cd-m-x:disabled, .rc-d-x:disabled {
  opacity: 1 !important;
  background: #E4DCC6 !important;
  background-image: none !important;
  color: #5A4E3C !important;
  border-color: #CDC0A2 !important;
  box-shadow: 0 3px 0 #C3B597, 0 8px 14px -10px rgba(120, 100, 60, .35) !important;
  cursor: not-allowed;
}

/* ── ⑩ 补漏：页签与裸按钮
      · `.ai-tabs`（AI 弹窗两页签，app.css:734 下划线式 ⇒ 改成胶囊玻璃）
      · `.adm-tabs`（admin 侧栏页签，原**紫色渐变** `rgba(178,158,246)` ⇒
        统一到闲鱼黄，否则 admin 页是全站唯一的紫色孤岛）
      · `#btnQuit`（admin 顶栏「退出」，裸 button 无 class）
      · `.cs-nav button`（资源页分页：resource.js:671 用裸 `<button data-act>`，
        没 class 只能按容器/id 兜） ─────────────────────────────────── */
.ai-tabs button, .adm-tabs button {
  background: var(--glass-bg) !important;
  background-image: var(--glass-bg) !important;
  border: 2px solid var(--glass-bd) !important;
  color: var(--brand-ink) !important;
  box-shadow: var(--glass-sh) !important;
  border-radius: 999px;
  transition: box-shadow .16s ease, border-color .16s ease, color .16s linear;
}
.ai-tabs button:hover, .adm-tabs button:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  background-image: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  border-color: #E0A400 !important;
  color: var(--brand-ink) !important;
}
.ai-tabs button.on, .adm-tabs button.on {
  border-color: var(--glass-bd-on) !important;
  box-shadow: 0 3px 0 var(--brand-lo), 0 10px 18px -10px rgba(180, 130, 10, .55) !important;
  font-weight: 700;
}
/* ⚠️ 计数小徽标 `.cnt` **不许在这里改** —— MEMORY 铁律「给数字穿衣服 =
   把整个胶囊撑大」，守卫 `_verify_glass_capsule.py` D3 会判失败。
   它的原始灰底定义在 `admin.html:54`，保持原样。 */

#btnQuit, .cs-nav button, .cd-nav button {
  background: var(--glass-bg) !important;
  background-image: var(--glass-bg) !important;
  border: 2px solid var(--glass-bd) !important;
  color: var(--brand-ink) !important;
  box-shadow: var(--glass-sh) !important;
  border-radius: 999px;
  cursor: pointer;
}
#btnQuit:hover, .cs-nav button:hover, .cd-nav button:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  background-image: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  border-color: #E0A400 !important;
  color: var(--brand-ink) !important;
}
#btnQuit:disabled, .cs-nav button:disabled, .cd-nav button:disabled,
.ai-tabs button:disabled, .adm-tabs button:disabled {
  opacity: 1 !important;
  background: #E4DCC6 !important;
  background-image: none !important;
  color: #5A4E3C !important;
  border-color: #CDC0A2 !important;
  box-shadow: 0 3px 0 #C3B597, 0 8px 14px -10px rgba(120, 100, 60, .35) !important;
  cursor: not-allowed;
}

/* ── ⑪ 裸分页按钮兜底（**用 id / 属性**，不用猜容器类）
      · `resource.js:671` 分页 = `<button id="csPrev">` / `<button id="csNext">`
      · `cards.js:1251` 分页 = `<button data-act="prev">` / `data-act="next"`
        （容器是匿名 `<p>`，**没有类** ⇒ 只能按属性选）
      ⚠️ 早先写的 `.cs-nav`/`.cd-nav` 是**臆造的类名**，仓里根本不存在 ⇒
         选不中，等于没生效。别再凭感觉编容器类。 ─────────────────── */
#csPrev, #csNext,
button[data-act="prev"], button[data-act="next"] {
  background: var(--glass-bg) !important;
  background-image: var(--glass-bg) !important;
  border: 2px solid var(--glass-bd) !important;
  color: var(--brand-ink) !important;
  box-shadow: var(--glass-sh) !important;
  border-radius: 999px;
  cursor: pointer;
  font-family: inherit;
  padding: 3px 14px;
  font-size: calc(12.6px * var(--fs));
  font-weight: 600;
}
#csPrev:hover, #csNext:hover,
button[data-act="prev"]:hover, button[data-act="next"]:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  background-image: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  border-color: #E0A400 !important;
  color: var(--brand-ink) !important;
}
#csPrev:disabled, #csNext:disabled,
button[data-act="prev"]:disabled, button[data-act="next"]:disabled {
  opacity: 1 !important;
  background: #E4DCC6 !important;
  background-image: none !important;
  color: #5A4E3C !important;
  border-color: #CDC0A2 !important;
  box-shadow: 0 3px 0 #C3B597, 0 8px 14px -10px rgba(120, 100, 60, .35) !important;
  cursor: not-allowed;
}

/* ── ⑫ `#csTabs button[data-sec]`（资源页案例三分表页签：**JS 动态生成**的
      裸 button，只有 `data-sec` 属性和一个 `.on` ⇒ 只能按 id+属性选）
      ⚠️ 下划线式页签**故意不在这里** —— 它们是
         守卫 `_verify_glass_capsule.py` B1/B2 明令「下划线页签不许玻璃化，
         套玻璃四不像」。⛔ 别把它加回来。 ───────────────────────────── */
#csTabs button[data-sec] {
  background: var(--glass-bg) !important;
  background-image: var(--glass-bg) !important;
  border: 2px solid var(--glass-bd) !important;
  color: var(--brand-ink) !important;
  box-shadow: var(--glass-sh) !important;
  border-radius: 999px;
  transition: box-shadow .16s ease, border-color .16s ease, color .16s linear;
}
#csTabs button[data-sec]:hover {
  background: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  background-image: linear-gradient(180deg, #FFF3B0 0%, #FFD233 55%, #FFB800 100%) !important;
  border-color: #E0A400 !important;
  color: var(--brand-ink) !important;
}
#csTabs button[data-sec].on {
  border-color: var(--glass-bd-on) !important;
  box-shadow: 0 3px 0 var(--brand-lo), 0 10px 18px -10px rgba(180, 130, 10, .55) !important;
}
/* ⚠️ 页签里的计数徽标（`b`）**不许碰** —— MEMORY 铁律
   「给胶囊里的数字穿衣服 = 把整个胶囊撑大」，`_verify_number_plain.py`
   会把对它的任何覆盖判为失败。这里只保证文字可读，不加任何盒模型。 */
#csTabs button[data-sec].on b { color: var(--brand-ink) !important; }
#csTabs button[data-sec]:disabled {
  opacity: 1 !important;
  background: #E4DCC6 !important;
  background-image: none !important;
  color: #5A4E3C !important;
  border-color: #CDC0A2 !important;
  box-shadow: 0 3px 0 #C3B597 !important;
  cursor: not-allowed;
}

/* 🚨 10-16：`.as-list`（上一轮的「一行一条卡片」）整段已删。
   涛爷要的是**易店式表格**（有列才能逐要素对齐），不是没有列的卡片行。
   现役形态 = `.ptable` 表格 / `.as-grid` 卡片，二者由 app.js 的 applyGridView 切换。 */

/* ── 采集结果视图切换条（2026-10-16 列表模式配套） ── */
.gridhead {
  display: flex; align-items: center; gap: 8px;
  margin: 14px 0 8px;
}
.gridhead #resCount {
  font-size: calc(12.1px * var(--fs));
  padding: 2.5px 9px; border-radius: 6px; font-weight: 700;
}

/* ══════════════════════════════════════════════════════════════════════
   ★★ 2026-10-16 涛爷令：采集结果改「易店式表格」
   ──────────────────────────────────────────────────────────────────────
   涛爷给的易店截图，列是：
     ☐ | 店铺 | 商品ID | 商品图 | 商品名称 | 价格 | 想要 | 发布日期 | 操作
   并额外要求：**增加「上架天数」（按发布日期算）**。

   ✅ 为什么用真 `<table>` 而不是「卡片行」：
      上一轮做的 `.as-list` 是「一行一条的卡片」—— 形状像列表，**但没有列**，
      店铺/商品ID/发布日期这些字段全都挤在标题下面，涛爷要的「对齐易店的
      几个要素」根本落不了地。真表格才有列 = 才能逐列对齐。

   🔴 e2e 稳定锚**一个都不能丢**（`_verify_ui1013_merge.py` 逐条断言）：
      `.pcard`（这里= `<tr>`）/ `.pick` / `.selmark` / `[data-id]`
      ⛔ 已按职责边界撤掉的 `.card-ai`（采集页不再有二创入口）不在此列。
   ══════════════════════════════════════════════════════════════════════ */

/* 🚨🚨🚨 容器层：**这一段是本轮最大的坑**，漏了它表格会整个塌掉。
   `#grid` 自带 `.grid { display:grid; grid-template-columns:repeat(auto-fill,
   minmax(196px,1fr)) }`（那是给卡片网格用的）。表格视图下 `#grid` 里装的是
   **一个 `<table>` 元素** ⇒ 它被当成「一个网格项」塞进 203px 宽的单列里
   ⇒ 实测截图整个塌成左侧一条竖线（表格宽 969px 却挤在 203px 列内、
      高度撑到 10176px）。
   ⇒ 必须把容器从 grid 改回普通块级流，并让表格撑满整宽。
   ⚠️ 记忆铁律对应：「页面不能有 X」式判据极脆 ⇒ 守卫必须**量尺寸**，
      只查 `table.ptable` 存在 = 假绿（10-16 实测踩到）。 */
#grid.as-table {
  display: block;
  width: 100%;
}
#grid.as-table > .ptable {
  width: 100%;
  /* 🔴 2026-10-21 涛爷令：「这个板块的展示已经超过长度了，需要滚动条横向拉
     才能点击分析按钮，但是实际上是不需要的，中间有很多的空间可以压缩，
     所以这个排版你需要去优化自适应一下。」
     ⇒ 去掉 `min-width: 1080px`（**这才是横向滚动条的真正来源**，
        1080 是硬下限 ⇒ 视口 < 1080 必横向滚）。
     ⭐ 现在改成：**列宽收窄 + 标题省略号 + 窄屏按重要性隐藏次要列**
        （见下方 @media）⇒ 任何视口都**不需要横向滚**。
     ⚠️ `overflow-x: auto` 保留（兜底：万一某列内容真的超 min-content，裁掉即可）。 */
  min-width: 0;
  /* 🔴🔴 2026-10-21 第二次修（涛爷：「ID 反而显示不全，中间空了那么多空间」）。
     上一版错在**列宽是估的**：实测每列都比内容窄
     （ID 90 < 需要 98、日期 124 < 129、天数 90 < 99、分析按钮 78 < 90）⇒ 全部截断；
     而余量全甩给唯一的 auto 列（标题）⇒ 标题吃 527px（占 40%）、其余列挨饿
     ⇒ 就是"中间空一大片、ID 却显示不全"。
     ⚠️ 试过 `table-layout: auto` 想让浏览器按内容分配 —— **实测否决**：
        `.tcell-in` 是 `nowrap + max-width`，其 min-content 就是 380px（文字不可压缩）
        ⇒ 标题列压不下去 ⇒ 1280 视口**溢出 109px**、1100 溢出 39px（冒横向滚动条）。
     ✅ 最终：`fixed` + **实测**列宽（量具 `tools/_probe_ptable_cols.py`）
        + 窄屏按重要性藏列 ⇒ 宽度可预测、绝不溢出、每一列都完整。 */
  table-layout: fixed;
}
#grid.as-table {
  overflow-x: auto;       /* 兜底（正常情况下不出现滚动条） */
}

/* 卡片网格视图（视图切换的另一半） */
#grid.as-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
  gap: 14px;
}

.ptable {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: calc(13.2px * var(--fs));
  /* 🔴 2026-10-21 涛爷令：「这个板块的展示已经超过长度了，需要滚动条横向拉
     才能点击分析按钮，但是实际上是不需要的，中间有很多的空间可以压缩」
     ⇒ 改用 `table-layout: fixed`：**标题列吃剩余空间**，其余列按声明宽
     ⇒ 表格永远 = 容器宽，⛔ 不再出现横向滚动条。
     ⚠️ fixed 的前提：**每一列都要有宽度**（含 `width:auto` 的那列吃余量），
        否则浏览器无处安放余量 ⇒ 行高被撑爆（历史坑，见下方列宽注释）。
     🔴 2026-10-21：最终仍用 `fixed`（`auto` 试过会让标题列不可压缩而溢出，
        理由见上方 `#grid.as-table > .ptable` 的注释）。
        ⭐ 但列宽**必须实测**（`tools/_probe_ptable_cols.py`），⛔ 不许再估。 */
  table-layout: fixed;
}
.ptable thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface-2);
  color: var(--text-2);
  font-size: calc(12.5px * var(--fs));
  font-weight: 700;
  text-align: left;
  /* 🔴 2026-10-21：左右 padding 10 → 7（11 列 ⇒ 一行省 66px） */
  padding: 9px 7px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.ptable tbody td {
  /* 🔴 2026-10-21：左右 padding 10 → 7（11 列 ⇒ 一行省 66px） */
  padding: 8px 7px;
  /* 🔴 2026-10-21：`table-layout:fixed` 下，若某列**声明宽 < 内容最小宽度**
     （如「2026-07-01 22:59:59」放进 116px），浏览器仍会按 min-content 撑宽
     ⇒ 表格又比容器宽几像素 ⇒ 又冒出横向滚动条。
     ⭐ 正解：**裁掉溢出**（内容超宽就省略）⇒ 表格宽度严格 = 容器宽。 */
  overflow: hidden;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
/* 行本体 —— ⭐ 用 `tr.pcard` 承接卡片时代的类名，e2e 选择器零改动
   🚨🚨🚨 **必须显式把 display 掰回 `table-row`**：
      全局 `.pcard { display:flex; flex-direction:column; ... }` 是给卡片写的，
      它会一并作用到 `<tr class="pcard">` 上 ⇒ `tr` 变成 flex 容器，
      `align-items` 默认 stretch 把每个 `<td>` 拉满行高
      ⇒ 实测行高 417px（td 内容只有 36~61px），表格看着像被撑散的竖条。
      ⛔ 别指望「td 都正常所以 tr 也正常」—— tr 的高度是 flex 布局算出来的，
         与 td 内容无关。 */
.ptable tbody tr.pcard {
  display: table-row;
  cursor: pointer;
  transition: background .15s;
}
.ptable tbody tr.pcard:hover { background: var(--surface-2); }
.ptable tbody tr.pcard.picked { background: var(--brand-soft); }
.ptable tbody tr.pcard.picked td { border-bottom-color: var(--brand); }
/* ══ 列宽（2026-10-21 实测重定）══════════════════════════════════════
   🔴 `width` = 建议宽（auto 布局下浏览器可让），`min-width` = **硬下限**。
   ⭐ 每列的 min-width 都是**量出来的**（`tools/_probe_ptable_cols.py`），
      不是估的 —— 估出来的数字上一版已经把 ID/日期/天数/按钮全截断了。
   ⭐ 关键列的 min-width 要留**余量**：ID 会到 15~16 位、想要数会到 5 位数
      （实测 `5972 人想要` 比 `331 人想要` 宽 2 个字）。
   ⛔ 别把 min-width 调到"刚好"—— 字体缩放（`--fs` 变量）一改就又截断了。 */
.ptable .c-ck   { width: 44px;  }   /* 实测需要 41 */
.ptable .c-shop { width: 132px; }   /* 实测 120：店名 + 「✓ 已入选」 */
.ptable .c-id   { width: 130px; }   /* 实测 123（13 位）；留到 15~16 位不截断 */
.ptable .c-ph   { width: 66px;  }   /* 实测 63：40×40 图 + 圆角 */
.ptable .c-title{ width: auto;  }   /* ⭐ 唯一的弹性列（吃剩余） */
.ptable .c-price{ width: 80px;  }   /* 实测 75 */
/* ★ 2026-10-24：原价（划线价）—— 现价旁边的小字划线，弱化显示 */
.c-price .c-orig, .pcard .row1 .c-orig{
  color: var(--text-3, #999);
  font-size: calc(11px * var(--fs, 1));
  text-decoration: line-through;
  margin-left: 3px;
  font-weight: 400;
}.ptable .c-want { width: 112px; }   /* 实测 103；要容得下「5972 人想要」5 位数 */
.ptable .c-opp  { width: 66px;  }   /* 实测 69（标签本身只要 30，留呼吸） */
/* ★ 2026-10-21 整店「爆品」列：标签(1行) + 日均&档位(1行)
   ⚠️ 宽度是**量出来的**（`tools/_probe_geo` 那套）：
      132px 时「日常销量」徽标溢出 4px（档位右缘 136 > 列宽 132）⇒ 加到 148px。 */
.ptable .c-hot  { width: 148px; }
/* ⛔ 爆品列只在「整店采集」显示（搜索/收藏/链接 都没有店内分位的意义） */
#grid.no-hot .ptable .c-hot { display: none; }
/* 🔴 2026-10-21 涛爷令：商机分 / 分析两列**只在「搜索采集」显示**。
   其他三种模式（整店/收藏/链接）算出来的热度/竞争度会误导：
   整店是同一个卖家（竞争度恒 1 ⇒ 永远"蓝海"）、收藏和链接是自己挑的（有偏）。
   ⚠️ th 与 td 用的是同一个类名 ⇒ 一起隐藏，不会错位。 */
#grid.no-opp .ptable .c-opp,
#grid.no-opp .ptable .c-oppact { display: none; }
/* 🔴 卡片视图**必须也靠 CSS**（10-21 e2e 实测抓到的漏网）：
   切模式不会重画列表 ⇒ `productCard()` 里的 `if (oppEnabled())` 对**已渲染**的卡片无效
   ⇒ 卡片里还留着「📊 分析」按钮和分数标签。
   ⇒ 这里统一隐藏；JS 那层保留（新渲染时干脆不生成，省 DOM）。两层缺一不可。 */
#grid.no-opp .opp-tag,
#grid.no-opp .oppbtn { display: none; }
/* 非搜索模式时结果条位置的那句说明（灰字，不抢眼但能解释"为什么没有"） */
.opp-note.opp-off { background: var(--surface-2); border-color: var(--border); color: var(--text-2); }
.opp-note.opp-off b { color: var(--text); }
.ptable .c-date { width: 140px; }   /* 实测 136：19 字符 `2026-07-01 22:47:56` */
.ptable .c-days { width: 112px; }   /* 实测 108：「上架 111 天」药丸 */
.ptable .c-oppact{ width: 108px; }  /* 实测 100：「📊 分析」按钮完整（⛔ 不许切）*/
/* ⭐ 合计 990px ⇒ 1440 视口（容器 1333）标题列 = 343px；
   ⛔ 上一版合计 936 但每列都估窄了 ⇒ 现在多花的 54px 全部从标题列的 527 里省出来。 */
/* ⭐ 商品名称超长 ⇒ **省略号**（不撑破表格）
   ⚠️ auto 布局下 `td` 的 max-width 不生效 ⇒ 必须给**内层 div** 加 */
.ptable .tcell-in {
  /* fixed 布局下 td 宽已由列宽定死 ⇒ 内层只要 `100%` 就能正确省略。
     （⛔ 不要写死 px 上限：那会让内容只画到上限、td 右侧留一段"看起来空着"的
       区域 —— 正是 `.tcell { max-width:460px }` 那条旧补丁造成的毛病。） */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ptable .idcell { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ptable .datec  { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 🔴 2026-10-21 涛爷令：「这个排版你需要去优化自适应一下」
   ⇒ 窄屏时**按重要性依次隐藏次要列**（⛔ 不是让用户横向拉）
   · <1180px：藏「商品ID」「发布日期」（有 title 悬浮可看全文）
   · <980px ：再藏「上架天数」
   ⚠️ 表头与单元格要**一起藏**（只藏 td 会错位）⇒ 用同一个类名选中 th/td。 */
/* 🔴 2026-10-21 重排（原来 1180 就藏「商品ID」—— 而涛爷这次抱怨的正是它）：
   按**重要性从低到高**依次藏，⛔ 「商品ID」排最后才藏（≤1000 仍保留）。
   · ≤1340：藏「发布日期」（「上架天数」已能表达新旧，日期是冗余信息）
   · ≤1180：藏「上架天数」
   · ≤1000：藏「商机分」（点「分析」仍能看到分数）
   ⚠️ 表头与单元格必须**一起藏**（只藏 td 会整列错位）。 */
@media (max-width: 1340px) {
  .ptable .c-date, .ptable .datecell { display: none; }
}
@media (max-width: 1180px) {
  .ptable .c-days, .ptable .dayscell { display: none; }
}
@media (max-width: 1000px) {
  .ptable .c-opp, .ptable .oppcell { display: none; }
}
.ptable .daysc  { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⛔ `.ptable .c-act` / `.ptable .actcell .btn` 已随「操作 / 看详情」整列删除
   （2026-10-17 涛爷令）—— 列没了还留宽度规则，是典型的「看着还有、其实没有」，
   下一次改表格的人会照着这条去对齐一个不存在的东西。 */

/* 缩略图（易店那张是 40×40 小方图） */
.ptable .ph {
  width: 44px; height: 44px;
  border-radius: 6px; overflow: hidden;
  background: var(--surface-2);
  position: relative;
  border: 1px solid var(--border);
}
.ptable .ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ptable .ph .n {
  position: absolute; right: 2px; bottom: 2px;
  background: rgba(16, 18, 22, .72); color: #fff;
  font-size: calc(10.5px * var(--fs));
  padding: 0 4px; border-radius: 3px; line-height: 1.5;
}
/* 「✓ 已入选」标记 —— ⚠️ 在表格里放到「店铺」列下方，不叠在图上（叠着会盖住图） */
.ptable .selmark {
  display: inline-block;
  margin-top: 3px;
  background: var(--green); color: #fff;
  font-size: calc(10.8px * var(--fs));
  padding: 1px 6px; border-radius: 4px;
}

/* ── 爆品分类标签（整店采集）─────────────────────────────────────
   配色语义：绿=新品机会 / 红=已被验证的爆款 / 灰=滞销 / 淡灰=普通。
   ⚠️ 两行布局：上行标签、下行「X 想要/天」（涛爷点名要的那个指标）。 */
.ptable .c-hot { white-space: nowrap; }
.hot-tag { font-size: calc(11.4px * var(--fs)); font-weight: 700; padding: 1px 7px; }
.hot-tag.hot-new  { background: rgba(22, 163, 74, .13); color: var(--green);
                    border: 1px solid rgba(22, 163, 74, .34); }
.hot-tag.hot-long { background: rgba(220, 38, 38, .11); color: #B91C1C;
                    border: 1px solid rgba(220, 38, 38, .3); }
.hot-tag.hot-dead { background: rgba(107, 114, 128, .12); color: var(--text-3);
                    border: 1px solid rgba(107, 114, 128, .28); }
.hot-tag.hot-mid  { background: var(--surface-2); color: var(--text-2);
                    border: 1px solid var(--border); }
.hot-tag.hot-na   { background: transparent; color: var(--text-3);
                    border: 1px dashed var(--border); }
/* 🔴 2026-10-21 几何实测修正：档位徽标原来掉到了**第三行**
   （`.hot-daily` 是 display:block，后面的徽标只能换行 ⇒ 整列被撑成三行高）。
   ⇒ 改成**两行**：① 分类标签独占一行 ② 日均 + 档位同一行。
   ⛔ 别再把 `.hot-daily` 改回 block（会又变三行）。 */
.hotcell .hot-tag { display: block; }
.hotcell .hot-daily {
  display: inline-block; margin-top: 2px;
  font-style: normal; font-size: calc(11px * var(--fs)); color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

/* ── 想要数档位徽标（爆款 / 日常销量 / 低销量）──────────────────────
   🔴 与「分类标签」是**两个维度**，配色必须能区分开：
      分类 = 上架时间 × 想要档位 交叉出来的（绿/红/灰）
      档位 = 想要数本身有多高（橙/蓝/浅灰）
   ⛔ 档位用**无边框**的小圆角块 + 淡化底色（次要信息，不该抢分类的视觉权重），
      但颜色仍要一眼可辨（用户要靠它判断"这货卖得动吗"）。 */
.hot-tier {
  display: inline-block; margin: 2px 0 0 4px; padding: 0 5px;
  font-size: calc(10.4px * var(--fs)); font-weight: 600; line-height: 1.5;
  border-radius: 4px; white-space: nowrap; vertical-align: baseline;
}
.hot-tier.tier-hot  { background: rgba(234, 88, 12, .13);  color: #C2410C; }
.hot-tier.tier-norm { background: rgba(37, 99, 235, .11);  color: #1D4ED8; }
.hot-tier.tier-low  { background: var(--surface-2);       color: var(--text-3); }

.ptable .shop {
  color: var(--brand); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 108px; display: block;
}
.ptable .idcell {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: calc(11.8px * var(--fs));
  color: var(--text-2);
  white-space: nowrap;
}
/* ⛔ 2026-10-21 删除：`.ptable .tcell { max-width: 460px; overflow:hidden }`。
   它给 **td** 设 max-width —— 在 `table-layout:fixed` 下被忽略，但 `overflow:hidden`
   仍把 td 变成裁剪盒 ⇒ 实测 td 占 527px、内容只画到 460px，
   **右边缘留出一段"看起来空着"的区域**（涛爷原话「中间空了那么多空间」）。
   现在改 auto 布局 + 内层 `.tcell-in` 限宽，这条已无用且有害。
   ⚠️ 铁律：**td 的 max-width 在两种表格布局下都不可靠** ⇒ 一律限内层元素。 */
.ptable .price { color: var(--brand); font-weight: 800; white-space: nowrap; }
/* ⚠️ 「想要」文案是「294 人想要」这类，78px 会折行 ⇒ 加宽到 96px 并 nowrap。
   （10-16 截图实测：窄列把数字和「人想要」拆成两行，行高看着就参差不齐） */
.ptable .want  { color: var(--text-2); white-space: nowrap; }
.ptable .datecell {
  color: var(--text-2); font-size: calc(12.1px * var(--fs));
  white-space: nowrap;
}
/* 上架天数 —— ⭐ 空值显示「未知」**绝不补 0**（补 0 = 冒充「今天上架」= 静默造假） */
.ptable .dayscell { white-space: nowrap; }
.ptable .dayscell .tag.t-listed-unknown { opacity: .75; }

/* 行内勾选框 —— ⚠️ 不再铺满整格（表格里 100% 会吃掉整行的点击） */
.ptable .pick {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface);
}
.ptable .pick input {
  position: absolute; inset: auto;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 15px; height: 15px; margin: 0;
  cursor: pointer; accent-color: var(--brand);
}
.ptable tr.picked .pick { border-color: var(--brand); background: var(--brand-soft); }

/* ⛔ 原「操作」列样式已删（2026-10-17）：该列整列撤除，
   详情改由「勾选 → 加入选品库」后到选品库看。 */


/* ============================================================
   ★★★ 2026-10-17 涛爷令（第 3 条）：补详情「进度抽屉」
   ============================================================
   流程统一为：实时查询列表 → 人工勾选 → 加入选品库（**逐条补详情**）
              → 右侧进度抽屉 → 完成后提示去二创。

   ⚠️ 为什么独立于 `.drawer`：商品详情抽屉可能同时开着（"边看货边补"）。
      两者共用节点会互相抢 `.on` 类，把对方的动画状态清掉。
   ⚠️ 不加 mask —— 补详情是**后台活**，不该锁住主界面：
      客户完全可以一边补详情一边去选品库翻别的货。
   ------------------------------------------------------------ */
.pdrawer { width: min(430px, 94vw); z-index: 62; }

.pd-kpis {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 9px; margin-bottom: 14px;
}
.pd-kpi {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 9px 11px;
  min-width: 0;
}
.pd-kpi b {
  display: block;
  font-size: calc(18px * var(--fs));
  line-height: 1.25;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.pd-kpi span {
  display: block;
  font-size: calc(11.4px * var(--fs));
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* 逐条清单 —— 每补完一条就追加一行（客户能看见"跑到哪了"） */
.pd-list {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 46vh; overflow: auto;
  padding-right: 2px;
}
.pd-row {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  font-size: calc(12.4px * var(--fs));
  min-width: 0;
}
.pd-row .t {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pd-row .r {
  color: var(--text-3);
  font-size: calc(11.2px * var(--fs));
  white-space: nowrap;
  max-width: 46%;
  overflow: hidden; text-overflow: ellipsis;
}
.pd-row.ok { border-color: #BFE9CB; background: var(--green-soft); }
.pd-row.ok .r { color: var(--green); }
.pd-row.bad { border-color: #F7C9C9; background: var(--red-soft); }
.pd-row.bad .r { color: var(--red); }

.pd-empty {
  padding: 22px 4px;
  text-align: center;
  color: var(--text-3);
  font-size: calc(12.6px * var(--fs));
}

.pd-foot {
  position: sticky; bottom: 0;
  display: flex; align-items: center; gap: 9px;
  padding: 14px 22px;
  margin: 14px -22px -40px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  box-shadow: 0 -6px 18px -12px rgba(80, 58, 10, .35);
}
.pd-foot .btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ══════════════════════════════════════════════════════════════════════
   ★★★ 2026-10-17 涛爷令：采集设置**全部并排一行**，输入框按内容给宽
   ──────────────────────────────────────────────────────────────────────
   原状：两块 `.cond`（筛选条件 / 采集参数）各占一行标题 + 一行字段，
   一共 7 个字段铺满两屏宽度，右半屏全是空白，控件还被撑得巨大
   （涛爷原话：「输入框不该大的这么大，不该浪费空间的就占用那么大的空间」）。

   `#condFilters` 用 **display:contents** 而不是继续当一块 `.cond`：
     · 它自己不生成盒子 ⇒ 里面的 3 个筛选字段**直接参与外层 `.params` 的 flex**
       ⇒ 与采集参数落在同一行；
     · id 仍然存在 ⇒ `switchMode('favor')` 里 `cf.style.display='none'` 照样
       能整组收起（这是收藏模式不筛的唯一开关，别为了排版把它拆了）。
   ⚠️ `.params.slim`（链接采集极简）只写 `.params > .field`，够不到
      `#condFilters` 里的字段 ⇒ 下面单独补一条，否则切到链接采集时
      价格/想要数/上架天数会**留在屏幕上**。
   ══════════════════════════════════════════════════════════════════════ */
#condFilters { display: contents; }
.params.slim #condFilters > .field { display: none; }

#condMain .params { gap: 14px; row-gap: 12px; }
/* 字段宽度只跟内容走，不参与拉伸 */
#condMain .field { flex: 0 0 auto; }
#condMain .field > label { white-space: nowrap; }
#condMain .field input, #condMain .field select { min-width: 0; }
#condMain .f-price .rng input { width: 68px; }
#condMain .f-want input { width: 92px; }
#condMain .f-listed input { width: 96px; }
#condMain #fldPlatform select { width: 132px; }
#condMain #fldTarget input { width: 88px; }
#condMain #fldLimit select { width: 104px; }
#condMain #fldAccount select { width: 172px; }
#condMain #fldHeadless select { width: 196px; }
/* 单次上限提示：从字段格里挪出来单独一行，避免把整排顶歪 */
.cond-note {
  margin-top: 10px; font-size: calc(12px * var(--fs)); color: var(--text-3);
  line-height: 1.5;
}

/* ══════════════════════════════════════════════════════════════════════
   ★★★ 2026-10-17 涛爷令：选品库卡片「文字区只留标题」+ 新增表格视图
   ──────────────────────────────────────────────────────────────────────
   ① 卡片：来源徽标 / 违禁词从文字区挪成**封面贴片**（absolute，不占排版高度）；
      关键词 tag 行与文案预览整块不渲染 ⇒ 文字区 = 价格/想要 + 标题两行。
   ② 表格视图：列 = ☐ / 图 / 标题 / 来源 / 价格 / 想要 / 上架天数 / AI。
   ⚠️ 表格里**不许出现第二个 `.grid`**（采集页踩过：卡片和表格两套 DOM 同时
      挂在页面上，`.grid` 的 grid-template 会把表格行撑歪）——
      `#groups.as-table` 时我们把表格直接挂进 `.sel-group`，不再建 `.grid`。
   ══════════════════════════════════════════════════════════════════════ */

/* ① 封面贴片：来源（**左下**）/ 违禁词（右上）
   🔴 来源为什么要放左下而不是左上：左上被 `.pcard .pick`（40×40 勾选框，
      `z-index:5` + 白底）占着 —— 放左上会被整个盖住，实测截图里只能看到
      「闲鱼」两个字从勾选框边缘透出来，等于没做。左下原先是空的
      （图数 / 选入日期都在右下）。 */
.pcard .ph .ph-plat {
  position: absolute; left: 7px; bottom: 7px; z-index: 2;
  padding: 2px 8px; border-radius: 999px;
  font-size: calc(11.6px * var(--fs)); font-weight: 700; line-height: 1.6;
  background: rgba(16, 18, 22, .72); color: #fff; backdrop-filter: blur(2px);
  max-width: calc(100% - 74px); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.pcard .ph .ph-warn {
  position: absolute; right: 7px; top: 7px; z-index: 2;
  width: 20px; height: 20px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: calc(12px * var(--fs)); font-weight: 700;
  background: var(--danger, #d64545); color: #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .25);
}
/* ★ 10-18 二改：AI 二创标识（`.ai-mark`）**从封面贴片挪到「想要数」旁边**。
   🔴 为什么挪：原来贴在主图上（top:54px 那枚深色胶囊），而商品主图普遍是
      花花绿绿的促销图 ⇒ 胶囊和图糊在一起看不清（涛爷原话「AI文案躲在主图中
      很难看清楚」）。挪到价格/想要数那一行，底色恒定 ⇒ 任何主图上都一眼可见。
   ⚠️ **正常流内联**，不是绝对定位 —— 上一版用 `position:absolute; left:0`
      想「零高度不占排版」，结果几何判据量出它**压在价格/想要数上面**
      （判据 right=False），压根不在「旁边」⇒ 判据赢、实现输，已改回内联。
      `.row1` 本来就是 flex，多一个 inline 元素**不新增行**，标题不会被顶下去。 */
.pcard .ai-mark {
  flex: none; margin-left: 2px;
  padding: 1px 7px; border-radius: 999px;
  font-size: calc(11.2px * var(--fs)); font-weight: 700; line-height: 1.65;
  /* 绿色系与全站「已改/已完成」语义一致（--green 是站内已定义的正向色） */
  background: rgba(22, 163, 74, .13); color: var(--green);
  border: 1px solid rgba(22, 163, 74, .34);
  white-space: nowrap;
}
/* 表格视图里的同一枚标识：跟在标题文字前面，绿色系与 `.t-ai` 一致 */
.seltable .c-title .aiw { margin-right: 2px; vertical-align: 1px; }
/* 文字区收紧：只有两行，别留大空隙 */
.pcard .bd { gap: 5px; }
.pcard .bd .ptitle { margin: 0; }


/* ⛔ 2026-10-21：商机统计条（.opppeers / .opppeer）已于 10-20 整块删除。
   删除时**漏删了尾巴** —— 这里长期只剩一句孤立的
   font-variant-numeric: tabular-nums 后面跟着一个孤零零的右花括号
   （没有选择器头），导致 app.css 全文**多出一个右花括号**，
   两个 CSS 守卫（relief3d / click_reach）的「花括号配平」判据一直红着
   （备份基线同样红 ⇒ 本轮之前就残，不是本轮引入）。
   ⇒ 本轮把残留整段清掉（.opppeer 在 app/ 下**零引用** = 死代码）。
   ⚠️ 教训 1：删 CSS 块要删**整块**（注释头 + 所有子规则 + 收尾括号），
      只删一半 ⇒ 后面所有人的「配平」判据都跟着红。
   ⚠️ 教训 2：这些守卫是**纯文本计数**（不剥注释）⇒ 注释里也**别写**
      花括号字面量，否则一样把判据弄红（本轮当场踩到，见 PITFALLS）。 */

/* ② 表格视图 */
.seltable {
  width: 100%; border-collapse: separate; border-spacing: 0;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); overflow: hidden;
  table-layout: fixed;
}
.seltable thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface-2); text-align: left;
  padding: 9px 10px; font-size: calc(12.4px * var(--fs)); font-weight: 600;
  color: var(--text-2); border-bottom: 1px solid var(--border); white-space: nowrap;
}
.seltable tbody td {
  padding: 8px 10px; border-bottom: 1px solid var(--border);
  font-size: calc(13px * var(--fs)); vertical-align: middle;
}
.seltable tbody tr:last-child td { border-bottom: 0; }
.seltable tbody tr.prow { cursor: pointer; }
.seltable tbody tr.prow:hover { background: var(--surface-2); }
.seltable tbody tr.prow.picked { background: var(--brand-soft); }
.seltable .c-ck    { width: 34px; }
.seltable .c-ph    { width: 62px; }
.seltable .c-title { width: auto; }
.seltable .c-plat  { width: 84px; }
.seltable .c-price { width: 78px; }
.seltable .c-want  { width: 96px; }
.seltable .c-days  { width: 96px; }
.seltable .c-act   { width: 92px; text-align: right; }
.seltable .c-ph img {
  width: 46px; height: 46px; object-fit: cover; border-radius: var(--r-sm);
  display: block; background: var(--surface-2);
}
/* 标题单行省略：表格里一行就够，悬停看全文（title 属性已给） */
.seltable .c-title.tcell {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.seltable .twarn { color: var(--danger, #d64545); font-weight: 700; }

/* 表格里的 AI 按钮：**不许折行**（`✨ AI` 被挤成上下两行，实测截图里很难看） */
/* ⚠️ 96px 会把按钮挤到 68px ⇒ 文字被裁（实测截图里「✨ AI」右边缺一块）。
   `table-layout:fixed` 下单元格宽是死的，按钮不会自己撑开 ⇒ 必须给够宽度，
   并且**用 scrollWidth 断言**兜住（只看「按钮存在」是假绿）。 */
.seltable .c-act { width: 118px; }
.seltable .c-act .btn { white-space: nowrap; padding: 6px 10px; }

/* ══════════════════════════════════════════════════════════════════════
   ★★★ 2026-10-17 涛爷令：AI 助手弹窗版面收紧（「哪儿哪儿都不对劲」）
   ──────────────────────────────────────────────────────────────────────
   原话：「有些按钮显得过大，图片也显得过大。导致还需要滚动滚条才能完整操作。」
   实测原因（1280×720 笔记本屏）：
     · 弹窗 920px 宽、`max-height:90vh`，但**图片 372px 见方** × 2 列
       + 文案框 300px + 大按钮 ⇒ 内容高度 ~860px > 视口 ⇒ 必须滚；
     · 顶部页签、底部按钮都是 `.btn` 默认尺寸（8px 20px + 14.3px 字），
       在这个密集弹窗里显得又大又抢眼；
     · 提示词框 96px 固定高，再占一大块。
   ⇒ 统一收紧：**图片跟着视口高度缩**（`min(330px, 32vh)`）、
      正文区内部滚动（header/tabs/foot 恒常驻可见）、按钮降一档。
   ⚠️ 只动尺寸，不改任何交互/文案/接口。
   ══════════════════════════════════════════════════════════════════════ */
.rmodal.ai-modal { width: min(980px, 94vw); max-height: 92vh; }
.ai-modal .rmodal-h { padding: 12px 18px; }
.ai-modal .rmodal-body { padding: 12px 18px 16px; }
.ai-modal .rmodal-h h3 { font-size: calc(16.2px * var(--fs)); margin-bottom: 3px; }
.ai-tabs { padding: 5px 14px; }
/* 🔴 页签瘦身（2026-10-17 涛爷第二轮回话）：
   原话「改文案和生图这两个胶囊状按钮显得特别肥胖，这是不是可以瘦一点呢？
   这样子显得跟上下间距都很挤」。
   「胖」和「挤」是**同一个病因的两个症状**，必须一起解：
     · **挤** = `.ai-tabs` 上 `padding: 0 14px` ⇒ 上下**零留白**，
       按钮（自带 padding）上下边直接贴住 1px 分隔线 ⇒ 挤。
     · **胖** = 按钮 `padding 7px/12px/8px` + **2px 玻璃边框** + 13.6px 字
       ⇒ 渲染高度 ~38px，而胶囊是 `border-radius:999px`
       ⇒ 高度越大越接近一个"胖椭圆"，越显臃肿。
   ⇒ ① 栏上下各给 5px 留白（`padding: 5px 14px`），按钮上下不再贴线；
     ② 按钮降一档（`padding 3px 12px 4px` + 13.0px 字）。
   🔴 **实测的高度构成（1280×720，算出来的不是猜的）**：
        36.1px = 上padding 5 + 下padding 6 + **玻璃边框 2×2** + 行高 21.12
        ⇒ 真正能动的只有 padding 和 font-size；边框是玻璃统一令的，不许碰。
        目标 ≤32px ⇒ padding 3/4 + font-size 13.0px（行高 20.8）
        ⇒ 3+4+4+20.8 = **31.8px** ✅
   ⚠️ **玻璃化样式一个字不许动**（`.ai-tabs button` 在本文件更后面有
      `border:2px solid var(--glass-bd) !important` + `border-radius:999px`
      + `--glass-sh`）。MEMORY 铁律「玻璃统一令」优先级高于本段：
      只调尺寸（padding/font-size），颜色与圆角交给玻璃那条。 */
.ai-tabs button {
  padding: 3px 12px 4px; font-size: calc(13.0px * var(--fs));
}
.ai-h { height: 26px; font-size: calc(13.2px * var(--fs)); gap: 6px; }

/* 🔴 提示词那一行：说明文字长 + 右侧「恢复预设」按钮（2026-10-17 涛爷报重叠）
   ────────────────────────────────────────────────────────────────────
   **这是我自己上一轮改出来的 bug**：收紧版面时把 `.ai-h` 压成 `height:26px`（固定高），
   而这行的说明文字「提示词（以这里的内容为准 · 预设只是初始值，你可随意修改提示词内容）」
   在 900px 宽度里换行到第二行 ⇒ 26px 装不下 ⇒ **溢出压住了下面的 textarea**
   （截图里「恢复预设」正好坐在文本框右上角）。
   ⚠️ 固定 `height` + 会换行的文字 = 必炸。判据不能只看「这一行高 26px 挺好」。

   ⇒ 正解两条同时上：
     ① 允许换行：`flex-wrap:wrap` + `height:auto` + `min-height:26px`；
     ② 文字块 `min-width:0` + `flex:1`，让它能收缩换行而不把按钮挤出容器；
        按钮 `flex-shrink:0` 保证自己不被压扁。
   ⚠️ **不能**改成把按钮挪到别处 —— 按钮的语义是"作用于下面这个文本框"，
      必须在文本框**紧上方**，视觉上是一组。 */
/* 🔴🔴 10-18 二次修（真根因，之前一直没找对）：
   原来这条写成 `#paneImg .ai-h`（作用域内**三个** .ai-h 全中），
   于是「恢复预设」那行需要的 `margin-bottom: 10px` + `flex-wrap: wrap`
   **也一并打到了两个图框标题行** ⇒ 图框标题行与 .img-frame 之间多出 10px 空档，
   图被整体往下推（涛爷原话：「查看大图按钮把图片位置往下挤了」）。

   ⇒ 正解：**能力只授予真正需要的那一行**。
     - `.img-prompt-row`（提示词行）：长说明文字要能换行、要跟文本框留间距；
     - 其余 `.ai-h`（两个图框标题行）：一律单行，且**不额外加 margin**，
       标题与图框之间只保留 `.img-col { gap: 8px }`。
   ⛔ 教训：**CSS 选择器的作用域必须与需求的覆盖面一致**，
      「顺手多覆盖几个」= 给别的元素也 granting 了它不需要的能力。 */
#paneImg .ai-h {
  height: auto; min-height: 26px; flex-wrap: nowrap;
}
#paneImg .ai-h.img-prompt-row {
  flex-wrap: wrap; row-gap: 6px;
  /* 🔴 「矩形不相交」是**弱判据**：实测 btn.b = ta.t（间距 0）时几何上不算越界，
     断言全绿，但视觉上按钮**骑在文本框边框上**，用户照样报"重叠"。
     ⇒ 必须给 margin-bottom 拉开真实间距。10px = 一个边框 + 一口气的呼吸。 */
  margin-bottom: 10px;
}
#paneImg .ai-h > span:first-child {
  flex: 1 1 auto; min-width: 0; line-height: 1.5;
}
#paneImg .ai-h > .btn { flex: 0 0 auto; margin-left: auto; }
/* 「查看大图」悬浮在图框右上角：position 让它脱离布局流，
   ⛔ 绝不能放回标题行 —— 一行放不下会把整个图框往下顶。 */
#paneImg .img-frame .img-open {
  position: absolute; top: 8px; right: 8px; z-index: 3;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
}
/* 文案页两栏：跟着视口缩，别写死 300px */
.ai-box { height: clamp(150px, 26vh, 280px); font-size: calc(13.6px * var(--fs)); }
/* 生图页：图片大小由高度预算决定 —— 这是「不用滚」的关键一条 */
.img-cols { gap: 14px; }
.img-frame { max-width: min(330px, 32vh); }
.img-prompt { height: 72px; font-size: calc(13.4px * var(--fs)); }
.ai-act { margin-top: 10px; gap: 10px; }
.ai-act .btn,
.ai-foot .btn { padding: 7px 16px; font-size: calc(13.2px * var(--fs)); }
.ai-foot { padding: 10px 18px; }
.ai-hint { margin-top: 8px; font-size: calc(13.2px * var(--fs)); }

/* ── 生图页：左右两列**限宽居中**（2026-10-17 补）──────────────────────
   实测（1280×720）：弹窗内宽 944px，两列各 460px，而图框只有 230px
   ⇒ 每列左右各空 115px，两图之间隔出 ~244px 一大片，
   截图上一眼就是「哪儿哪儿散着」（用户原话口径）。
   ⇒ 把两列整体限宽居中，列宽收到刚好容下图框 + 一点边距。
      **限宽值由 e2e 反推**：图框 32vh（720 屏 = 230px）+ 每侧留白 ≤60px
      ⇒ 列 ≤ 290px，两列 + gap 14px ⇒ `max-width: 580px`。
   ⚠️ `max-width` 只收**列**不碰 `.img-frame` 的高度预算（那是 32vh 管的），
      大屏（1500×1000，图 320px）时列 283px 会把图轻微压到 283，
      这是可接受的取舍 —— 换小屏不再空旷。 */
.img-cols { max-width: 580px; margin-left: auto; margin-right: auto; }

/* ── 生图页底部：主操作**回到最左侧**（2026-10-17 涛爷当面否掉右对齐）──
   🔴 血债记录 —— 我上一轮自作主张写了 `margin-left:auto` 把它推到右侧，
     涛爷当场否掉，原话：
       「模仿生成主图这个按钮还是放在最左侧比较合适，要不然现在在右侧
         点击后又移动到左侧来显示。特别奇怪。」
   **真实理由是功能连续性**（我上轮只想到了"视觉重心"，想错了）：
     点「生成」后 `.ai-hint` 会在按钮**右侧**展开提示（"正在按你的提示词生成主图，
     约 10~30 秒…"）。按钮若在右 ⇒ 点击瞬间提示出现在按钮**左边**，
     视觉锚点跨半屏跳一次 ⇒ 「点了之后东西跑另一边去了」。
   ⇒ 主按钮钉在左边，提示永远向右展开，点击前后**锚点不动**。
   🔴 铁律：**用户提新要求 ⇒ 先改守卫再改实现**，
     否则守卫会把「当时那个错误设计」钉成契约。 */
.ai-act { justify-content: flex-start; }
.ai-act .btn.primary { margin-left: 0; }

/* ★ 2026-10-20 商机分析（采集页）：分数标签 + 结果条 + 表格列
   —— 分数是**批次内相对位次**（0~100，越高越值得做）。
   🔴 语义色与选品库那枚一致：高分绿 / 中分橙 / 低分灰。
   ⛔ **低分不用红** —— 本站红色表示「风险/失败」，
      而「这品在这一批里不算热」不是风险，标红会让人以为要处理什么。 */
.opp-tag {
  display: inline-block; min-width: 30px; text-align: center;
  padding: 1px 7px; border-radius: 999px;
  font-size: calc(12px * var(--fs)); font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: rgba(107, 114, 128, .12); color: var(--text-2);
  border: 1px solid rgba(107, 114, 128, .3);
}
.opp-tag.opp-high { background: rgba(22, 163, 74, .13); color: var(--green);
                   border-color: rgba(22, 163, 74, .34); }
.opp-tag.opp-mid  { background: rgba(217, 119, 6, .12); color: #B45309;
                   border-color: rgba(217, 119, 6, .3); }
.opp-note {
  margin: 0 0 10px; padding: 10px 14px;
  background: var(--brand-soft); border: 1px solid #F0DFAE;
  border-radius: var(--r-md);
  font-size: calc(12.8px * var(--fs)); color: var(--brand-ink); line-height: 1.7;
}
.opp-note b { color: var(--text); }
.opp-note .hint { color: var(--text-3); }
.pcard .oppcell { white-space: nowrap; }
/* ⛔ 2026-10-21 删除 `.ptable .c-opp { width: 74px; }` ——
   列宽**只在「列宽」段定义一处**（约 4317 行），这里再写一份就是
   "改了 A 处、生效的是 B 处"（实测：我改成 66px，实际仍是 74px）。 */

/* ★ 2026-10-20 商机分析：单件分析入口 + 详情面板
   （涛爷令「每个产品最后右侧增加商机分析按钮，可以单独分析每个产品的分析结果」） */
/* ⛔ 同样删掉了 `width: 78px`（后置覆盖了列宽段的 108px ⇒ 「📊 分析」被切）。
   这里只保留**行为**样式；宽度见列宽段（单一事实源）。 */
.ptable .c-oppact { text-align: center; white-space: nowrap; }
.oppbtn { padding: 3px 10px !important; font-size: calc(12.2px * var(--fs)) !important;
          line-height: 1.4; white-space: nowrap; }
.pcard .oppbtn { margin-left: 6px; }
.oppdetail {
  background: var(--brand-soft); border: 1px solid #F0DFAE;
  border-radius: var(--r-md); padding: 12px 14px; margin: 8px 0;
  font-size: calc(12.8px * var(--fs)); color: var(--brand-ink); line-height: 1.65;
}
.oppdetail.err { background: rgba(220,38,38,.06); border-color: rgba(220,38,38,.25);
                 color: var(--red); }
.od-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.od-t { font-weight: 600; color: var(--text); }
.od-advice { font-size: calc(12.2px * var(--fs)); color: var(--text-2); }
.od-verdict { color: var(--text); margin-bottom: 8px; }
.od-verdict b { color: var(--brand-ink); }
.od-dims { display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
           gap: 10px; margin-bottom: 8px; }
.od-dim { background: var(--surface); border: 1px solid var(--border);
          border-radius: var(--r-sm); padding: 8px 10px; }
.od-dl { font-size: calc(11.8px * var(--fs)); color: var(--text-3); }
.od-dv { font-size: calc(15px * var(--fs)); font-weight: 700; color: var(--text);
         font-variant-numeric: tabular-nums; }
.od-bar { height: 5px; border-radius: 999px; background: rgba(107,114,128,.16);
          margin: 5px 0 3px; overflow: hidden; }
.od-fill { height: 100%; background: var(--brand); border-radius: 999px; }
.od-ds { font-size: calc(11.4px * var(--fs)); color: var(--text-3); }
.od-dd { font-size: calc(12px * var(--fs)); color: var(--text-2); margin-top: 3px; }
.od-meta { color: var(--text-2); margin-bottom: 4px; }
.od-meta b { color: var(--text); }
.od-note { font-size: calc(11.8px * var(--fs)); color: var(--text-3); }
.od-peers { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border); }
.od-ph { font-size: calc(12.2px * var(--fs)); color: var(--text-2); font-weight: 600; margin-bottom: 5px; }
.od-pl { display: flex; flex-wrap: wrap; gap: 6px; }
.od-pr { background: var(--surface); border: 1px solid var(--border);
         border-radius: 999px; padding: 2px 9px; font-size: calc(11.8px * var(--fs));
         color: var(--text-2); }
.od-pr .ct { color: var(--text-3); margin-left: 4px; }
.oppdetailrow td { padding: 0 6px; }


/* ══ 🔴 2026-10-26 「采集记录」板块（第五个模块）══ */
.runs-wrap { display: flex; flex-direction: column; gap: 6px; padding: 6px 0; }
.run-row {
  display: grid;
  grid-template-columns: 148px minmax(90px,1fr) 56px minmax(120px,1.4fr) 74px 60px 56px 96px;
  gap: 10px; align-items: center;
  padding: 9px 12px; border: 1px solid #eee2c8; border-radius: 10px;
  background: #fff; font-size: 12px; cursor: pointer;
  transition: border-color .12s, background .12s;
}
.run-row:hover { border-color: var(--brand); background: #fffdf5; }
.run-row.on { border-color: var(--brand); background: var(--brand-soft); }
.rr-when { color: #8a6a3a; white-space: nowrap; }
.rr-kw { font-weight: 600; color: #3d2f14; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rr-mode { color: #9a8a6a; }
.rr-cond { color: #9a7a4a; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rr-saved { color: #c26a00; font-weight: 700; white-space: nowrap; }
.rr-dur { color: #9a8a6a; white-space: nowrap; }
.rr-st { text-align: center; border-radius: 6px; padding: 2px 0; font-size: 11px; }
.st-done { background: #e8f7ec; color: #1a7f37; }
.st-error { background: #fdecec; color: #c62828; }
.st-running { background: #fff6e0; color: #a8710a; }
.st-stopped { background: #f1f1f1; color: #777; }
.rr-open { color: var(--brand-ink); text-align: right; white-space: nowrap; }
.runs-empty { padding: 34px; text-align: center; color: #9a8a6a; font-size: 13px; }
.res-run-tip {
  margin-left: 12px; padding: 3px 10px; border-radius: 8px;
  background: var(--brand-soft); color: var(--brand-ink); font-size: 12px;
}

.runs-pager {
  display: flex; align-items: center; justify-content: center;
  gap: 12px; padding: 12px 0 4px;
}
.runs-page-lab { font-size: 12.5px; color: var(--text-2); }

/* ══════════════════════════════════════════════════════════════
   ★ 2026-10-27 拒绝退款的「凭证图片」上传（涛爷报障 MUST_HAVE_PROOF）
   只在 `.rf-form` 里用，样式跟着面板走（与 .rf-field 同源配色）。
   ══════════════════════════════════════════════════════════════ */
.rf-proof-box {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px;
  align-items: center;
}
.rf-proof-add {
  width: 74px; height: 74px; border: 1px dashed #d9d9e0; border-radius: 8px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; cursor: pointer; color: var(--text-2); background: #fafafb;
  transition: border-color .15s, color .15s;
}
.rf-proof-add:hover { border-color: #ffb020; color: #d98b00; }
.rf-proof-add span { font-size: 20px; line-height: 1; }
.rf-proof-add em { font-style: normal; font-size: calc(11.5px * var(--fs)); }

.rf-proof-item {
  position: relative; width: 74px; height: 74px; border-radius: 8px;
  overflow: hidden; border: 1px solid #e6e6ea; background: #fff;
}
.rf-proof-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rf-proof-del {
  position: absolute; top: 2px; right: 2px; width: 18px; height: 18px;
  border: 0; border-radius: 50%; background: rgba(0,0,0,.55); color: #fff;
  font-size: 12px; line-height: 18px; text-align: center; cursor: pointer; padding: 0;
}
.rf-proof-del:hover { background: #e54545; }
.rf-tip-inline { font-weight: normal; color: var(--text-2); font-size: calc(12.3px * var(--fs)); }

/* ══════════════════════════════════════════════════════════════
   ★ 2026-10-27 表格视图的勾选框（涛爷报障：位置漂移 + 勾了不像打钩）
   ──────────────────────────────────────────────────────────────
   根因：`.pcard .pick` 那套是**为卡片视图写的**
        （`position:absolute; left:10px; top:10px; 40×40`）。
        而表格行是 `<tr class="pcard pcard-live">` —— 同一个类名，
        但 `position:absolute` 在 `<tr>` 上不成立 ⇒ 勾选框相对**外层的定位祖先**
        漂移（截图里正好压到"店铺"列下面），40×40 又把行高撑得很难看。
   ✅ 表格里改成**普通流 + 16×16**；卡片视图一个字不动。
   ══════════════════════════════════════════════════════════════ */
tr.pcard .pick {
  /* 🔴 10-27 二改：**必须是 relative，不能是 static**。
     上一版写成 static，`::after`（可见的那个框）是 `position:absolute;
     left:50%; top:50%` 居中的 —— 没有定位父级它就会去认 td/table/body，
     于是框被"扔"到页面别处，行里看着就是**勾选框直接消失**（涛爷截图）。
     `relative` 让它成为 ::after 的定位父级 ⇒ 框正好落在 18×18 的命中区中央。 */
  position: relative;
  left: auto; top: auto;
  width: 18px; height: 18px; flex: 0 0 18px;
  background: none; border: 0; box-shadow: none; border-radius: 0;
}
tr.pcard .pick:hover { border-color: transparent; box-shadow: none; }
tr.pcard .pick::after {
  width: 16px; height: 16px; border-radius: 4px; border-width: 1.5px;
}
tr.pcard .pick:hover::after { border-color: var(--brand); }
/* 打钩：两条边框旋转 45°（不依赖字体/图片）。尺寸按 16×16 的框重算，
   原来 5×9 是按 20×20 设计的，套在 16×16 上会偏大、看起来像"歪的十字"。 */
tr.pcard .pick:has(input:checked)::before {
  width: 4px; height: 8px;
  transform: translate(-50%, -56%) rotate(45deg);
  border-width: 0 2px 2px 0;
}
/* 勾选列：收窄 + 垂直居中，别把行撑高 */
tr.pcard > td.c-ck {
  width: 36px; padding: 0 4px 0 10px;
  vertical-align: middle; text-align: center;
}

/* ══════════════════════════════════════════════════════════════
   ★ 2026-10-27 表格行的两个视觉 bug（涛爷报障）
   ══════════════════════════════════════════════════════════════ */

/* ① 选中行多出一圈「轮廓」
   ──────────────────────────────────────────────────────────────
   根因：`.pcard.picked` 里那条 `box-shadow: inset 0 0 0 2px var(--brand)`
        是**为卡片选中态**画的描边，而表格行也是 `.pcard.picked` ⇒ 整行被套一圈。
   表格自己的选中表现已经有了（`background` + 底边变色），**不需要再描边**。 */
tr.pcard.picked {
  border-color: transparent;
  box-shadow: none;
}
/* 表格行也别沿用卡片的 hover 位移/阴影（行没有圆角，位移看着像抖动） */
tr.pcard:hover { transform: none; box-shadow: none; }

/* ② 勾上之后只剩一块黄：白勾被黄底盖住了
   ──────────────────────────────────────────────────────────────
   根因：**同一元素的 `::after` 绘制在 `::before` 之上**（DOM 顺序），
        黄底在 ::after、白勾在 ::before ⇒ 勾永远被压在下面。
   ✅ 给两者显式分层：框在下（0），勾在上（1）。卡片视图同一个坑，一并修。 */
.pcard .pick::after { z-index: 0; }
.pcard .pick:has(input:checked)::before { z-index: 1; }
